Перейти к основному содержимому

Вебхук: API для отправки звонков и диалогов

Вебхук — универсальный способ передать разговоры в Лия Sense из системы, для которой нет готовой интеграции. Вы получаете персональную ссылку и отправляете на неё каждый завершённый звонок или диалог одним HTTP-запросом с JSON. Дальше система всё делает сама: раз в сутки накопленные разговоры уходят на транскрибацию и анализ.

Есть две интеграции с одинаковым API, но разным типом данных:

ИнтеграцияТип ссылки (connector_type)Что принимает
Вебхук: звонкиcallsСобытия type: "call" — метаданные звонка и ссылка на аудиозапись
Вебхук: диалогиdialogsСобытия type: "text" — метаданные диалога и массив сообщений

Тип фиксируется при подключении. Звонок, отправленный по ссылке для диалогов (и наоборот), отклоняется с ошибкой 400.

Как это работает

  1. Вы подключаете источник «Вебхук» в Лия Sense и получаете ссылку вида https://webhooks.sense.lia.chat/api/v1/webhooks/<токен>/events.
  2. Ваша система отправляет POST на эту ссылку по мере завершения разговоров — по одному разговору на запрос. Событие сразу принимается и ставится в очередь, ответ — 201.
  3. Периодическая задача сбора раз в сутки (ночью, 00:00–04:00 МСК) забирает из очереди новые разговоры и передаёт их в Лия Sense. Разговоры, отправленные днём, появляются в системе на следующее утро.
  4. Аудиозаписи передаются только ссылкой: система не принимает файлы в теле запроса, а скачивает запись по вашей ссылке во время ночной выгрузки. Поэтому ссылка должна оставаться рабочей после отправки события — см. требования к ссылке на запись.
Создайте задачу сбора сразу после подключения

Приём событий не зависит от задачи сбора: пока задачи нет, события копятся в очереди и удаляются через 30 дней. Чтобы данные не пропали, сразу после подключения создайте периодическую задачу над этим источником.

Подключение

  1. Лия SenseЗадачи → вкладка ИсточникиДобавить источник.
  2. Выберите тип: Голос для звонков или Чаты для диалогов, затем платформу Вебхук: звонки или Вебхук: диалоги.
  3. В форме сразу появится поле Ссылка для отправки данных. Скопируйте её кнопкой рядом с полем и передайте тому, кто настраивает отправку.
  4. Введите название источника и нажмите Подключить. Ссылка закрепляется за источником в момент подключения.
  5. Создайте периодическую задачу сбора над источником (см. предупреждение выше).

Полей для ввода учётных данных у вебхука нет. Нет и поля «Лимит запросов в секунду»: Лия Sense не опрашивает вашу систему. При отправке событий соблюдайте лимиты API и обрабатывайте ответ 429.

Ссылка — это секрет

Авторизация выполняется по токену в самой ссылке, отдельных заголовков нет. Передавайте ссылку так же, как пароль: не публикуйте её в открытых репозиториях, логах и переписке. При подозрении на утечку обратитесь в поддержку — ссылку перевыпустят; старая ссылка продолжит принимать события ещё 24 часа, чтобы вы успели переключиться.

Проверка связи

Замените в ссылке /events на /ping и выполните GET:

curl -sS "https://webhooks.sense.lia.chat/api/v1/webhooks/<токен>/ping"
{"status": "ok", "state": "bound", "connector_type": "calls"}
ПолеЗначенияОписание
stateboundСсылка привязана к источнику, события принимаются
issuedСсылка выдана, но источник ещё не подключён: POST на неё вернёт 409
connector_typecallsСсылка принимает события type: "call"
dialogsСсылка принимает события type: "text"

Ответ 404 означает, что токен неизвестен, ссылка отозвана или прошло больше 24 часов после её перевыпуска.

Отправка события

POST https://webhooks.sense.lia.chat/api/v1/webhooks/<токен>/events
Content-Type: application/json
  • Тело — один JSON-объект в кодировке UTF-8; одно событие (разговор) на запрос.
  • Максимальный размер тела — 1 МиБ. Тело должно быть передано целиком за 30 секунд.
  • Неизвестные поля игнорируются. Типы строковых полей проверяются строго: число, null или объект вместо строки — ошибка 400, а не приведение типа.

Звонок

