Graine AI

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

SurfaceFieldNature
Webhook / branchingsub_statusA 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 recordhangup_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 / debuggingplatform_analysis.call_disconnect_reasonThe 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.

Triggersub_statusTypical call_disconnect_reasonConfigured
The person hangs upCUSTOMER_ENDED_CALLuser_hangup— the normal outcome.
The agent decides the conversation is doneAGENT_ENDED_CALLllm_prompted_hangupThe agent's conversation settings — its prompt defines when it may close the call.
Silence timeoutINACTIVITY_TIMEOUTinactivity_timeoutConversation settings — seconds of silence before the agent gives up.
Voicemail answersVOICEMAIL_DETECTED / voicemail_detectedvoicemail_detectedVoicemail detection on the agent. Arrives under two statuses — see the voicemail rule. Late-verdict voicemails are not billed.
Transfer to a humanTRANSFERREDThe agent's transfer tool. Outranks agent-ended: a transferred call the agent then closed is TRANSFERRED.
Web-call duration ceilingMAX_CALL_DURATIONweb_call_max_duration_reachedWeb-call settings.
The AI stack errors mid-callAGENT_ERRORtranscriber_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}/stopstopped_by_userThe stop endpoint, or stopping the batch.
Cancelled before it dialledcanceledCancelling a scheduled call or its campaign.
The wallet cannot fund itinsufficient_balanceTop up; see error 1400. Every retry fails identically until funded.
Carrier-side failurebusy_here, no_answer, declined, sip_<code>, …carrier prose— the failure resolution ladder.
Platform faultplatform_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

  1. Read the call: GET /v2/calls/{execution_id}status, hangup_by, hangup_reason, duration_seconds.
  2. Read the webhook record for the same call_id: sub_status is the verdict; platform_analysis.call_disconnect_reason is the raw detail behind it.
  3. A sip_<code> you see repeatedly from one destination or region is a carrier-side pattern — quote the code and the call_id to support.
  4. 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.

On this page