{
"id": "whd_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
"type": "call.ended",
"created_at": "2026-10-12T05:31:02.417Z",
"data": {
"call": {
"id": "call_7e9a1c3b5d2f4a6c8e0b1d3f5a7c9e1b",
"agent_id": "agt_credfin_dpd_8_30_m",
"call_type": "ai_voice",
"to": "+9198XXXXXX02",
"status": "queued",
"scheduled_for": "2026-10-12T07:31:02.417Z",
"attempts_count": 1,
"outcome": null,
"summary": null,
"duration_sec": null,
"billable_minutes": 0,
"cost": {
"amount_micros": 0,
"currency": "INR",
"status": "pending"
},
"has_recording": false,
"has_transcript": false,
"metadata": {
"loan_id": "CF-LN-311907"
},
"created_at": "2026-10-12T05:30:00.511Z",
"updated_at": "2026-10-12T05:31:02.417Z",
"ended_at": null
},
"attempt": {
"id": "att_3c5e7a9b1d4f4e6a8b0c2d4e6f8a0b2c",
"attempt_number": 1,
"call_type": "ai_voice",
"status": "no_answer",
"failure_reason": null,
"ended_by": null,
"duration_sec": null,
"billable_minutes": 0,
"started_at": null,
"ended_at": "2026-10-12T05:31:01.990Z",
"outcome": null,
"cost": {
"amount_micros": 0,
"currency": "INR",
"status": "final"
}
},
"will_retry": true
}
}Call ended
Sent when a dial attempt of an AI voice call or a human call ends, whatever the result. For AI calls
will_retry: true means another attempt is queued (the call is back to queued with scheduled_for); the
last attempt has will_retry: false. Human calls never retry. Reachability dials do not send call.ended
(see reachability.result). For a human call the terminal call.status_changed is sent first and
call.ended follows 2 seconds later. data.attempt is null only if the attempt record is missing
(should not happen).
{
"id": "whd_0a1b2c3d4e5f40718293a4b5c6d7e8f9",
"type": "call.ended",
"created_at": "2026-10-12T05:31:02.417Z",
"data": {
"call": {
"id": "call_7e9a1c3b5d2f4a6c8e0b1d3f5a7c9e1b",
"agent_id": "agt_credfin_dpd_8_30_m",
"call_type": "ai_voice",
"to": "+9198XXXXXX02",
"status": "queued",
"scheduled_for": "2026-10-12T07:31:02.417Z",
"attempts_count": 1,
"outcome": null,
"summary": null,
"duration_sec": null,
"billable_minutes": 0,
"cost": {
"amount_micros": 0,
"currency": "INR",
"status": "pending"
},
"has_recording": false,
"has_transcript": false,
"metadata": {
"loan_id": "CF-LN-311907"
},
"created_at": "2026-10-12T05:30:00.511Z",
"updated_at": "2026-10-12T05:31:02.417Z",
"ended_at": null
},
"attempt": {
"id": "att_3c5e7a9b1d4f4e6a8b0c2d4e6f8a0b2c",
"attempt_number": 1,
"call_type": "ai_voice",
"status": "no_answer",
"failure_reason": null,
"ended_by": null,
"duration_sec": null,
"billable_minutes": 0,
"started_at": null,
"ended_at": "2026-10-12T05:31:01.990Z",
"outcome": null,
"cost": {
"amount_micros": 0,
"currency": "INR",
"status": "final"
}
},
"will_retry": true
}
}Headers
The event type, same as the body's type.
call.ended, call.analyzed, call.status_changed, reachability.result, reachability.completed, campaign.started, campaign.paused, campaign.resumed, campaign.completed, campaign.canceled, campaign.record.updated, call.dispositioned, dialer.contact_updated Delivery id (whd_...), same as the body's id. Stays the same across retries of one delivery: use it to
drop duplicates.
t=<unix seconds>,v1=<hex HMAC-SHA256(secret, t + "." + raw body)>. t is the time of this delivery try.
Verify it before trusting the body (see the Webhooks tag and the event descriptions).
^t=\d+,v1=[0-9a-f]{64}$Always voice-gateway-webhooks/1.
"voice-gateway-webhooks/1"Body
Response
Any 2xx status, within 10 seconds, acknowledges the delivery (the body is ignored). Anything else (other statuses, timeouts, connection errors) is retried: 8 tries in total, 30 seconds after the first failure, then doubling (30 s, 1 min, 2 min, 4 min, 8 min, 16 min, 32 min; about 63 minutes in all). Redirects are not a supported answer.