Skip to content

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_name
  • assistant_id
  • assistant_name
  • to_number
  • call_status
  • call_status_reason
  • sip_status_code
  • sip_status_text
  • answered_at
  • recording_path
  • transcripts
  • started_at
  • ended_at
  • call_duration_minutes
  • created_by_email
  • call_type
  • call_service
  • platform_number
  • usage (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.

HTTP/1.1 200 OK
Content-Type: application/json

{"received": true}

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_path can be empty/null when recording fails after runtime retries
  • Empty recording_path does not block terminal webhook delivery
  • Ensure your webhook endpoint responds quickly (< 10 seconds)
  • Store the room_name to correlate with call initiation

Exotel Terminal Mapping Notes

  • Exotel outbound setup can complete asynchronously after the initial 202 Accepted API response.
  • If SIP returns 200 OK but no RTP arrives (no_rtp_after_answer), runtime surfaces final call_status as failed.
  • 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_status is 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.