#Import: запись вне МИС

Сценарий Import нужен, когда врач записывает приём в Smartica не открывая МИС — например, с телефона на выезде или во время обхода. Позже он заходит в МИС, находит эту запись в списке и привязывает её к нужному приёму.

Если врач работает за компьютером с открытой МИС, вам скорее нужен другой сценарий: Launch: запуск из МИС.

#Чем Import отличается от Launch

Launch Import
С чего начинается Кнопка в карточке приёма МИС Запись в Smartica, позже форма в МИС
Когда известен ID приёма МИС Сразу, в SSO-запросе Позже, в момент привязки
Откуда берутся поля бланка Обычно GET у вашей МИС Всегда из тела запроса на привязку
Нужен ли SSO Да Нет
Типичная ситуация Врач за рабочим местом Приёмы «в поле», обходы, выезды

Оба сценария используют один и тот же platform и один и тот же API-ключ клиники. Способ доставки результата (push или pull) тоже общий для всего подключения.

Сценарии можно включить одновременно.

#Не перепутайте два идентификатора

В этом сценарии их два, и это главный источник ошибок.

Поле Чей ID Где находится Пример
smartica_encounter_id Наш В адресе запроса 7f3a9c2e-4b1e-4a8d-9c3f-2e1a8b4d6f0c
encounter_id Ваш В теле запроса 12345

Мнемоника: наш ID — в адресе, ваш ID — в теле.

#Как это работает по шагам

  1. Врач записывает приём в Smartica. Появляется стенограмма.
  2. Позже врач открывает в МИС форму «Импорт из Smartica».
  3. Ваш бэкенд запрашивает у Smartica список записей, ожидающих привязки.
  4. Врач видит список и выбирает нужную запись, а также указывает, к какому приёму в МИС её привязать.
  5. Ваш бэкенд отправляет запрос на привязку. Вместе с ним передаёт название бланка и его поля.
  6. Smartica заполняет протокол.
  7. Результат попадает в МИС — либо мы присылаем PUT (push), либо вы забираете сами и подтверждаете (pull).

Интерфейс выбора целиком живёт в вашей МИС. Smartica только отдаёт список и принимает привязку.

#Базовый адрес и авторизация

Все запросы этого сценария идут от вас к нам:

https://app.smartica.ai/api/v1/integrations/{platform}

{platform} — идентификатор вашей клиники, выданный при подключении. Заголовки:

Authorization: Bearer <API-ключ клиники>
Accept: application/json

Это тот же ключ, что используется для SSO в сценарии Launch. Храните его только на сервере.

Все ошибки приходят в одном формате:

{
  "error": "encounter_taken",
  "message": "Этот приём МИС уже привязан к другой записи."
}

При ошибках валидации добавляется объект errors с разбивкой по полям.

#Шаг 1. Получить список записей

GET /api/v1/integrations/{platform}/encounters?user_email=doctor@clinic.ru&user_full_name=Иванов%20Иван%20Иванович&status=awaiting_import&limit=50
Authorization: Bearer <API-ключ клиники>
Accept: application/json

#Параметры запроса

Параметр Обязательный Описание
user_email да Email врача. Вернутся только его записи
user_full_name почти всегда ФИО врача. Передавайте всегда: если аккаунта ещё нет и для клиники включено автосоздание, мы заведём врача по этому ФИО
status да Для формы выбора всегда awaiting_import
since нет Не показывать записи старше этой даты. Максимум 90 дней назад
limit нет Сколько записей вернуть. По умолчанию 50, максимум 100
cursor нет Маркер следующей страницы из предыдущего ответа

Если since не передан, показываются записи за последние 30 дней.

Если врача с таким email ещё нет и вы не передали user_full_name, ответ будет 404 doctor_not_found. Это не то же самое, что пустой список: пустой список значит «врач есть, записей нет».

#Ответ 200

