Integration of 1C-Bitrix with the medical information system (MIS)

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Integrating 1C-Bitrix with a Medical Information System (MIS)

A private clinic's website accepts online appointments. The MIS (Medical Information System) is the clinic's operational backbone: physician schedules, medical history, electronic health records, service accounting, and billing. The goal: an appointment made on the website must automatically appear in the MIS schedule, while available slot data must be displayed on the website in real time.

The MIS market is heterogeneous: Infoclinica, 1C:Medicine, qMS, Medesk, Renaissance-M, ArchiMed+, Medods — each has its own API (or lacks one entirely). The integration architecture depends on the specific MIS.

Typical Integration Scenarios

Scenario A: The MIS provides a REST/SOAP API. The website communicates directly with the MIS API to retrieve schedules and book patient appointments. The cleanest approach, but not all MIS platforms support it.

Scenario B: Intermediate broker. The MIS publishes the schedule to an intermediate database (PostgreSQL or MySQL); the website reads from there. A website booking creates a request in the intermediate table, which the MIS picks up via cron.

Scenario C: Integration bus. For large clinics with multiple MIS instances and many systems — a dedicated integration service (e.g., based on RabbitMQ or Apache Kafka) synchronises data between systems.

For a single clinic with one MIS — Scenario A or B.

Integration via MIS REST API (using Medesk as an example)

class MedeskApiClient
{
    private string $apiKey;
    private string $baseUrl = 'https://api.medesk.net/api/v2';

    public function getDoctorSchedule(int $doctorId, string $dateFrom, string $dateTo): array
    {
        return $this->request('GET', '/schedules', [
            'doctor_id' => $doctorId,
            'from'      => $dateFrom, // Y-m-d
            'to'        => $dateTo,
            'include'   => 'free_slots',
        ]);
    }

    public function createAppointment(array $patientData, int $slotId): array
    {
        return $this->request('POST', '/appointments', [
            'slot_id'       => $slotId,
            'patient'       => [
                'first_name'  => $patientData['name'],
                'last_name'   => $patientData['surname'],
                'phone'       => $patientData['phone'],
                'email'       => $patientData['email'],
                'birth_date'  => $patientData['birth_date'],
            ],
            'comment'       => $patientData['comment'] ?? '',
            'source'        => 'website',
        ]);
    }

    public function cancelAppointment(int $appointmentId, string $reason = ''): array
    {
        return $this->request('DELETE', "/appointments/{$appointmentId}", [
            'reason' => $reason,
        ]);
    }

    private function request(string $method, string $path, array $data = []): array
    {
        $url = $this->baseUrl . $path;
        if ($method === 'GET' && $data) {
            $url .= '?' . http_build_query($data);
        }

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CUSTOMREQUEST  => $method,
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                "Authorization: Bearer {$this->apiKey}",
            ],
            CURLOPT_POSTFIELDS => in_array($method, ['POST', 'PUT', 'PATCH'])
                ? json_encode($data) : null,
        ]);

        $response = json_decode(curl_exec($ch), true);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode >= 400) {
            \Bitrix\Main\Diag\Debug::writeToFile(
                "MIS API Error {$httpCode}: " . json_encode($response),
                'MIS',
                '/local/logs/mis-integration.log'
            );
            throw new \RuntimeException("MIS API error: {$httpCode}");
        }

        return $response ?? [];
    }
}

Schedule Caching

Calling the MIS API directly on every visit to a physician's page is a bad idea — the MIS may be slow or enforce request rate limits. Cache the schedule:

class DoctorScheduleService
{
    private MedeskApiClient $mis;

    public function getAvailableSlots(int $doctorId, string $date): array
    {
        $cacheKey  = "doctor_slots_{$doctorId}_{$date}";
        $cacheTtl  = 180; // 3 minutes — balance between freshness and load

        $cache = \Bitrix\Main\Data\Cache::createInstance();
        if ($cache->initCache($cacheTtl, $cacheKey, '/mis/slots/')) {
            return $cache->getVars()['slots'];
        }

        $schedule = $this->mis->getDoctorSchedule($doctorId, $date, $date);
        $slots    = $this->formatSlots($schedule);

        $cache->startDataCache();
        $cache->endDataCache(['slots' => $slots]);

        return $slots;
    }

