Голосовые звонки WhatsApp в службе поддержки: от ссылки до вебхука
Чтобы добавить голосовые звонки WhatsApp в систему поддержки через UnifyPort, сервер подготавливает звонок с media_type: "voice" и передаёт назначенному оператору приватную ссылку для браузера. Набор начинается, когда оператор открывает её и нажимает Start call. Сервер связывает полученный call_id с задачей поддержки и отслеживает результат по событиям звонка.
Руководство описывает исходящие звонки одному собеседнику, запускаемые человеком в закрытой бете. UnifyPort использует неофициальный интерфейс; это не инструкция для Meta WhatsApp Business Calling API. Обзор возможностей и порядок подачи заявки есть в анонсе беты звонков.
1. Подготовьте аккаунт и выберите диалог
Попросите техническую поддержку включить доступ к звонкам для рабочего пространства. Создание API-ключа не открывает доступ: is_beta — признак допуска только для чтения, а не настройка, которую можно изменить через metadata рабочего пространства.
Перед отображением действия звонка убедитесь, что аккаунт мессенджера WhatsApp авторизован и его runtime_status равен running. Выберите личный диалог этого же аккаунта и сохраните возвращённый conversation_id. Не подставляйте вместо него номер телефона и не собирайте идентификатор провайдера самостоятельно.
В приложении закрепите за действием выбранные рабочее пространство, аккаунт, диалог и задачу поддержки. Проверяйте права оператора на сервере. Новое сообщение или переключение диалога не должно незаметно менять адресата уже подготовленного звонка.
В примерах API-ключ остаётся на сервере. Браузер получает только нужную ему приватную ссылку.
2. Подготовьте голосовой звонок на сервере
Замените заполнители API-ключом рабочего пространства, подключённым аккаунтом и выбранным диалогом. Выполните запрос на сервере:
curl -X POST 'https://api.unifyport.ai/v1/calls' \
-H 'X-Api-Key: <YOUR_API_KEY>' \
-H 'Content-Type: application/json' \
-d '{
"account_id": "acc_example",
"to": { "id": "<conversation_id>", "type": "user" },
"media_type": "voice"
}'
Справочник создания звонка описывает ответ 201 Created следующей структуры. Значения приведены для примера и не подтверждают реальный звонок:
{
"request_id": "req_example",
"data": {
"call_id": "call_example",
"direction": "outbound",
"media_type": "voice",
"state": "preparing",
"join_url": "https://calls.example.com/call/call_example#ticket=example-only",
"expires_at": "2026-10-27T08:02:00Z"
}
}
Сохраните data.call_id вместе с выбранными аккаунтом, диалогом и задачей. Сохраните request_id для диагностики. Считайте data.join_url секретом: передайте его без изменений назначенному оператору. Полный URL не должен попадать в журналы, аналитику или публичные сообщения.
201 и preparing означают, что ссылка готова. Они не означают, что телефон клиента звонит или клиент ответил.
3. Дайте оператору начать звонок в браузере
В интерфейсе покажите выбранного клиента и индикатор готовности к запуску. Предоставляйте join_url только нужному оператору. На странице звонка оператор разрешает доступ к микрофону и нажимает Start call до наступления expires_at.
По умолчанию подготовка действует две минуты с момента создания, но сервер может менять срок. Читайте expires_at, а не вычисляйте жёстко заданный дедлайн. Этот срок не ограничивает длительность уже соединённого звонка. Доступ к микрофону обязателен, к камере — необязателен.
Блокируйте повторную отправку, пока приложение ждёт ответ создания. Если ответ потерян, покажите неопределённый результат и запросите текущий звонок, прежде чем рассматривать новый запрос создания. Одна заблокированная кнопка не координирует нескольких операторов, поэтому обрабатывайте конфликты на уровне аккаунта и на сервере.
4. Свяжите вебхуки с задачей поддержки
Если вебхук использует явный список событий, добавьте оба имени ниже, сохранив остальные нужные подписки. Это фрагмент поля, а не полный запрос создания вебхука:
{
"subscribed_events": ["call.answered", "call.ended"]
}
В рабочих пространствах с доступом к бете *, пустой или отсутствующий список также включает разрешённые события звонков. Подписка с шаблоном не предоставляет доступ к этим событиям рабочим пространствам вне беты.
| Наблюдение | Действие приложения |
|---|---|
data.call_id в ответе создания | Связать звонок с выбранной задачей поддержки |
data.call.id в call.answered | Зафиксировать, что соединение установлено |
data.call.id, data.reason, data.duration_seconds в call.ended | Записать причину завершения и длительность соединения |
Перед обработкой подписанных событий настройте signing_secret у вебхука и храните этот секрет на сервере.
Проверяйте подпись по исходному телу запроса и устраняйте дубликаты по id конверта события согласно контракту доставки. Сопоставляйте account_id и data.call.id события с сохранённой связью. Запоздавшее или повторное событие не должно возвращать завершённый звонок в состояние разговора в интерфейсе.
Храните подтверждение ответа отдельно: duration_seconds округляется вниз, поэтому 0 само по себе не доказывает отсутствие ответа. Синхронная ошибка создания может не сопровождаться событием завершения; фиксируйте HTTP-ошибки отдельно. Полные данные описаны в справочниках события ответа и события завершения. Завершение звонка не означает автоматического решения задачи поддержки.
5. Запросите или завершите управляемый звонок
Для восстановления состояния или потерянного идентификатора используйте получение текущего звонка:
curl --get 'https://api.unifyport.ai/v1/calls/active' \
-H 'X-Api-Key: <YOUR_API_KEY>' \
--data-urlencode 'account_id=acc_example'
data: {} означает отсутствие текущего звонка, которым UnifyPort управляет для этого аккаунта. Это не история звонков, не подтверждение результата предыдущей попытки и не способ обнаружить звонок в родном приложении WhatsApp. Ответ не выдаёт одноразовый join_url повторно.
Когда оператор решает завершить известный звонок, используйте сохранённый call_id и его аккаунт:
curl -X POST 'https://api.unifyport.ai/v1/calls/call_example/hangup' \
-H 'X-Api-Key: <YOUR_API_KEY>' \
-H 'Content-Type: application/json' \
-d '{"account_id":"acc_example"}'
Метод завершения звонка возвращает конечное состояние ended или failed; повторный запрос может вернуть уже существующее конечное состояние. Не повторяйте запрос лишь из-за failed и не завершайте звонок другого оператора только для устранения конфликта создания.
Частые проблемы интеграции
| Ситуация | Следующий шаг |
|---|---|
403 beta_feature_not_enabled | Уточнить допуск рабочего пространства у поддержки; metadata не включает доступ |
| Истёк срок ссылки подготовки | Перестать использовать ссылку, проверить текущий звонок и дать оператору явно решить, нужна ли новая подготовка |
| Браузеру недоступен микрофон | Проверить разрешение для страницы звонка, прежде чем ожидать успешного набора |
| Создание конфликтует с существующим звонком | Запросить управляемый звонок и определить его оператора и назначение в приложении |
| Тайм-аут создания или завершения | Сохранить неопределённый результат, сопоставить текущее состояние и полученные события до решения о повторе |
Проверьте полный сценарий поддержки
В согласованном тестовом диалоге проверьте соединение и завершение, просроченную ссылку, отказ в доступе к микрофону, двойное нажатие, конфликт аккаунта и потерянный HTTP-ответ. Убедитесь, что повторная доставка событий не создаёт дубли задач, а полные ссылки не попадают в журналы. Это проверки для приёмки вашей интеграции, а не результаты испытаний, заявленные в статье.
Можно автоматически обзвонить список номеров?
Нет. Этот процесс требует conversation_id выбранного аккаунта и нажатия Start call человеком в браузере.
Можно принимать входящие звонки через эту интеграцию?
В текущей бете — нет. call.incoming зарезервирован; наличие в каталоге событий не открывает входящие звонки. Групповые звонки, запись, транскрибация, SIP/PBX и голосовые ИИ-агенты также не входят в эту бету.
С чего начать команде?
Если доступ ещё не открыт, подайте заявку технической поддержке, указав ID рабочего пространства и предполагаемый сценарий поддержки. Не отправляйте API-ключ или приватную ссылку. После включения доступа проверьте один выбранный диалог по справочнику создания звонка и затем добавляйте действие в рабочий процесс поддержки.
Превратите интеграцию сообщений в стабильный продуктовый pipeline.
Начните с отправки через единый API, затем возвращайте входящие сообщения в бизнес-систему стандартными событиями.