{
  "encounters": [
    {
      "smartica_encounter_id": "7f3a9c2e-4b1e-4a8d-9c3f-2e1a8b4d6f0c",
      "title": "Приём 26 мая, 14:30",
      "recorded_at": "2026-05-26T14:30:11+00:00",
      "duration_sec": 742,
      "status": "awaiting_import"
    },
    {
      "smartica_encounter_id": "b2c1d0e9-8f7a-4b6c-9d5e-1a2b3c4d5e6f",
      "title": "Приём 26 мая, 15:05",
      "recorded_at": "2026-05-26T15:05:42+00:00",
      "duration_sec": 313,
      "status": "awaiting_import"
    }
  ],
  "next_cursor": null,
  "doctor_created": false
}
Поле Описание
smartica_encounter_id ID записи в Smartica. Подставляется в адрес на шаге 2
title Заголовок записи, можно показать врачу в списке
recorded_at Когда была сделана запись, ISO 8601
duration_sec Длительность в секундах, может быть null
status Здесь всегда awaiting_import
next_cursor Маркер следующей страницы или null, если список кончился
doctor_created true, если в этом запросе мы только что завели врача; иначе false

#Постраничный обход

Если next_cursor не null, передайте его значение в параметре cursor следующего запроса. Разбирать содержимое строки не нужно.

GET /api/v1/integrations/{platform}/encounters?user_email=doctor@clinic.ru&status=awaiting_import&cursor=eyJjcmVhdGVkX2F0IjoiMjAyNi0wOC0wNyAxNDozMDoxMS4wMDAwMDAiLCJpZCI6NDJ9

Повторяйте, пока next_cursor не станет null.

#Список по другим статусам

Тот же endpoint принимает и другие значения status — это удобно для мониторинга или для режима pull. В ответе тогда появляются два дополнительных поля:

{
  "encounters": [
    {
      "smartica_encounter_id": "7f3a9c2e-4b1e-4a8d-9c3f-2e1a8b4d6f0c",
      "title": "Приём 26 мая, 14:30",
      "recorded_at": "2026-05-26T14:30:11+00:00",
      "duration_sec": 742,
      "status": "ready",
      "encounter_id": "12345",
      "revision": 1
    }
  ],
  "next_cursor": null
}

#Шаг 2. Привязать запись к приёму МИС

POST /api/v1/integrations/{platform}/encounters/{smartica_encounter_id}/import
Authorization: Bearer <API-ключ клиники>
Content-Type: application/json
Accept: application/json

{
  "user_email": "doctor@clinic.ru",
  "user_full_name": "Иванов Иван Иванович",
  "encounter_id": "12345",
  "template": "Кардиолог",
  "fields": [
    { "id": "complaints", "label": "Жалобы" },
    { "id": "diagnosis_primary", "label": "Диагноз (основной)" }
  ]
}

#Поля тела запроса

Поле Обязательное Описание
user_email да Email врача, которому принадлежит запись
user_full_name почти всегда ФИО врача. Передавайте всегда: при первом обращении с неизвестным email и включённом автосоздании мы заведём аккаунт
encounter_id да ID приёма или документа в вашей МИС. Только цифры или UUID
template да Название бланка или специальности
fields да Непустой массив { id, label }. До 80 элементов
rebind нет true, чтобы перепривязать запись к другому encounter_id

Отдельного запроса за списком бланков не существует и не нужен: вы передаёте бланк прямо здесь.

#Ответ 202 Accepted

Код 202, а не 200, потому что заполнение протокола начинается асинхронно — результата в этом ответе ещё нет.

{
  "smartica_encounter_id": "7f3a9c2e-4b1e-4a8d-9c3f-2e1a8b4d6f0c",
  "encounter_id": "12345",
  "status": "processing",
  "revision": 0
}

После успешной привязки запись исчезает из списка awaiting_import.

Повторный запрос с той же парой smartica_encounter_id + encounter_id безопасен: он вернёт то же самое и ничего не продублирует.

Если в бланке нашлись поля с одинаковым id, к ответу добавится массив warnings. Привязка при этом проходит, но часть полей заполнена не будет — см. Дубликаты id в бланке.

#Перепривязка

