call_started tells you a call is live. Use it to show the call on a wallboard, open a ticket, or look up the caller before the agent needs the data.
When it fires#
The event fires once per call, when the call moves from registered to ongoing:
- Inbound phone call: when the agent joins the caller's room and the conversation starts.
- Outbound phone call: when the callee answers.
- Web call: when the browser joins and the conversation starts.
It does not fire for calls that never connect (busy, no answer, failed dial). Those calls go straight to call_ended with call_status not_connected.
Which settings enable it#
call_startedmust be in the agent'swebhook_events. It is on by default.- A URL must resolve: the agent's
webhook_url, else the workspace webhook URL.
See Register a webhook.
Payload#
The body is { event, call }. The call object is the call at the moment it started: no transcript yet, no end time, no cost and no analysis.
{
"event": "call_started",
"call": {
"call_id": "call_3ffd3f3831b0d4e1a2c96f07",
"call_type": "phone_call",
"agent_id": "agent_5b0e2c7d9a1f4e3b8c6d2a10",
"agent_version": 2,
"agent_name": "Bondi Dental Front Desk",
"call_status": "ongoing",
"direction": "inbound",
"from_number": "+61412345678",
"to_number": "+61291234567",
"metadata": {
"sip_headers": { "x-clinic-id": "bondi-01" }
},
"retell_llm_dynamic_variables": {
"x-clinic-id": "bondi-01",
"business_name": "Bondi Dental"
},
"collected_dynamic_variables": {},
"start_timestamp": 1760672019481,
"end_timestamp": null,
"duration_ms": null,
"transcript": "",
"transcript_object": [],
"transcript_with_tool_calls": [],
"recording_url": null,
"recording_multi_channel_url": null,
"public_log_url": "https://api.tried.com.au/public/calls/call_3ffd3f3831b0d4e1a2c96f07/log?exp=1761276819500&sig=9c1e4b7a2d5f8e0c3b6a9d2e5f8c1b4a7d0e3f6a9c2b5e8d1f4a7c0e3b6d9f2a",
"knowledge_base_retrieved_contents_url": "https://api.tried.com.au/public/calls/call_3ffd3f3831b0d4e1a2c96f07/kb-retrievals?exp=1761276819500&sig=0b3e6a9c2f5d8b1e4a7c0d3f6b9e2a5c8d1f4b7e0a3c6f9d2b5e8a1c4f7d0b3e",
"stir_shaken_attestation": "A",
"transfer_destination": null,
"llm_token_usage": null,
"disconnection_reason": null,
"call_analysis": null,
"latency": {},
"call_cost": null,
"batch_call_id": null,
"contact_id": "contact_6e2a9c1f4b7d0e3a5c8f1b24"
}
}Fields#
The full field reference is on Call object. Fields to note at call_started:
| Field | Type | Value at this point |
|---|---|---|
call_status |
string | ongoing |
call_type |
string | phone_call or web_call |
direction |
string or null | inbound or outbound for phone calls, null for web calls |
from_number, to_number |
string or null | E.164 numbers for phone calls, null for web calls |
start_timestamp |
integer | When the conversation began, Unix ms |
end_timestamp, duration_ms |
null | Not ended yet |
transcript, transcript_object, transcript_with_tool_calls |
empty | Filled as the call runs. Use transcript_updated for live text |
retell_llm_dynamic_variables |
object | Variables the call started with: from the create call request, the inbound webhook, SIP X- headers, pre-call workflows |
metadata |
object | Your metadata. Inbound SIP headers appear under metadata.sip_headers |
public_log_url, knowledge_base_retrieved_contents_url |
string | Signed links valid for 7 days |
stir_shaken_attestation, caller_cnam |
string | Present only when the carrier provided them |
concurrency_burst |
boolean | Present and true only when the call was placed over the base concurrency limit |
custom_sip_headers |
object | Present only when the call was created with custom SIP headers |
contact_id |
string or null | The built-in contact for the caller on phone calls |
call_cost, call_analysis |
null | Arrive with call_ended and call_analyzed |
How to respond#
Reply with any 2xx within the timeout. The body of your reply is ignored. call_started cannot change the call: to pick the agent or set variables for inbound calls, use the inbound call webhook. To change a live call, use update-live-call.
Delivery is in the background, so a slow reply never delays the conversation. Failed attempts are retried up to 3 times (1 s, 2 s, 4 s).
if (payload.event === "call_started") {
const { call_id, from_number, agent_id } = payload.call;
await wallboard.add({ call_id, from_number, agent_id, started: payload.call.start_timestamp });
}