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

Звонки: как это работает

Обновлено 17 октября 2026

Звонки в API — на двух уровнях. События приходят во всех мессенджерах: кто звонит и чем кончилось — для журнала звонков и перезвона. Звонки со звуком ведёт сервер Gratio: отвечает или звонит сам, говорит файлом, пишет голос собеседника и отдаёт живой звук приложению — для голосового бота, автоинформатора, записи разговора.

Канал События звонков Звонки со звуком
MAX да если call_audio: true
WhatsApp да если call_audio: true — включается для номера
Telegram да нет: методы звонков отвечают 501

Проверьте флаг call_audio в getCapabilities. Без него методы звонков отвечают 501 not_supported, а события приходят как обычно.

События звонков

Звонки номеру и с его телефона приходят уведомлениями incomingCall и outgoingCall: первое, со status: "offer", — пока звонит, итог — следующим. Флаг в настройках — incomingCallWebhook.

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

Звонки со звуком

Метод Что делает
answerCall ответить на входящий
rejectCall отклонить входящий
makeCall позвонить собеседнику
playAudio сказать звуковым файлом
callAudio живой звук в обе стороны через WebSocket
hangUpCall положить трубку
getCall и getActiveCalls состояние звонка и идущие разговоры

callId — id звонка: idMessage из incomingCall со status: "offer" или callId из ответа makeCall. В MAX это UUID, в WhatsApp — строка вида call-….

Ход звонка, который ведёт сервер, приходит уведомлением callStateChanged: каждое состояние — один раз. Флаг в настройках — тот же incomingCallWebhook.

Входящий

Собеседник              Gratio                        Приложение
    │── звонок ───────────►│                                │
    │                      │── incomingCall: offer ────────►│
    │                      │◄────────────────── answerCall ─│
    │                      │── callStateChanged: connecting►│
    │◄═══ голос ══════════►│── callStateChanged: active ───►│
    │                      │◄═══ callAudio, playAudio ═════►│
    │── кладёт трубку ────►│── callStateChanged: ended ────►│
  1. Пришёл incomingCall со status: "offer". Пока звонит, вызовите answerCall или rejectCall. В MAX на это есть до 90 секунд. Ничего не вызвали — звонок звонит на телефонах номера как обычно.
  2. connecting — сервер соединяется, active — звук пошёл: приветствие из playUrl звучит с начала.
  3. Во время разговора: playAudio — файл, callAudio — живой звук, hangUpCall — положить трубку.
  4. ended — причина в endReason, запись — в recording.

Исходящий

Приложение                     Gratio                    Собеседник
    │── makeCall ─────────────────►│── звонок ────────────────►│
    │◄── callStateChanged: ringing │                           │
    │                              │◄─────────── снял трубку ──│
    │◄── callStateChanged: connecting                          │
    │◄── callStateChanged: active  │◄═══ голос ═══════════════►│
    │◄═══ callAudio, playAudio ═══►│                           │
    │── hangUpCall ───────────────►│── отбой ─────────────────►│
    │◄── callStateChanged: ended   │                           │
  1. makeCall отвечает объектом звонка со state: "ringing" — у собеседника звонит телефон.
  2. Снял трубку — connecting, затем active. Не ответил — ended с endReason: "no_answer"; в MAX ждём 45 секунд.
  3. Дальше — как у входящего.

Объект звонка

Его возвращают answerCall, makeCall, getCall, getActiveCalls; те же поля — в callStateChanged:

{
  "callId": "31A9ADA2-3889-4B2B-9D88-6A7142875D7F",
  "chatId": "8695836",
  "direction": "in",
  "state": "ended",
  "answered": true,
  "endReason": "hangup",
  "endedBy": "remote",
  "talkSeconds": 21.7,
  "recording": {
    "downloadUrl": "https://api.gratio.net/calls/7100000001/31A9ADA2-3889-4B2B-9D88-6A7142875D7F/5e0c….wav",
    "mimeType": "audio/wav",
    "durationSeconds": 21.7
  }
}
Поле Тип Описание
callId string id звонка
chatId string собеседник
direction string in — входящий, ответили через answerCall; out — позвонили через makeCall
state string ringing — звонит, connecting — соединяемся, active — идёт разговор, ended — закончился
answered boolean звук шёл — разговор состоялся
endReason string или null почему закончился — см. таблицу ниже; до конца — null
endedBy string или null кто закончил — см. таблицу ниже; до конца — null
talkSeconds number длительность разговора, секунд
recording object только после конца и если писали: downloadUrl, mimeType, durationSeconds

Почему закончился

endReason Что значит
hangup положили трубку
rejected звонок отклонён
canceled звонящий сбросил до ответа
busy собеседник занят
no_answer не ответили
max_duration разговор дошёл до maxDurationSeconds — сервер положил трубку
failed соединение не установилось или сорвалось
connection_lost MAX: пропала связь со звонком
disconnected MAX: канал отключился от мессенджера во время разговора
restart MAX: сервис обновлялся, и разговор не закончился за 2 минуты

Кто закончил

endedBy Кто
remote собеседник или мессенджер
api приложение: hangUpCall, rejectCall или команда hangup в callAudio
system сервер Gratio: предел длительности, нет ответа, сбой

Запись

  • Пишется голос собеседника: WAV, моно, 16 кГц, 16 бит. Включена по умолчанию, выключить — record: false.
  • Ссылка приходит в recording.downloadUrl в callStateChanged со state: "ended" и в getCall. Ссылка подписанная, без токена — как ссылки на файлы, см. Файлы и ссылки на них.
  • Свою запись в другом формате ведите через callAudio: там тот же звук кадрами.

Ограничения

Что Сколько При превышении
Разговор через API MAX: один на канал 409 busy; второй входящий звонит на телефонах как обычно
Исходящий звонок не чаще раза в 20 секунд MAX — 429 too_frequent, WhatsApp — 429 call_tempo, с Retry-After
Длительность maxDurationSeconds: 1–3600, по умолчанию 600 сервер кладёт трубку, endReason: "max_duration"
Очередь звука собеседнику до 5 минут 400 audio_queue_full
Видео не передаётся — только голос —

Что дальше

Была ли статья полезна?