Если врач ошибся и привязал запись не к тому приёму, отправьте запрос заново с новым encounter_id и rebind: true. В ответе появится дополнительное поле:

{
  "smartica_encounter_id": "7f3a9c2e-4b1e-4a8d-9c3f-2e1a8b4d6f0c",
  "encounter_id": "67890",
  "previous_encounter_id": "12345",
  "status": "processing",
  "revision": 1
}

Если при перепривязке изменились template или fields, протокол будет заполнен заново.

Без rebind: true попытка привязать запись к другому приёму вернёт 409 already_imported.

#Пример клиента

Две операции подряд: получить список ожидающих записей и привязать выбранную к приёму МИС. Ключ читается из окружения, platform — из конфигурации.

$base = 'https://app.smartica.ai/api/v1/integrations/'.getenv('SMARTICA_PLATFORM');
$headers = [
    'Authorization' => 'Bearer '.getenv('SMARTICA_SSO_KEY'),
    'Accept' => 'application/json',
];

// 1. Список записей, ожидающих привязки. Листаем по next_cursor.
$encounters = [];
$cursor = null;

do {
    $response = $http->get($base.'/encounters', [
        'headers' => $headers,
        'query' => array_filter([
            'user_email' => $doctor->email,
            'status' => 'awaiting_import',
            'limit' => 50,
            'cursor' => $cursor,
        ]),
    ]);

    $page = json_decode((string) $response->getBody(), true);
    $encounters = array_merge($encounters, $page['encounters']);
    $cursor = $page['next_cursor'];
} while ($cursor !== null);

// 2. Привязка выбранной записи к приёму МИС.
$response = $http->post($base.'/encounters/'.$smarticaEncounterId.'/import', [
    'headers' => $headers,
    'json' => [
        'user_email' => $doctor->email,
        'encounter_id' => (string) $encounter->id,
        'template' => $encounter->form->name,
        'fields' => $fields, // [['id' => ..., 'label' => ...], ...]
    ],
]);

// 202 Accepted — заполнение началось, результата здесь ещё нет.
const base = `https://app.smartica.ai/api/v1/integrations/${process.env.SMARTICA_PLATFORM}`;
const headers = {
  Authorization: `Bearer ${process.env.SMARTICA_SSO_KEY}`,
  Accept: 'application/json',
};

// 1. Список записей, ожидающих привязки. Листаем по next_cursor.
const encounters = [];
let cursor = null;

do {
  const query = new URLSearchParams({
    user_email: doctor.email,
    status: 'awaiting_import',
    limit: '50',
    ...(cursor ? { cursor } : {}),
  });

  const page = await fetch(`${base}/encounters?${query}`, { headers }).then((r) => r.json());

  encounters.push(...page.encounters);
  cursor = page.next_cursor;
} while (cursor !== null);

// 2. Привязка выбранной записи к приёму МИС.
await fetch(`${base}/encounters/${smarticaEncounterId}/import`, {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    user_email: doctor.email,
    encounter_id: String(encounter.id),
    template: encounter.form.name,
    fields, // [{ id, label }, ...]
  }),
});

// 202 Accepted — заполнение началось, результата здесь ещё нет.
base = f"https://app.smartica.ai/api/v1/integrations/{os.environ['SMARTICA_PLATFORM']}"
headers = {
    "Authorization": f"Bearer {os.environ['SMARTICA_SSO_KEY']}",
    "Accept": "application/json",
}

# 1. Список записей, ожидающих привязки. Листаем по next_cursor.
encounters = []
cursor = None

while True:
    params = {"user_email": doctor.email, "status": "awaiting_import", "limit": 50}

    if cursor:
        params["cursor"] = cursor

    page = httpx.get(f"{base}/encounters", headers=headers, params=params, timeout=30).json()
    encounters.extend(page["encounters"])
    cursor = page["next_cursor"]

    if cursor is None:
        break

# 2. Привязка выбранной записи к приёму МИС.
httpx.post(
    f"{base}/encounters/{smartica_encounter_id}/import",
    headers=headers,
    json={
        "user_email": doctor.email,
        "encounter_id": str(encounter.id),
        "template": encounter.form.name,
        "fields": fields,  # [{ "id": ..., "label": ... }, ...]
    },
    timeout=30,
)

