#Первый приём
Соберите конфигурацию Launch + push. В карточке приёма есть кнопка «smartica.ai». smartica.ai возвращает протокол в МИС.
Сначала прочитайте Обзор.
Если у вас есть ИИ-агент с доступом к репозиторию, поручите агенту черновик. Промпт и план приведены в статье Промпты для ИИ-агента.
#Что понадобится
- Кнопка в карточке приёма вашей МИС.
- HTTP endpoint, доступный серверам smartica.ai по HTTPS.
- Параметры подключения. smartica.ai выдаёт параметры на шаге 1.
#Шаг 1. Получите параметры подключения
Пока smartica.ai не зарегистрирует вашу клинику, любой запрос вернёт 404.
Напишите на support@smartica.ai или откройте статью Поддержка. Передайте:
- название МИС и клиники
- адреса тестового и рабочего окружений вашей МИС
- контакт разработчика
- схему авторизации для входящих запросов от smartica.ai
В ответ вы получите:
| Что | Зачем |
|---|---|
platform |
Идентификатор клиники в адресах запросов к smartica.ai |
| Точный SSO endpoint | Адрес запроса ссылки для входа врача |
| API-ключ клиники | Секрет для авторизации в smartica.ai |
| Параметры окружения | Адрес тестового стенда и сетевые требования |
Подробности смотрите в статье Подключение.
smartica.ai выдаёт ключ на тестовый контур. Адреса контуров указаны в статье Тестовая площадка. Примеры ниже написаны для рабочего адреса. На тестовом контуре отличается только хост.
Пока ждёте ответ, пишите код шагов 2 и 3. Этот код не зависит от выданных параметров.
#Шаг 2. Сделайте endpoint в своей МИС
Для старта smartica.ai вызывает у вас только PUT. Бланк уходит в запросе ссылки на шаге 3. GET нужен, если в этом запросе нет template и fields.
PUT /api/for-smartica/encounters/{encounter_id} → принять заполненный протокол
GET /api/for-smartica/encounters/{encounter_id} → отдать бланк, если он не ушёл в запросе ссылки
Адрес выбираете вы. Согласуйте адрес со smartica.ai. По умолчанию защищайте эти методы Bearer-токеном поверх HTTPS. Для совместимости допустим HTTP Basic. mTLS и подпись запроса требуют доработки. Согласуйте mTLS и подпись до начала разработки. Сравнение вариантов смотрите в разделе Какую схему выбрать.
#GET — отдаёте контекст приёма
Этот метод нужен, только если бланк не передаёте в запросе ссылки. smartica.ai запрашивает название бланка и список полей.
Ответ должен быть 200 и JSON:
{
"encounter_id": "12345",
"user_email": "doctor@clinic.ru",
"template": "Кардиолог",
"fields": [
{ "id": "complaints", "label": "Жалобы" },
{ "id": "diagnosis_primary", "label": "Диагноз (основной)" }
]
}
id — технический ключ поля в вашей МИС. label — заголовок для заполнения smartica.ai. Обычно это подпись поля на бланке врача. Если подпись понятна только вместе с разделом выше, опишите поле целиком. «Жалобы» уже полный заголовок. «Норма» без раздела не объясняет поле.
Полный контракт описан в статье Бланк приёма.
#PUT — принимаете результат
Когда протокол готов, smartica.ai присылает:
{
"encounter_id": "12345",
"fields": [
{ "id": "complaints", "value": "Жалобы на головную боль..." },
{ "id": "diagnosis_primary", "value": "Гипертоническая болезнь I ст." }
],
"transcript": "Полная стенограмма разговора..."
}
Разложите value по полям с теми же id. Сохраните стенограмму.
Три правила, которые чаще всего нарушают:
- Поля, которых нет в массиве, не очищайте. smartica.ai присылает только поля, для которых нашлось содержание. Пустой массив
fieldsвалиден. Стенограмму всё равно сохраните. - Повторный такой же запрос не должен создавать вторую карточку. Обновляйте существующий приём по
encounter_id. Повторы приходят. Врач может дописать приём или нажать «отправить заново». - Отвечайте
200с JSON-телом. Не отвечайте204, пустым телом или HTML.
Полный контракт описан в статье Запись протокола в карту.
#Пример реализации
Ниже минимальный рабочий вариант на Node.js с Express. Логика переносится на любой стек.
const express = require('express');
const app = express();
app.use(express.json({ limit: '5mb' }));
const crypto = require('crypto');
// Проверка Bearer-токена. Токен вы генерируете сами и сообщаете нам при подключении.
function requireSmarticaAuth(req, res, next) {
const header = req.headers.authorization || '';
const expected = `Bearer ${process.env.SMARTICA_INBOUND_TOKEN}`;
// Сравнение за постоянное время, чтобы токен нельзя было подобрать по таймингу.
const a = Buffer.from(header);
const b = Buffer.from(expected);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).json({ message: 'Неверный токен' });
}
next();
}
// GET нужен, только если бланк не ушёл в запросе ссылки.
app.get('/api/for-smartica/encounters/:id', requireSmarticaAuth, async (req, res) => {
const encounter = await db.findEncounter(req.params.id);
if (!encounter) {
return res.status(404).json({ message: 'Приём не найден' });
}
res.json({
encounter_id: String(encounter.id),
user_email: encounter.doctorEmail,
template: encounter.formName,
fields: encounter.formFields.map((f) => ({ id: f.key, label: f.title })),
});
});
// PUT — принимаем заполненный протокол.
app.put('/api/for-smartica/encounters/:id', requireSmarticaAuth, async (req, res) => {
const { encounter_id: bodyId, fields = [], transcript } = req.body;
if (bodyId !== req.params.id) {
return res.status(422).json({ message: 'ID в адресе и в теле не совпадают' });
}
const encounter = await db.findEncounter(req.params.id);
if (!encounter) {
return res.status(404).json({ message: 'Приём не найден' });
}
await db.transaction(async (tx) => {
// Обновляем только пришедшие поля. Остальные не трогаем.
for (const field of fields) {
await tx.upsertProtocolField(encounter.id, field.id, field.value);
}
await tx.saveTranscript(encounter.id, transcript);
});
res.json({ encounter_id: String(encounter.id), status: 'saved' });
});
app.listen(3000);
До подключения smartica.ai проверьте GET курлом, если бланк не передаёте в запросе ссылки:
curl --fail-with-body --silent --show-error \
--header 'Authorization: Bearer ЗАМЕНИТЕ-НА-ТОКЕН' \
--header 'Accept: application/json' \
'https://mis.example.ru/api/for-smartica/encounters/12345'
#Шаг 3. Добавьте кнопку «smartica.ai» и серверный SSO
Кнопка не ведёт на smartica.ai напрямую. Сделайте так:
- Браузер передаёт вашему бэкенду только ID текущего приёма.
- Бэкенд проверяет, что этот врач имеет доступ к этому приёму.
- Бэкенд берёт email и ФИО врача из своей базы. Не принимайте email и ФИО от браузера.
- Бэкенд запрашивает у smartica.ai одноразовую ссылку.
- Бэкенд перенаправляет браузер на полученную ссылку.
Не передавайте ключ клиники в JavaScript.
Запрос за ссылкой:
POST https://app.smartica.ai/api/v1/integrations/{platform}/sso/token
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_full_name всегда. Для уже существующего врача поле не обязательно. При первом входе без user_full_name smartica.ai не создаст аккаунт и вернёт 422.
Ответ:
{
"launch_url": "https://app.smartica.ai/launch?platform=…&encounter_id=12345&t=…",
"expires_in": 60
}
Сразу откройте launch_url врачу. Используйте редирект 302 или новую вкладку. Ссылка одноразовая. Ссылка живёт столько секунд, сколько указано в expires_in.
Не разбирайте этот URL. Не собирайте URL самостоятельно. Не сохраняйте параметр t. Не логируйте ссылку целиком.
Пример на Node.js:
app.post('/encounters/:id/smartica-launch', requireDoctorAuth, async (req, res) => {
const encounter = await db.findEncounter(req.params.id);
// Врач берётся из сессии МИС, а не из тела запроса.
if (!encounter || !canAccess(req.user, encounter)) {
return res.status(403).json({ message: 'Нет доступа к приёму' });
}
const response = await fetch(process.env.SMARTICA_SSO_ENDPOINT, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SMARTICA_SSO_KEY}`,
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
user_email: req.user.email,
user_full_name: req.user.fullName,
encounter_id: String(encounter.id),
template: encounter.formName,
fields: encounter.formFields.map((field) => ({ id: field.key, label: field.title })),
}),
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
return res.status(502).json({ message: error.message || 'Smartica недоступна' });
}
const { launch_url: launchUrl } = await response.json();
res.redirect(302, launchUrl);
});
Коды ошибок и остальные поля запроса описаны в статье Ссылка для входа врача.
#Если бланк не передать в запросе ссылки
Уберите template и fields из запроса выше и реализуйте GET из шага 2. smartica.ai заберёт бланк сама.
#Шаг 4. Прогоните один контрольный приём
Проверяйте по порядку. Так видно, на каком шаге произошла ошибка.
- Создайте тестовый приём с уникальным
encounter_id. - Если бланк не в запросе ссылки, вызовите свой
GETкурлом. Убедитесь, что ответ200и JSON корректен. - Откройте карточку приёма в МИС. Нажмите «smartica.ai».
- Убедитесь, что врач попал в smartica.ai без повторного ввода логина и пароля.
- Запишите короткий разговор. Остановите запись. Дождитесь обработки.
- Проверьте, что пришёл
PUT. ВPUTесть значения полей и полная стенограмма. - Отправьте тот же
PUTповторно курлом. Убедитесь, что в МИС остался один обновлённый приём. - Добавьте ещё одну запись в тот же приём. Проверьте, что результат обновился и не продублировался.
Если что-то не сработало, откройте Если запросы не проходят.
#Чеклист готовности
- Получены
platform, точный SSO endpoint и API-ключ клиники -
encounter_idпередаётся строкой.encounter_idне переиспользуется для другого приёма - SSO вызывается только с бэкенда. Ключ отсутствует во фронтенде и в логах
- В запросе ссылки есть
templateи непустойfields. Если бланка в запросе нет,GETвозвращает стабильныйtemplate, уникальныеidи понятныеlabel - Email врача в SSO относится к врачу этого приёма. Если есть
GET, email в нём тот же -
PUTобновляет существующий приём.PUTне создаёт дубликаты - Поля, отсутствующие в
PUT, не затираются - Успешные ответы содержат JSON.
204 No Contentне используется - Тексты ошибок не содержат стектрейсов, SQL и секретов
Расширенный чеклист перед боевым запуском приведён в статье Подключение.
#Что дальше
- Если нужна запись без МИС, откройте Запись вне МИС (Import).
- Если МИС в закрытом контуре и входящие запросы невозможны, откройте Запуск из МИС, протокол забираете вы (Launch Pull).
- Если черновик должен сделать ИИ-агент, откройте Промпты для ИИ-агента.
- Точные лимиты указаны в статье Лимиты.