curl -sS -X POST "https://webhooks.sense.lia.chat/api/v1/webhooks/<токен>/events" \
-H "Content-Type: application/json" \
-d '{
"type": "call",
"external_id": "pbx-2026-09-11-000871",
"occurred_at": "2026-09-11T10:15:03+03:00",
"source_channel": "office-pbx",
"status": "answered",
"metadata": {
"direction": "inbound",
"queue": "sales",
"agent": "Иванов",
"caller": "+79990000000"
},
"audio": {
"url": "https://pbx.example.com/recordings/871.mp3",
"format": "mp3",
"duration_seconds": 63,
"channels": 1
}
}'

Ответ:

{"status": "accepted", "external_id": "pbx-2026-09-11-000871", "event_id": 1841, "messages": 0}

Текстовый диалог

curl -sS -X POST "https://webhooks.sense.lia.chat/api/v1/webhooks/<токен>/events" \
-H "Content-Type: application/json" \
-d '{
"type": "text",
"external_id": "chat-55120",
"occurred_at": "2026-09-11T10:02:00+03:00",
"source_channel": "site-widget",
"status": "closed",
"metadata": {"department": "support"},
"messages": [
{
"sequence_number": 1,
"text": "Здравствуйте, не могу войти в личный кабинет",
"timestamp": "2026-09-11T10:02:01+03:00",
"author_name": "Клиент",
"author_role": "client",
"author_id": "u-77"
},
{
"sequence_number": 2,
"text": "Добрый день! Уточните, пожалуйста, ваш email",
"timestamp": "2026-09-11T10:02:20+03:00",
"author_name": "Иванов",
"author_role": "operator",
"author_id": "op-3"
}
]
}'

Ответ:

{"status": "accepted", "external_id": "chat-55120", "event_id": 1842, "messages": 2}

Поля события

ПолеТипОбязательноеОписание
type"call" или "text"ДаТип разговора. Должен совпадать с типом ссылки
external_idстрока, ^[A-Za-z0-9._:-]{1,255}$ДаИдентификатор разговора в вашей системе. Ключ дедупликации: повтор с тем же external_id не создаёт новый разговор
occurred_atстрока, ISO 8601 со смещениемДаВремя разговора, например 2026-09-11T10:15:03+03:00 или 2026-09-11T07:15:03Z. Допустимое окно: от года назад до суток вперёд относительно текущего момента
source_channelстрока ≤ 64 символовНетКанал или площадка, откуда пришёл разговор (office-pbx, site-widget, telegram). Значения попадают в фильтр задачи «Канал»
statusстрока ≤ 32 символовНетСтатус разговора в вашей системе. Сохраняется как есть и на приём события не влияет — отправляйте только завершённые разговоры
metadataобъектНетЛюбые дополнительные данные: отдел, оператор, номер клиента, тариф. См. Метаданные
audioобъектДля type: "call"Аудиозапись, см. ниже
messagesмассив, от 1 до 2000 элементовДля type: "text"Сообщения диалога, см. ниже

Объект audio

ПолеТипОбязательноеОписание
urlстрока ≤ 2048 символовДаПрямая ссылка на аудиофайл. Требования — в разделе Ссылка на запись
formatстрока, ^[a-z0-9]{1,8}$ДаФактический формат файла строчными буквами. Для обработки используйте mp3, wav, ogg, opus, m4a или flac: другие значения API примет, но дальнейшая обработка не гарантируется
duration_secondsцелое число от 0 до 2 147 483 647НетДлительность записи в секундах. Только число: "63" строкой отклоняется
channels1 или 2НетЧисло каналов в записи. 2 — стерео, где канал 0 — оператор, канал 1 — клиент

Элементы messages[]

ПолеТипОбязательноеОписание
textстрокаДаТекст сообщения
timestampстрока, ISO 8601 со смещениемДаВремя сообщения
author_nameстрокаДаИмя автора, как оно должно отображаться в разговоре
author_roleстрокаДаРоль автора: используйте client для клиента и operator для сотрудника. Другие значения тоже принимаются
sequence_numberцелое число от 0 до 2 147 483 647НетПорядковый номер сообщения. Если не передан — берётся позиция в массиве, начиная с 1
external_idстрокаНетИдентификатор сообщения в вашей системе. Если не передан — <external_id разговора>:<sequence_number>
author_idстрокаНетИдентификатор автора в вашей системе

Порядок сообщений в Лия Sense определяется sequence_number. Передавайте сообщения в хронологическом порядке с возрастающими уникальными номерами — или не передавайте sequence_number вовсе, тогда порядок задаст массив. timestamp используется для времени начала и конца диалога и времени ответа, но порядок реплик не меняет.

