Skip to main content

Вебхуки

Вебхуки — основной канал результатов; опрос GET /v1/calls/{call_id} — запасной. Администратор регистрирует адрес на агенте: откройте агента в редакторе → вкладка «Настройки» → раздел «Вебхуки (после звонка)». Содержимое полезной нагрузки описано также на странице «Вебхуки».
  • URLhttp:// или 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во время звонка, когда наблюдатель соответствия, настроенный на агенте, фиксирует нарушение и его реакция включает уведомление. Используйте для оповещения супервизора в реальном времени. severitylow | 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 секунд. Повторные доставки переподписываются свежей меткой, поэтому окно с повторами не конфликтует.
В Python (Flask) используйте 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. Устойчивый адрес начнёт работать сам, когда запись будет готова.