Вебхуки
Вебхуки — основной канал результатов; опросGET /v1/calls/{call_id} — запасной. Администратор регистрирует адрес на агенте: откройте агента в редакторе → вкладка «Настройки» → раздел «Вебхуки (после звонка)». Содержимое полезной нагрузки описано также на странице «Вебхуки».
- URL —
http://илиhttps://(платформа не требует HTTPS; используйте его всё равно). Доставка подчиняется политике исходящих адресов платформы: приватные адреса и запрещённые хосты могут быть отклонены конфигурацией оператора, и такие доставки уходят в очередь недоставленных. - Подписка на события — любое подмножество трёх событий ниже (по умолчанию
call_ended+call_analyzed). - Секрет подписи — задаётся на стороне платформы ссылкой на серверный секрет (именем переменной окружения развёртывания); само значение в кабинет не вводится и передаётся вам отдельным каналом.
- Агент — адрес привязан ровно к одному живому агенту организации. Поле обязательное: пустое имя агента отклоняется с кодом
webhooks.agent_required, неизвестный или архивный — с кодомwebhooks.agent_not_found. Уровня «вся организация» не бывает: чтобы покрыть несколько агентов, зарегистрируйте адрес у каждого (URL можно переиспользовать, различать поagent_idв payload). - Тестовые события — отдельный флаг, при котором адрес получает и события тестовых сессий редактора (
mode: "test"). - Таймаут — сколько платформа ждёт ваш
2xx(по умолчанию платформы 10 с, настраивается по адресу до 120 с;0означает значение платформы).
Конверт события
Каждое событие — HTTP(S)POST с телом JSON (UTF-8, не-ASCII символы не экранируются).
Заголовки каждой доставки:
Какое событие когда приходит
call_ended— как только завершилась сессия, до анализа и обычно до финализации записи. Это раннее уведомление: транскрипт,call_status,status_reason,error_codeи поля времени (answered_at,media_started_at,talk_from,talk_to).latencyприсутствует только если собирались метрики по тёрнам; её ключи — сырые имена метрик, оканчивающиеся на_ms(turn_ms,llm_ms,asr_ms,tts_ms, …), значения —{avg, max}в миллисекундах.call_analyzed— когда закончились оба пост-конвейера: анализ (терминальный в любом изdone,skipped,failed,interrupted— событие приходит всегда) и финализация записи. Событие самодостаточно: полный блок звонка + анализ + готовая запись. Если нужен только итог, подпишитесь на одинcall_analyzedи пропуститеcall_ended. Его блокcallидентиченcall_ended.call, за исключением того, что запись остаётся на верхнем уровне и внутриcallне дублируется.observer_incident— во время звонка, когда наблюдатель соответствия, настроенный на агенте, фиксирует нарушение и его реакция включает уведомление. Используйте для оповещения супервизора в реальном времени.severity—low|medium|high(неизвестное значение приводится кmedium),confidence— 0–1 илиnull,modeможет быть"", если неизвестен. Несколько инцидентов в одном звонке дают отдельные события; повторы одного инцидента сохраняют тот жеevent_id.ping— тестовая доставка по кнопке в кабинете;agent_id— агент, к которому привязан адрес (""для адреса уровня организации). Ответьте2xxи проверьте подпись как у настоящего события. Не повторяется.
analysis_status не done, call_analysis равен {}, а поле верхнего уровня reason объясняет причину (например voicemail, no_speech, analytics_disabled); analytics_tier, analytics_model_used и analytics_language тогда пустые строки.
Семантика доставки
- Не менее одного раза. Доставка считается успешной только по
2xxот вашего адреса; всё остальное (не-2xx, таймаут, ошибка соединения) повторяется. Дубли вы будете получать — дедуплицируйте поevent_id. - Повторы с экспоненциальной задержкой — это настройки платформы, а не адреса: по умолчанию до 8 попыток с паузами 30, 60, 120, 240, 480, 960 и 1920 секунд. После последней неудачной попытки событие уходит в очередь недоставленных, откуда администратор может доставить его вручную (История → детали сессии → доставки вебхуков). Недоставленные со временем вычищаются, поэтому окно ручного повтора конечно.
- Доставки всегда идут на текущий адрес, поэтому опечатку в URL можно исправить и событие доставить заново; удалённый или отключённый адрес отправляет свои ожидающие события прямо в очередь недоставленных.
- Порядок — по возможности.
call_endedпопадает в очередь раньшеcall_analyzed, но порядок доставки не гарантирован;observer_incidentприходит посреди звонка. Обрабатывайте каждое событие независимо, сопоставляя поcall_id. - Отвечайте быстро. Возвращайте
2xxсразу после проверки подписи и обрабатывайте асинхронно: платформа ждёт не дольше таймаута адреса.
Проверка подписи
X-CFlow-Timestamp — целое число секунд эпохи. Проверяйте по сырым байтам тела (до разбора JSON и повторной сериализации — тело в UTF-8 с неэкранированными не-ASCII символами), сравнивайте за постоянное время и отклоняйте устаревшие метки — окно 300 секунд. Повторные доставки переподписываются свежей меткой, поэтому окно с повторами не конфликтует.
request.get_data(); в Node (Express) — express.raw({ type: "application/json" }): оба сохраняют точные байты, по которым считалась подпись.
Записи разговоров
recording_url — устойчивый адрес (https://{{HUBTALK_FQDN}}/api/recordings/rec-…): он не истекает и его можно сохранять у себя. Каждый GET по нему отвечает 307 со свежей короткоживущей ссылкой на скачивание, поэтому адрес, сохранённый месяцы назад, продолжает работать — пока сама запись не удалена политикой хранения (recording.expires_at). Другие ответы: 409, пока запись ещё не готова (продолжайте опрашивать), 410 после удаления, 503 при временной недоступности хранилища. Добавьте ?download=1, чтобы отдавалось как вложение. Адрес не требует аутентификации, но неугадываем; обращайтесь с ним как с токеном доступа.
Особый случай: если запись финализируется необычно долго (дольше ожидания платформы, по умолчанию 30 с), call_analyzed всё равно отправляется — с текущим нетерминальным статусом записи и пустым recording_url. Устойчивый адрес начнёт работать сам, когда запись будет готова.