Hangups & Termination
Who ended a call, why, and where that answer lives — the three surfaces that record termination, every trigger that can end a call, and the exact values each one produces.
Every finished call records how it ended. That answer lives on three surfaces, and they are not interchangeable — one is stable vocabulary to branch on, two are context for humans.
The three surfaces
| Surface | Field | Nature |
|---|---|---|
| Webhook / branching | sub_status | A closed, stable vocabulary — 8 UPPERCASE success tokens, 25 lowercase failure tokens plus the sip_<code> family. The only surface to write code against. Full tables on Webhook Payloads. |
| Execution record | hangup_by, hangup_reason on GET /v2/calls/{execution_id} | Which side hung up, and a human-readable sentence for why. Informational — casing and wording vary by carrier and path. Display it; do not switch on it. |
| Webhook / debugging | platform_analysis.call_disconnect_reason | The raw internal hangup detail, unmapped and in original casing (user_hangup, llm_prompted_hangup, inactivity_timeout, …). For debugging a specific call, never for branching — sub_status is its mapped, guaranteed form. |
There are no numeric hangup codes on this API
Termination is expressed as named tokens, not provider code tables. The one
carrier-numeric that can surface is the sip_<code> sub-status family
(sip_484, …) when a SIP-style carrier rejects a call with an unusual code —
match it with a prefix rule, and see
carrier cause.
Everything that can end a call
Each trigger, the values it produces, and where its knob lives.
| Trigger | sub_status | Typical call_disconnect_reason | Configured |
|---|---|---|---|
| The person hangs up | CUSTOMER_ENDED_CALL | user_hangup | — the normal outcome. |
| The agent decides the conversation is done | AGENT_ENDED_CALL | llm_prompted_hangup | The agent's conversation settings — its prompt defines when it may close the call. |
| Silence timeout | INACTIVITY_TIMEOUT | inactivity_timeout | Conversation settings — seconds of silence before the agent gives up. |
| Voicemail answers | VOICEMAIL_DETECTED / voicemail_detected | voicemail_detected | Voicemail detection on the agent. Arrives under two statuses — see the voicemail rule. Late-verdict voicemails are not billed. |
| Transfer to a human | TRANSFERRED | — | The agent's transfer tool. Outranks agent-ended: a transferred call the agent then closed is TRANSFERRED. |
| Web-call duration ceiling | MAX_CALL_DURATION | web_call_max_duration_reached | Web-call settings. |
| The AI stack errors mid-call | AGENT_ERROR | transcriber_error, transcriber_connection_error, synthesizer_error, llm_error | — arrives with status: "completed" and is billable: audio flowed and the carrier completed the call. Count it among failed conversations. |
You stop it — POST /v2/calls/{execution_id}/stop | stopped_by_user | — | The stop endpoint, or stopping the batch. |
| Cancelled before it dialled | canceled | — | Cancelling a scheduled call or its campaign. |
| The wallet cannot fund it | insufficient_balance | — | Top up; see error 1400. Every retry fails identically until funded. |
| Carrier-side failure | busy_here, no_answer, declined, sip_<code>, … | carrier prose | — the failure resolution ladder. |
| Platform fault | platform_error, call_disconnected | — | — ours; if persistent, quote the call_id to support. |
Two rules that prevent miscounting
Casing is the family. UPPERCASE sub_status ⇒ the call connected;
lowercase ⇒ it did not. First match wins, in the documented order — a
call satisfying several rules reports the highest-ranked one, so voicemail
beats transfer beats agent-ended. The full ordering is part of the contract.
hangup_by values
Observed values today: Caller, Callee, system, transfer — which side
of the line, or which mechanism, initiated the disconnect. Casing follows the
recording path and is not normalised; treat the field as display text. When
your logic needs "who ended it", derive it from sub_status
(CUSTOMER_ENDED_CALL vs AGENT_ENDED_CALL vs TRANSFERRED), which is
guaranteed.
Debugging a termination
- Read the call:
GET /v2/calls/{execution_id}—status,hangup_by,hangup_reason,duration_seconds. - Read the webhook record for the same
call_id:sub_statusis the verdict;platform_analysis.call_disconnect_reasonis the raw detail behind it. - A
sip_<code>you see repeatedly from one destination or region is a carrier-side pattern — quote the code and thecall_idto support. - For a call that ended earlier than expected with
AGENT_ENDED_CALL, read the transcript tail: the agent closes when its prompt says the goal is met — the fix is prompt wording, not an API setting.