Даты и время

occurred_at и timestamp принимаются только в ISO 8601 с явным смещением часового пояса: 2026-09-11T10:15:03+03:00 или 2026-09-11T07:15:03Z. Метка без смещения (2026-09-11T10:15:03) отклоняется с 400: иначе время разговора было бы интерпретировано неверно, а метрики — смещены.

Метаданные

metadata — произвольный JSON-объект, который сохраняется целиком и становится метаданными разговора в Лия Sense. Схема метаданных источника строится по ключам первого уровня из последних 100 принятых событий; тип поля определяется по значениям (number, boolean, иначе string). Вложенные объекты допустимы, но в схеме считаются строками.

Два поля дают фильтры в задаче сбора:

  • source_channel (поле события, не metadata) — фильтр «Канал»;
  • metadata.direction — для звонков фильтр «Направление». Рекомендуем значения inbound, outbound, internal.

Списки значений для фильтров строятся по уже принятым событиям: пока вы не отправили ни одного, выпадающие списки в задаче будут пустыми.

Повторы и дедупликация

Дедупликация выполняется по паре «источник + external_id». Повторный корректный запрос с тем же external_id возвращает 200 со статусом duplicate и не изменяет уже принятое событие — сохраняется первый принятый вариант, включая audio.format и все метаданные. Поэтому запрос безопасно повторять при сетевых ошибках, а чтобы дослать исправленные данные, используйте другой external_id.

Повтор проходит те же проверки, что и первый запрос: некорректный повтор получит 400, повтор при исчерпанном лимите — 429.

Ответы и ошибки

КодТелоКогда
201{"status":"accepted","external_id":…,"event_id":…,"messages":N}Событие принято и поставлено в очередь
200{"status":"duplicate","external_id":…,"event_id":…}Событие с таким external_id уже было принято; содержимое повтора проигнорировано
400{"error":"Ошибка валидации","details":[{"field":"…","message":"…"}]}Некорректный JSON, нарушение схемы, тип события не совпал с типом ссылки, дата без смещения или вне окна, больше 2000 сообщений, звонок без audio.url/audio.format, недопустимая ссылка на запись
404{"error":"Вебхук не найден"}Токен неизвестен, ссылка отозвана или прошло больше 24 часов после её перевыпуска
408{"error":"Тело запроса не дочитано за отведённое время"}Тело не передано целиком за 30 секунд
409{"error":"Вебхук ещё не привязан к источнику данных"}Ссылка выдана, но источник в Лия Sense ещё не подключён
413{"error":"Тело запроса слишком большое"} (или стандартный ответ веб-сервера)Тело больше 1 МиБ
429{"error":"Превышен лимит запросов"} + заголовок Retry-AfterПревышен один из лимитов

Поля успешного ответа:

ПолеОписание
statusaccepted — новое событие, duplicate — уже принятое ранее
external_idПереданный вами идентификатор разговора
event_idИдентификатор события в Лия Sense. Сохраните его: он понадобится при обращении в поддержку
messagesЧисло принятых сообщений (для звонка — 0). В ответе duplicate отсутствует

В details[].field ответа 400 указано поле с ошибкой в точечной нотации, например messages.0.text или audio.url; details[].message — причина (может быть на английском).

Что делать при каждом ответе

  • 201, 200 — успех. Ответ подтверждает приём в очередь, а не появление разговора в Лия Sense: аудио скачивается и разговор обрабатывается ночью. Проверить результат можно в истории запусков задачи сбора (Лия Sense → Задачи, разверните строку задачи).
  • 400 — исправьте тело по details и отправьте снова с тем же external_id: отклонённое событие не считается принятым.
  • 404 — не повторяйте автоматически; проверьте ссылку и получите действующую у администратора Лия Sense.
  • 408 — повторите запрос целиком с тем же external_id.
  • 409 — попросите администратора завершить подключение источника, проверьте /ping и повторите запрос.
  • 413 — уменьшите тело до 1 МиБ (например, сократите metadata) и повторите с тем же external_id. Не делите один разговор на несколько событий: они станут отдельными разговорами.
  • 429 — подождите указанное в Retry-After число секунд и повторите.
  • 5xx или сетевая ошибка — повторите запрос с тем же external_id, лучше с нарастающей задержкой; дубликаты не создаются.

Лимиты

ОграничениеЗначение
Размер тела запроса1 МиБ
Время передачи тела30 секунд
Сообщений в одном диалоге2000
Запросов в минуту на одну ссылку600
Принятых событий в сутки на одну ссылку100 000 (сутки по UTC). Повторы (200 duplicate) не расходуют квоту