# 202 Accepted — заполнение началось, результата здесь ещё нет.
var baseUrl = $"https://app.smartica.ai/api/v1/integrations/{platform}";

httpClient.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("SMARTICA_SSO_KEY"));

// 1. Список записей, ожидающих привязки. Листаем по next_cursor.
var encounters = new List<PartnerEncounter>();
string? cursor = null;

do
{
    var url = $"{baseUrl}/encounters?user_email={Uri.EscapeDataString(doctor.Email)}"
            + $"&status=awaiting_import&limit=50"
            + (cursor is null ? "" : $"&cursor={Uri.EscapeDataString(cursor)}");

    var page = await httpClient.GetFromJsonAsync<EncounterPage>(url);

    encounters.AddRange(page!.Encounters);
    cursor = page.NextCursor;
}
while (cursor is not null);

// 2. Привязка выбранной записи к приёму МИС.
var response = await httpClient.PostAsJsonAsync(
    $"{baseUrl}/encounters/{smarticaEncounterId}/import",
    new
    {
        user_email = doctor.Email,
        encounter_id = encounter.Id.ToString(),
        template = encounter.Form.Name,
        fields // [{ id, label }, ...]
    });

// 202 Accepted — заполнение началось, результата здесь ещё нет.
String baseUrl = "https://app.smartica.ai/api/v1/integrations/" + platform;
String auth = "Bearer " + System.getenv("SMARTICA_SSO_KEY");

// 1. Список записей, ожидающих привязки. Листаем по next_cursor.
List<PartnerEncounter> encounters = new ArrayList<>();
String cursor = null;

do {
    String url = baseUrl + "/encounters"
            + "?user_email=" + URLEncoder.encode(doctor.getEmail(), StandardCharsets.UTF_8)
            + "&status=awaiting_import&limit=50"
            + (cursor == null ? "" : "&cursor=" + URLEncoder.encode(cursor, StandardCharsets.UTF_8));

    HttpRequest request = HttpRequest.newBuilder(URI.create(url))
            .header("Authorization", auth)
            .header("Accept", "application/json")
            .GET()
            .build();

    EncounterPage page = objectMapper.readValue(
            httpClient.send(request, BodyHandlers.ofString()).body(), EncounterPage.class);

    encounters.addAll(page.encounters());
    cursor = page.nextCursor();
} while (cursor != null);

// 2. Привязка выбранной записи к приёму МИС.
Map<String, Object> payload = Map.of(
        "user_email", doctor.getEmail(),
        "encounter_id", String.valueOf(encounter.getId()),
        "template", encounter.getForm().getName(),
        "fields", fields // [{ id, label }, ...]
);

HttpRequest importRequest = HttpRequest.newBuilder(
                URI.create(baseUrl + "/encounters/" + smarticaEncounterId + "/import"))
        .header("Authorization", auth)
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(payload)))
        .build();

// 202 Accepted — заполнение началось, результата здесь ещё нет.
httpClient.send(importRequest, BodyHandlers.ofString());
Функция ЗапросКSmartica(Метод, Путь, Тело = Неопределено)

    Заголовки = Новый Соответствие;
    Заголовки.Вставить("Authorization", "Bearer " + Константы.SmarticaКлючКлиники.Получить());
    Заголовки.Вставить("Accept", "application/json");

    Если Тело <> Неопределено Тогда
        Заголовки.Вставить("Content-Type", "application/json");
    КонецЕсли;

    Соединение = Новый HTTPСоединение(
        "app.smartica.ai", 443, , , , 30, Новый ЗащищенноеСоединениеOpenSSL);

    База = "/api/v1/integrations/" + Константы.SmarticaPlatform.Получить();
    Запрос = Новый HTTPЗапрос(База + Путь, Заголовки);

    Если Тело <> Неопределено Тогда
        Запись = Новый ЗаписьJSON;
        Запись.УстановитьСтроку();
        ЗаписатьJSON(Запись, Тело);
        Запрос.УстановитьТелоИзСтроки(Запись.Закрыть(), КодировкаТекста.UTF8);
    КонецЕсли;

    Ответ = ?(Метод = "GET", Соединение.Получить(Запрос), Соединение.ОтправитьДляОбработки(Запрос));

    Чтение = Новый ЧтениеJSON;
    Чтение.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
    Результат = ПрочитатьJSON(Чтение, Истина);
    Чтение.Закрыть();

    Возврат Новый Структура("Код, Данные", Ответ.КодСостояния, Результат);

