Вебхук: API для отправки звонков и диалогов
Вебхук — универсальный способ передать разговоры в Лия Sense из системы, для которой нет готовой интеграции. Вы получаете персональную ссылку и отправляете на неё каждый завершённый звонок или диалог одним HTTP-запросом с JSON. Дальше система всё делает сама: раз в сутки накопленные разговоры уходят на транскрибацию и анализ.
Есть две интеграции с одинаковым API, но разным типом данных:
| Интеграция | Тип ссылки (connector_type) | Что принимает |
|---|---|---|
| Вебхук: звонки | calls | События type: "call" — метаданные звонка и ссылка на аудиозапись |
| Вебхук: диалоги | dialogs | События type: "text" — метаданные диалога и массив сообщений |
Тип фиксируется при подключении. Звонок, отправленный по ссылке для диалогов (и наоборот), отклоняется с ошибкой 400.
Как это работает
- Вы подключаете источник «Вебхук» в Лия Sense и получаете ссылку вида
https://webhooks.sense.lia.chat/api/v1/webhooks/<токен>/events. - Ваша система отправляет
POSTна эту ссылку по мере завершения разговоров — по одному разговору на запрос. Событие сразу принимается и ставится в очередь, ответ —201. - Периодическая задача сбора раз в сутки (ночью, 00:00–04:00 МСК) забирает из очереди новые разговоры и передаёт их в Лия Sense. Разговоры, отправленные днём, появляются в системе на следующее утро.
- Аудиозаписи передаются только ссылкой: система не принимает файлы в теле запроса, а скачивает запись по вашей ссылке во время ночной выгрузки. Поэтому ссылка должна оставаться рабочей после отправки события — см. требования к ссылке на запись.
Приём событий не зависит от задачи сбора: пока задачи нет, события копятся в очереди и удаляются через 30 дней. Чтобы данные не пропали, сразу после подключения создайте периодическую задачу над этим источником.
Подключение
- Лия Sense → Задачи → вкладка Источники → Добавить источник.
- Выберите тип: Голос для звонков или Чаты для диалогов, затем платформу Вебхук: звонки или Вебхук: диалоги.
- В форме сразу появится поле Ссылка для отправки данных. Скопируйте её кнопкой рядом с полем и передайте тому, кто настраивает отправку.
- Введите название источника и нажмите Подключить. Ссылка закрепляется за источником в момент подключения.
- Создайте периодическую задачу сбора над источником (см. предупреждение выше).
Полей для ввода учётных данных у вебхука нет. Нет и поля «Лимит запросов в секунду»: Лия 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"}
| Поле | Значения | Описание |
|---|---|---|
state | bound | Ссылка привязана к источнику, события принимаются |
issued | Ссылка выдана, но источник ещё не подключён: POST на неё вернёт 409 | |
connector_type | calls | Ссылка принимает события 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" строкой отклоняется |
channels | 1 или 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 | Превышен один из лимитов |
Поля успешного ответа:
| Поле | Описание |
|---|---|
status | accepted — новое событие, 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.
Чек-лист для разработчика
- Получите ссылку у администратора Лия Sense и проверьте её через
/ping— ожидаетсяstate: "bound". - Отправляйте по одному разговору на запрос, сразу после его завершения; в
external_idиспользуйте стабильный идентификатор из вашей системы. - Все даты — ISO 8601 со смещением часового пояса; сообщения диалога — в хронологическом порядке.
- Для звонков: ссылка на запись по
https, без авторизации и редиректов, живёт не меньше 7 дней;formatсоответствует файлу. - Обрабатывайте ответы по разделу Ответы и ошибки; сохраняйте
event_idдля обращений в поддержку. - Убедитесь, что над источником создана и включена периодическая задача сбора, а привязанный пайплайн запускает анализ.