---
title: "Leg hung up (CDR-style nested structure)"
method: POST
path: "leg.disconnected"
---

# Leg hung up (CDR-style nested structure)

`POST leg.disconnected` (webhook)

## Payload

- object: Event envelope delivered via HTTP POST to registered webhook URLs. Event-specific fields are flattened into the top-level object (no "data" wrapper). Includes X-Signature-256 header when a secret is configured, and an X-Event-Id header equal to the event_id field.
  - `type` 'leg.ringing' | 'leg.early_media' | 'leg.connected' | 'leg.disconnected' | 'leg.joined_room' | 'leg.left_room' | 'leg.muted' | 'leg.unmuted' | 'leg.deaf' | 'leg.undeaf' | 'leg.hold' | 'leg.unhold' | 'leg.command_failed' | 'leg.ice_interrupted' | 'leg.ice_restored' | 'leg.stream_added' | 'leg.stream_removed' | 'leg.stream_rejected' | 'leg.stream_failed' | 'leg.stream_room_changed' | 'leg.stream_role_changed' | 'siprec.session_started' | 'siprec.session_ended' | 'siprec.metadata_updated' | 'siprec.participant_joined' | 'siprec.participant_left' | 'dtmf.received' | 'rtt.received' | 'speaking.started' | 'speaking.stopped' | 'playback.started' | 'playback.finished' | 'playback.error' | 'tts.started' | 'tts.finished' | 'tts.error' | 'tts.staged' | 'tts.discarded' | 'recording.started' | 'recording.finished' | 'recording.paused' | 'recording.resumed' | 'leg.transfer_initiated' | 'leg.transfer_requested' | 'leg.transfer_progress' | 'leg.transfer_completed' | 'leg.transfer_failed' | 'room.created' | 'room.deleted' | 'room.bridged' | 'room.bridge_updated' | 'room.unbridged' | 'room.routing_changed' | 'leg.role_changed' | 'stt.text' | 'stt.turn' | 'agent.connected' | 'agent.disconnected' | 'agent.user_transcript' | 'agent.agent_response' | 'amd.result' | 'amd.beep' | 'sip.registration_attempt' | 'sip.registration_active' | 'sip.registration_expired' | 'sip.outbound_registration_active' | 'sip.outbound_registration_failed' | 'sip.outbound_registration_expired' | 'sip.trunk_up' | 'sip.trunk_down', required
  - `timestamp` string, date-time, required
  - `event_id` string, uuid: Stable per-event idempotency key; identical across delivery retries and across all subscribers of the event.
  - `instance_id` string: Instance identifier
  - `custom_data` unknown
  - `leg_id` string: Leg identifier
  - `app_id` string
  - `cdr` object
    - `reason` string, required: Disconnect reason. Common SIP failures are mapped to named reasons; unmapped 4xx/5xx/6xx codes appear as sip_{code}. This is an open set — treat an unrecognized value as a new reason rather than an error. Known values: api_hangup, room_deleted, remote_bye, caller_cancel, max_duration, session_expired, rtp_timeout, busy, declined, rejected, unavailable, not_found, forbidden, server_error, ring_timeout, unauthorized, timeout, cancelled, not_acceptable, service_unavailable, invite_failed, connect_failed, challenged, transfer_completed, transfer_originate_failed, transfer_connect_failed, bad_answer, siprec_answer_failed, mixer_panic, hangup, peer_slow, connection_reset, ws_error, ws_dial_failed, moq_error, ice_failure, ice_failed, ice_disconnected, peer_closed, dtls_failed, livekit_client_initiated, livekit_duplicate_identity, livekit_server_shutdown, livekit_kicked, livekit_room_deleted, livekit_state_mismatch, livekit_join_failure, livekit_migration, livekit_signal_close, livekit_room_closed, livekit_user_unavailable, livekit_user_rejected, livekit_token_expired, livekit_media_failure, livekit_disconnected, livekit_client_closed, livekit_signal_closed, livekit_signal_error, livekit_pc_setup_failed, livekit_add_track_failed, livekit_publisher_failed, livekit_subscriber_failed, livekit_signal_loop_exit, livekit_set_remote_desc_failed, livekit_create_answer_failed, livekit_set_local_desc_failed, livekit_signal_send_failed, livekit_set_publisher_remote_failed, livekit_participant_left.
    - `duration_total` number, required: Seconds from leg creation to disconnect
    - `duration_answered` number, required: Seconds from answer to disconnect (0 if never answered)
  - `quality` object, nullable: RTP quality metrics. Omitted for WebRTC legs or unanswered legs with no media.
    - `mos_score` number, required: Mean Opinion Score (1.0–5.0) estimated via simplified E-model (ITU-T G.107) from packet loss and jitter
    - `rtp_packets_received` integer, required: Total inbound RTP audio packets received
    - `rtp_packets_lost` integer, required: Estimated lost packets based on sequence number gaps
    - `rtp_jitter_ms` number, required: Inter-arrival jitter in milliseconds (RFC 3550 §A.8)

---

[API](https://skmtc.dev/voiceblender/apis/voiceblender-api.md) - [All operations](https://skmtc.dev/voiceblender/apis/voiceblender-api/llms.txt) - [OpenAPI document](https://skmtc.dev/voiceblender/apis/voiceblender-api/revisions/fa6da4e6a00b?raw)
