End Call Webhook
If the assistant has assistant_end_call_url configured, a POST request is sent when the call reaches a terminal state.
Webhook Request
POST /your-webhook-endpoint HTTP/1.1
Content-Type: application/json
{
"success": true,
"message": "Call details fetched successfully",
"data": {
"room_name": "550e8400-e29b-41d4-a716-446655440000_abc123",
"assistant_id": "550e8400-e29b-41d4-a716-446655440000",
"assistant_name": "Support Agent",
"to_number": "+15550200000",
"call_status": "completed",
"call_status_reason": null,
"sip_status_code": null,
"sip_status_text": null,
"answered_at": "2024-01-15T10:00:02.000Z",
"recording_path": "https://your-bucket.s3.us-east-1.amazonaws.com/recordings/call_abc123.ogg",
"transcripts": [
{
"speaker": "agent",
"text": "Hello John, how can I help you today?",
"timestamp": "2024-01-15T10:00:01.000Z"
},
{
"speaker": "user",
"text": "I need help with my order.",
"timestamp": "2024-01-15T10:00:05.000Z"
},
{
"speaker": "agent",
"text": "I'd be happy to help. What's your order number?",
"timestamp": "2024-01-15T10:00:08.000Z"
}
],
"started_at": "2024-01-15T10:00:00.000Z",
"ended_at": "2024-01-15T10:05:30.000Z",
"call_duration_minutes": 5.5,
"created_by_email": "user@example.com",
"call_type": "outbound",
"call_service": "exotel",
"platform_number": "08044319240",
"usage": {
"llm_input_audio_tokens": 12450,
"llm_input_text_tokens": 850,
"llm_output_audio_tokens": 0,
"llm_output_text_tokens": 1230,
"llm_total_tokens": 14530,
"tts_characters_count": 485,
"tts_audio_duration": 32.5
}
}
}
Webhook Payload Schema
| Field | Type | Description |
|---|---|---|
success |
boolean | Always true for webhook notifications. |
message |
string | Status message. |
data |
object | Complete call details. |
data.room_name |
string | The LiveKit room name. |
data.assistant_id |
string | ID of the assistant used. |
data.assistant_name |
string | Name of the assistant. |
data.to_number |
string | Phone number that was called. |
data.call_status |
string | Call lifecycle status (initiated, answered, completed) or terminal SIP outcome (busy, no_answer, rejected, cancelled, unreachable, timeout, failed). |
data.call_status_reason |
string | Optional detailed reason for non-success outcomes. |
data.sip_status_code |
number | SIP response code when available for SIP-driven setup outcomes. May be null for generic failures/timeouts. |
data.sip_status_text |
string | SIP reason text when available for SIP-driven setup outcomes. May be null for generic failures/timeouts. |
data.answered_at |
string | Timestamp when the user answered (if answered). |
data.recording_path |
string | S3 URL of the call recording (if enabled). |
data.transcripts |
array | List of conversation messages. |
data.transcripts[].speaker |
string | Who spoke (agent or user). |
data.transcripts[].text |
string | The transcribed text. |
data.transcripts[].timestamp |
string | ISO 8601 timestamp. |
data.started_at |
string | Call start time (ISO 8601). |
data.ended_at |
string | Call end time (ISO 8601). |
data.call_duration_minutes |
number | Total call duration in minutes. |
data.created_by_email |
string | Email of the user who owns this call. |
data.call_type |
string | Call direction: outbound, inbound, or web. |
data.call_service |
string | Telephony provider: exotel, twilio, or web. |
data.platform_number |
string | Platform's own phone number used for this call. |
data.usage |
object | Token and TTS usage metrics (if available). |
data.usage.llm_input_audio_tokens |
number | OpenAI Realtime audio input tokens. |
data.usage.llm_input_text_tokens |
number | OpenAI Realtime text input tokens. |
data.usage.llm_output_audio_tokens |
number | OpenAI Realtime audio output tokens. |
data.usage.llm_output_text_tokens |
number | OpenAI Realtime text output tokens. |
data.usage.llm_total_tokens |
number | Total LLM tokens for this call. |
data.usage.tts_characters_count |
number | Characters sent to TTS provider. |
data.usage.tts_audio_duration |
number | TTS audio duration in seconds. |
Call Status Glossary (Authoritative)
Lifecycle statuses:
initiated: Call record created and setup started.answered: Bridge signaled call answer and media path became ready.completed: Call ended after an answered/active session.
Terminal setup outcomes:
busy: Callee line is busy.no_answer: Callee did not answer in time.rejected: Callee/provider explicitly rejected the call.cancelled: Setup was cancelled or terminated before answer.unreachable: Destination number is unreachable/invalid for routing.timeout: Setup timed out before completion.failed: Generic setup/runtime failure not represented by the above outcomes.
Current Runtime Payload Shape
The webhook payload is generated from the stored call record and currently includes:
room_nameassistant_idassistant_nameto_numbercall_statuscall_status_reasonsip_status_codesip_status_textanswered_atrecording_pathtranscriptsstarted_atended_atcall_duration_minutescreated_by_emailcall_typecall_serviceplatform_numberusage(object with LLM token counts and TTS usage, included when available)
Quick Test with curl
curl -X POST "https://your-webhook-url" \
-H "Content-Type: application/json" \
-d '{
"success": true,
"message": "Call details fetched successfully",
"data": {
"room_name": "550e8400-e29b-41d4-a716-446655440000_abc123",
"assistant_id": "550e8400-e29b-41d4-a716-446655440000",
"assistant_name": "Support Agent",
"to_number": "+15550200000",
"recording_path": "https://your-bucket.s3.us-east-1.amazonaws.com/recordings/call_abc123.ogg",
"transcripts": [
{
"speaker": "agent",
"text": "Hello John, how can I help you today?",
"timestamp": "2024-01-15T10:00:01.000Z"
},
{
"speaker": "user",
"text": "I need help with my order.",
"timestamp": "2024-01-15T10:00:05.000Z"
}
],
"started_at": "2024-01-15T10:00:00.000Z",
"ended_at": "2024-01-15T10:05:30.000Z",
"call_duration_minutes": 5.5,
"call_type": "outbound",
"call_service": "exotel",
"platform_number": "08044319240"
}
}'
Webhook Response
Your webhook endpoint should return a 200 OK response. The response body is not processed.
Important
- Webhooks are sent when call status becomes terminal (for example
completed,busy,no_answer,failed) - Current runtime sends a single webhook request with a 10s timeout
- Current runtime treats non-2xx HTTP status as failed delivery in runtime logging
- Current runtime does not parse webhook response body
recording_pathcan be empty/null when recording fails after runtime retries- Empty
recording_pathdoes not block terminal webhook delivery - Ensure your webhook endpoint responds quickly (< 10 seconds)
- Store the
room_nameto correlate with call initiation
Exotel Terminal Mapping Notes
- Exotel outbound setup can complete asynchronously after the initial
202 AcceptedAPI response. - If SIP returns
200 OKbut no RTP arrives (no_rtp_after_answer), runtime surfaces finalcall_statusasfailed. - Final status is emitted once per call lifecycle in webhook delivery flow.
How to Read Outbound Outcomes
- Outbound API response status (
200/202/4xx/5xx) indicates request validation/acceptance at API time. - For asynchronous Exotel setup, webhook
data.call_statusis authoritative for lifecycle updates and terminal outcomes. - Treat terminal statuses (
completed,busy,no_answer,rejected,cancelled,unreachable,timeout,failed) as final outcomes.
Public Payload vs Internal Tracking
Internal delivery-tracking fields (for example webhook claim/inflight timestamps) are runtime internals and are not part of the public webhook payload contract.