    public function bookSlot(int $slotId, array $patientData): array
    {
        $result = $this->mis->createAppointment($patientData, $slotId);

        // Invalidate the schedule cache for this physician
        $date      = date('Y-m-d'); // or the slot's date
        $doctorId  = $this->getSlotDoctorId($slotId);
        \Bitrix\Main\Data\Cache::clearByTag("doctor_slots_{$doctorId}_{$date}");

        // Save the appointment in 1C-Bitrix
        $this->saveAppointmentInBitrix($result, $patientData);

        return $result;
    }
}

Storing Appointments in 1C-Bitrix

Appointments are duplicated in 1C-Bitrix — for history, notifications, and continuity of operation when the MIS is unavailable:

class AppointmentTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getTableName(): string { return 'local_mis_appointments'; }

    public static function getMap(): array
    {
        return [
            new \Bitrix\Main\ORM\Fields\IntegerField('ID',          ['primary' => true, 'autocomplete' => true]),
            new \Bitrix\Main\ORM\Fields\IntegerField('USER_ID'),
            new \Bitrix\Main\ORM\Fields\IntegerField('MIS_APPOINTMENT_ID'),
            new \Bitrix\Main\ORM\Fields\IntegerField('DOCTOR_ID'),
            new \Bitrix\Main\ORM\Fields\DatetimeField('APPOINTMENT_TIME'),
            new \Bitrix\Main\ORM\Fields\StringField('STATUS'),      // booked|confirmed|cancelled|completed
            new \Bitrix\Main\ORM\Fields\StringField('SERVICE_NAME'),
            new \Bitrix\Main\ORM\Fields\StringField('PATIENT_PHONE'),
            new \Bitrix\Main\ORM\Fields\DatetimeField('CREATED_AT'),
        ];
    }
}

Patient Notifications

After booking — SMS and email confirmation via 1C-Bitrix. Reminders are sent 24 hours and 2 hours before the appointment. Reminders are implemented via a 1C-Bitrix agent that checks appointments every hour:

function SendMisAppointmentReminders(): string
{
    $now      = new \Bitrix\Main\Type\DateTime();
    $in24h    = (new \DateTime())->modify('+24 hours');
    $in2h     = (new \DateTime())->modify('+2 hours');

    // Appointments in 24 hours without a sent reminder
    $appointments = AppointmentTable::getList([
        'filter' => [
            'STATUS'              => 'booked',
            '>=APPOINTMENT_TIME'  => \Bitrix\Main\Type\DateTime::createFromTimestamp($in2h->getTimestamp()),
            '<=APPOINTMENT_TIME'  => \Bitrix\Main\Type\DateTime::createFromTimestamp($in24h->getTimestamp()),
            'REMINDER_24H_SENT'  => 'N',
        ],
    ]);

    while ($row = $appointments->fetch()) {
        SmsService::send($row['PATIENT_PHONE'],
            "Reminder: your appointment is on " . date('d.m.Y H:i', strtotime($row['APPOINTMENT_TIME']))
        );
        AppointmentTable::update($row['ID'], ['REMINDER_24H_SENT' => 'Y']);
    }

    return __FUNCTION__ . '();';
}

MIS Error Handling

The MIS may be unavailable (maintenance, server issues). Strategy: if the MIS is unavailable — save the request to the local_mis_pending_appointments table with status pending, display "Appointment received, we will contact you to confirm" to the patient. An agent retries pending appointments every 5 minutes.

Scope of Work

  • Analysis of the specific MIS API (differs per vendor)
  • PHP client for the MIS API with error handling
  • Schedule caching with invalidation on booking
  • Online appointment component on the website
  • Appointment table in 1C-Bitrix, SMS/email notifications
  • Pending appointment queue for MIS unavailability

Timeline: 5–8 weeks with a documented MIS API. 8–14 weeks when integrating via an intermediate database or developing the API on the MIS side.