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

callStateChanged

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

Звонок ведёт сервер Gratio: приложение ответило на входящий через answerCall или позвонило через makeCall. Каждое изменение состояния такого звонка приходит уведомлением callStateChanged, один раз.

typeWebhook callStateChanged
typeMessage —
Флаг настроек incomingCallWebhook
Каналы MAX, WhatsApp — где getCapabilities отвечает call_audio: true

Пример

Разговор с Марией закончился: она положила трубку через 21,7 секунды, разговор записан:

{
  "typeWebhook": "callStateChanged",
  "instanceData": {"idInstance": 7100000001, "wid": "79990000001@c.us", "typeInstance": "v3", "channel": "max"},
  "timestamp": 1791988832,
  "idMessage": "31A9ADA2-3889-4B2B-9D88-6A7142875D7F",
  "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/5b1e….wav",
    "mimeType": "audio/wav",
    "durationSeconds": 21.7
  },
  "eventId": "callstate:31A9ADA2-3889-4B2B-9D88-6A7142875D7F:ended"
}

Поля

Поля на верхнем уровне — объект звонка, тот же, что отвечают getCall и getActiveCalls:

Поле Описание
callId id звонка; idMessage — то же значение. У входящего — idMessage из incomingCall (см. incomingCall и outgoingCall) со status: "offer"
chatId собеседник
direction in — ответили на входящий, out — позвонили через makeCall
state ringing, connecting, active или ended — таблица ниже
answered true — звук шёл, разговор состоялся
endReason почему звонок закончился; null, пока он идёт
endedBy кто закончил: remote — собеседник, api — приложение, system — сервер; null, пока звонок идёт
talkSeconds длительность разговора в секундах, с дробной частью
recording запись разговора — только в ended, если писали
eventId callstate:{callId}:{state} — каждое состояние приходит один раз

recording.downloadUrl — подписанная ссылка на WAV (голос собеседника, моно, 16 кГц), скачивается без токена.

Состояния

Состояния идут по порядку, каждое — одно уведомление:

входящий:  answerCall ─► connecting ─► active ─► ended
исходящий: makeCall ───► ringing ────► connecting ─► active ─► ended
state Что значит
ringing исходящий звонок звонит у собеседника
connecting собеседник ответил или вы ответили, звук ещё не пошёл
active разговор идёт, звук в обе стороны
ended звонок закончился — причина в endReason

Причины конца

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

Как обработать

Сохранить запись разговора, когда звонок закончился:

import requests


def on_call_state(body: dict) -> None:
    if body["typeWebhook"] != "callStateChanged":
        return
    if body["state"] != "ended":
        update_call(body["callId"], state=body["state"])
        return
    update_call(body["callId"], state="ended", reason=body["endReason"], seconds=body["talkSeconds"])
    recording = body.get("recording")
    if recording:
        r = requests.get(recording["downloadUrl"], timeout=120)   # без токена: ссылка подписанная
        r.raise_for_status()
        with open(f"call-{body['callId']}.wav", "wb") as out:
            out.write(r.content)

Что дальше

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