КонецФункции

// 1. Список записей, ожидающих привязки.
Список = ЗапросКSmartica("GET",
    "/encounters?user_email=" + КодироватьСтроку(Врач.Email, СпособКодированияСтроки.КодировкаURL)
    + "&status=awaiting_import&limit=50");

// 2. Привязка выбранной записи к приёму МИС.
Поля = Новый Массив;
Поля.Добавить(Новый Структура("id, label", "complaints", "Жалобы"));
Поля.Добавить(Новый Структура("id, label", "diagnosis_primary", "Диагноз (основной)"));

Тело = Новый Структура;
Тело.Вставить("user_email", Врач.Email);
Тело.Вставить("encounter_id", Строка(Приём.Код));
Тело.Вставить("template", "Кардиолог");
Тело.Вставить("fields", Поля);

Итог = ЗапросКSmartica("POST", "/encounters/" + ВыбранныйUUID + "/import", Тело);

#Статусы записи

status Что означает Что делать
awaiting_import Есть стенограмма, ещё не привязана к МИС Показать врачу в форме выбора
processing Привязка принята, идёт заполнение Подождать
ready Протокол готов При pull — забрать. При push — ничего, мы пришлём сами
accepted Доставлено в МИС Готово
failed Заполнение не удалось Обратиться в поддержку

#Шаг 3. Получить результат

Дальше всё зависит от того, какой режим доставки настроен для вашего подключения.

#Режим push

Ничего делать не нужно. Когда протокол готов, Smartica сама вызовет ваш PUT — точно так же, как в сценарии Launch. Контракт описан в статье PUT: запись протокола.

#Режим pull: забрать протокол

Опрашивайте записи со статусом ready или проверяйте конкретную запись:

GET /api/v1/integrations/{platform}/encounters/{smartica_encounter_id}
Authorization: Bearer <API-ключ клиники>
Accept: application/json

Ответ 200:

{
  "smartica_encounter_id": "7f3a9c2e-4b1e-4a8d-9c3f-2e1a8b4d6f0c",
  "encounter_id": "12345",
  "status": "ready",
  "revision": 1,
  "filled_at": "2026-05-26T14:44:02+00:00",
  "exported_at": null,
  "fields": [
    { "id": "complaints", "value": "Жалобы на давящую боль за грудиной..." },
    { "id": "diagnosis_primary", "value": "Гипертоническая болезнь I ст." }
  ],
  "transcript": "Полная стенограмма разговора врача и пациента...",
  "recorded_at": "2026-05-26T14:30:11+00:00",
  "duration_sec": 742,
  "title": "Приём 26 мая, 14:30"
}

Пока status не равен ready, массив fields будет пустым — забирать ещё нечего.

Запомните значение revision: оно понадобится на следующем шаге.

#В fields приходят только заполненные поля

В ответе будут не все поля бланка, а только те, для которых в разговоре нашлось содержание. Пустые значения и заглушки вроде «не указано» мы отбрасываем на своей стороне, поэтому поле с пустым value не придёт — оно просто не появится в массиве.

Отсюда правило записи: поля, которых нет в fields, не трогайте. Если очищать их по факту получения результата, вы сотрёте то, что врач успел заполнить руками. На всякий случай защититесь и с другой стороны: если значение поля пришло пустым, считайте поле неполученным и в протокол его не пишите.

Пустой fields при status: ready — тоже валидная ситуация: в разговоре не нашлось материала для бланка, например запись оказалась слишком короткой. Стенограмму при этом всё равно сохраните. Если так происходит регулярно, дело почти всегда в label: по названиям полей непонятно, что в них класть.

