Every ended call has a disconnection_reason. It is on the call object, in the call_ended webhook, as a column and filter in Call History, and in exports. Start here when calls end earlier than expected.
Calls that never connected have call_status: "not_connected" instead of ended. Their reasons are marked Not connected below.
Normal endings#
| Reason | Trigger | Action |
|---|---|---|
user_hangup |
The caller hung up or left the room | None. If callers hang up early, check latency and the greeting |
agent_hangup |
The agent's end_call tool ended the call |
Check the prompt says when to end |
call_transfer |
A transfer bridged the caller to another destination, or an inbound call with no free slot was sent to the number's fallback | None |
voicemail_reached |
Voicemail detection found a machine and the voicemail option hung up or left a message | Adjust the agent's voicemail_option |
ivr_reached |
The agent detected an automated phone menu and the IVR option ended the call | Adjust ivr_option |
inactivity |
Nobody spoke for end_call_after_silence_ms (default 600,000 ms, minimum 10,000) |
Lower or raise the timeout; use reminders (reminder_trigger_ms, reminder_max_count) |
max_duration_reached |
The call reached max_call_duration_ms (default 1 hour, 60,000 to 7,200,000 ms) |
Raise the limit if long calls are expected |
manual_stopped |
The call was stopped through the API (stop-call) |
None |
call_take_over |
A person took over the call from Live Monitoring | None |
user_requested_dnc |
DNC detection heard the caller ask not to be called; the contact is marked do-not-call | None. Set enable_dnc_detection off to stop it |
transfer_bridged, transfer_cancelled |
Transfer outcomes recorded for agentic transfer legs | See Debug transfers |
Safety endings#
| Reason | Trigger | Action |
|---|---|---|
scam_detected |
The scam guardrail input topic matched what the caller said |
Review the transcript. Remove scam from guardrail_config.input_topics if it fires on real callers |
guardrail_triggered |
A guardrail ended the call | Review guardrail_config and its fallback_message |
Errors during the call#
| Reason | Trigger | Action |
|---|---|---|
error_llm |
The LLM failed and could not recover | Set an LLM fallback model in Reliability settings; check the provider key and status |
error_asr |
Speech recognition failed and could not recover | Set a Speech recognition fallback |
error_tts |
Text to speech failed and could not recover | Add Fallback Voices to the agent and a workspace Fallback voice |
error_unknown |
Any other runtime failure, or a call that stopped sending events | Open the call's Detail Logs (public_log_url) |
error_llm_websocket_open |
The runtime could not open your Custom LLM WebSocket | Check the URL is reachable from the runtime and accepts the connection |
error_llm_websocket_lost_connection |
Your Custom LLM socket closed mid-call | Check your server's logs and timeouts |
error_llm_websocket_runtime |
Your Custom LLM server sent an error or failed while responding | Check your server's logs |
error_llm_websocket_corrupt_payload |
Your Custom LLM server sent a message that is not valid protocol JSON | Validate your messages against the Custom LLM protocol |
Not connected#
| Reason | Trigger | Action |
|---|---|---|
dial_busy |
SIP 486 or 600 | Retry later |
dial_no_answer |
SIP 480, 408 or 487, or no answer within ring_duration_ms (default 30 s) |
Raise ring_duration_ms; call at better times |
dial_failed |
Any other dial failure | See Debug outbound calls |
invalid_destination |
SIP 400, 404, 410, 484, 485 or 604 | Check the number is valid E.164 |
user_declined |
SIP 603 | The callee rejected the call |
marked_as_spam |
SIP 608: the carrier flagged the call | Check caller ID reputation and branding |
telephony_provider_permission_denied |
SIP 401, 403 or 407 | Check trunk credentials and that your carrier allows the destination |
telephony_provider_unavailable |
SIP 502, 503 or 504 | Carrier outage; retry |
sip_routing_error |
No outbound trunk for the number | Provision the number, or set the trunk |
concurrency_limit_reached |
No free slot (inbound queue timed out with a fallback number that failed, or a registered call could not be admitted) | Raise the limit or reserve inbound slots; see Limits and concurrency |
no_concurrency_fallback |
An inbound caller waited 40 seconds for a slot and the number has no fallback_number |
Set a fallback_number on the number, raise the limit |
registered_call_timeout |
A call created with register-phone-call was not dialled within 5 minutes |
Dial the returned SIP URI sooner |
error_user_not_joined |
A web call's browser never joined within 5 minutes (TRIED_WEB_JOIN_TIMEOUT_MS) |
Start the call in the browser right after creating it |
no_valid_payment |
A batch dial was refused for lack of credit | Add credit or turn on auto-recharge; see Billing |
credit_exhausted |
Credit ran out | Add credit |
budget_reached is also a valid reason; when the monthly budget is reached, new calls are refused with 402 and error_code: "budget_reached" before a call is created.
Accepted but not set by Tried#
These values exist for compatibility with the Retell API and are accepted on stored calls, but the platform does not currently set them: error_no_audio_received, user_requested_callback, network_blocked, error_retell.
Find patterns#
- In Call History, show the Disconnection Reason column and filter on it.
- In Alerting, rules on
call_success_rate,transfer_call_failure_countandcustom_function_failure_countcatch rises in failed calls. - Export calls to CSV from Exports; the
disconnection_reasoncolumn is included.