#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 — в теле.
#Как это работает по шагам
- Врач записывает приём в Smartica. Появляется стенограмма.
- Позже врач открывает в МИС форму «Импорт из Smartica».
- Ваш бэкенд запрашивает у Smartica список записей, ожидающих привязки.
- Врач видит список и выбирает нужную запись, а также указывает, к какому приёму в МИС её привязать.
- Ваш бэкенд отправляет запрос на привязку. Вместе с ним передаёт название бланка и его поля.
- Smartica заполняет протокол.
- Результат попадает в МИС — либо мы присылаем
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 приходит актуальный номер. Действие простое: заберите протокол заново и подтвердите новую ревизию. Данные, которые вы уже сохранили, надо обновить — у вас лежит устаревшая версия.
#Правила, которые нельзя нарушить
- Одна запись Smartica привязывается к одному приёму МИС. Сменить приём можно только через
rebind. - Один
encounter_idвашей МИС занимается одной записью Smartica. Попытка занять его второй раз вернётencounter_taken. - Привязать можно только запись, у которой уже есть стенограмма. Иначе —
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— повторный забор перед подтверждением