#Режим pull: подтвердить получение

После того как вы успешно сохранили протокол у себя, отправьте подтверждение:

POST /api/v1/integrations/{platform}/encounters/{smartica_encounter_id}/ack
Authorization: Bearer <API-ключ клиники>
Content-Type: application/json

{
  "revision": 1
}

Ответ 200:

{
  "smartica_encounter_id": "7f3a9c2e-4b1e-4a8d-9c3f-2e1a8b4d6f0c",
  "status": "accepted",
  "revision": 1
}

Подтверждайте только после реального сохранения. Пока подтверждения нет, приём считается недоставленным, и врач видит это в интерфейсе Smartica.

Повторное подтверждение той же ревизии безопасно и вернёт тот же ответ.

#Что если ревизия устарела

Пока вы сохраняли протокол, врач мог дописать приём — тогда Smartica перезаполнила протокол и увеличила revision. Ваше подтверждение старой версии будет отклонено:

{
  "error": "revision_mismatch",
  "message": "Ревизия устарела — заберите протокол заново.",
  "revision": 4
}

Код ответа — 409. В поле revision приходит актуальный номер. Действие простое: заберите протокол заново и подтвердите новую ревизию. Данные, которые вы уже сохранили, надо обновить — у вас лежит устаревшая версия.

#Правила, которые нельзя нарушить

  1. Одна запись Smartica привязывается к одному приёму МИС. Сменить приём можно только через rebind.
  2. Один encounter_id вашей МИС занимается одной записью Smartica. Попытка занять его второй раз вернёт encounter_taken.
  3. Привязать можно только запись, у которой уже есть стенограмма. Иначе — no_transcript.

#Ошибки

HTTP error Причина Что делать
401 invalid_bearer_key Ключ неверен или не передан Проверить API-ключ клиники. Не повторять автоматически
404 wrong_host Базовый адрес не совпадает с адресом контура Взять адрес из статьи Тестовая площадка
404 doctor_not_found Врача с таким email нет в вашей клинике, и создать его нельзя Проверить email. Передать user_full_name и повторить. Если ошибка остаётся — автосоздание для клиники выключено, напишите в поддержку Smartica
404 encounter_not_found Запись не найдена или принадлежит другому врачу Проверить smartica_encounter_id и user_email
403 email_in_other_organization Email уже относится к другой организации в Smartica Написать в поддержку Smartica для переноса аккаунта
409 no_transcript У записи ещё нет стенограммы Подождать и повторить
409 already_imported Запись уже привязана к другому приёму Передать rebind: true, если перепривязка действительно нужна
409 encounter_taken Этот encounter_id уже занят другой записью Выбрать другой приём МИС
409 revision_mismatch Подтверждается устаревшая версия Забрать протокол заново и подтвердить актуальную ревизию
410 note_deleted Врач удалил запись в Smartica Убрать из очереди, ничего не сохранять
422 validation_failed Ошибка в полях (в том числе нет ФИО для создания врача) или формате encounter_id Исправить поля, детали — в объекте errors
429 Превышен лимит запросов Учесть заголовок Retry-After, снизить частоту опроса

Точные значения лимитов — в статье Лимиты и ограничения.

#Чеклист

  • Список awaiting_import запрашивается с бэкенда, ключ не попадает в браузер
  • В list и import всегда передаётся user_full_name
  • Реализован постраничный обход по next_cursor
  • В форме врач выбирает и запись Smartica, и приём МИС
  • В запросе на привязку передаются template и непустой fields
  • Привязка выполняется по одной записи за раз
  • Привязанные записи не показываются в списке повторно
  • При push реализован приём PUT
  • При pull реализованы забор протокола и подтверждение с актуальной revision
  • Поля, которых нет в fields, при записи не очищаются
  • Обрабатывается 409 revision_mismatch — повторный забор перед подтверждением

#Связанные статьи

← Все статьи: Интеграция с МИС Поиск по базе знаний