Дополнительно действует защита от перебора по IP-адресу. При превышении любого лимита ответ — 429 с заголовком Retry-After.

Если ваши объёмы больше — обратитесь в поддержку.

Ссылка на запись

Система не скачивает запись в момент приёма события — только проверяет форму ссылки. Скачивание происходит ночью, во время выгрузки в Лия Sense. Отсюда требования:

Что должна обеспечить ваша система

  • Ссылка остаётся рабочей до успешного скачивания записи: минимум — до завершения ближайшей ночной выгрузки, поэтому не ставьте срок жизни ровно 24 часа. Если скачать запись не удалось, система повторяет отправку — сразу в той же выгрузке, затем в следующие ночи — пока событие не накопит пять отказов; поэтому рекомендуем держать ссылку рабочей не меньше 7 дней. Ссылка, истекающая через 15 минут, гарантированно даст звонок без аудио.
  • Ответ 200 с файлом в теле, без редиректов: любой ответ 3xx считается ошибкой.
  • Без авторизации: ни заголовка Authorization, ни cookies. Подписанный параметр в query-строке допустим, если он живёт достаточно долго.
  • Файл отдаётся за 120 секунд. Отдельного ограничения на размер файла нет — действует только это время.
  • Формат файла — один из поддерживаемых Лия Sense: MP3, WAV, OGG, OPUS, M4A, FLAC.

Что проверяется при приёме (нарушение — 400 с описанием в details)

  • Схема строго https.
  • В адресе нет учётных данных (user:password@).
  • Длина не больше 2048 символов, без пробелов.
  • Хост — публично доступное доменное имя или публичный IP-адрес. Отклоняются приватные, локальные и служебные IP-адреса (10.x, 192.168.x, 127.0.0.1, link-local, multicast) и неоднозначные числовые хосты вроде 2130706433. Доменное имя при приёме через DNS не проверяется — его доступность выяснится только при скачивании.
  • По вашему запросу для источника можно включить белый список хостов записей: обратитесь в поддержку и передайте список доменов, например records.example.com. После включения ссылки на другие хосты будут отклоняться с 400.
Проверьте ссылку так же, как её будет скачивать система
status=$(curl -sS --max-time 120 -D /tmp/rec.headers -o /tmp/rec.mp3 -w '%{http_code}' \
"https://pbx.example.com/recordings/871.mp3")
echo "HTTP $status" # должно быть ровно 200
test -s /tmp/rec.mp3 && echo "файл не пустой"
grep -Ei 'Content-Type|Content-Length' /tmp/rec.headers

Команда намеренно без -L: любой ответ 3xx — ошибка. Повторите проверку через несколько дней: именно требование «ссылка живёт достаточно долго» нарушается чаще всего.

Когда данные появятся в Лия Sense

  • Выгрузку делает периодическая задача сбора, которая запускается раз в сутки в ночном окне 00:00–04:00 МСК. В выгрузку попадают события, принятые до старта задачи; остальные будут обработаны во время следующего запуска.
  • Разовая задача с датами для вебхука не работает: она всегда вернёт ноль разговоров. Используйте только периодическую задачу; для немедленной проверки её можно запустить вручную.
  • Фильтры задачи — «Канал» и, для звонков, «Направление» — применяются на этапе выгрузки: отфильтрованные события в Лия Sense не попадают.
  • Если Лия Sense не смогла принять конкретный звонок (как правило — недоступная ссылка на запись), система повторяет отправку: сразу в той же выгрузке и затем в следующие ночи. Событие, накопившее пять отказов, больше не отправляется; исправьте ссылку и пришлите событие с новым external_id.

Чек-лист для разработчика

  1. Получите ссылку у администратора Лия Sense и проверьте её через /ping — ожидается state: "bound".
  2. Отправляйте по одному разговору на запрос, сразу после его завершения; в external_id используйте стабильный идентификатор из вашей системы.
  3. Все даты — ISO 8601 со смещением часового пояса; сообщения диалога — в хронологическом порядке.
  4. Для звонков: ссылка на запись по https, без авторизации и редиректов, живёт не меньше 7 дней; format соответствует файлу.
  5. Обрабатывайте ответы по разделу Ответы и ошибки; сохраняйте event_id для обращений в поддержку.
  6. Убедитесь, что над источником создана и включена периодическая задача сбора, а привязанный пайплайн запускает анализ.