Event Structure and Reference
Event envelope
Every newly generated public webhook uses this envelope:
| Field | Type | Description |
|---|---|---|
id | string | Stable logical webhook event identifier. Retries and manual resend reuse it. |
type | string | Event name, such as call.completed. Event-specific fields are in data. |
created_at | string | UTC RFC 3339 / ISO 8601 time with millisecond precision. T separates date and time; Z means UTC. |
sequence | integer | Per-call event-generation sequence, starting at 0 for newly sequenced calls. |
data | object | Common call information plus optional metadata and event-specific fields. |
Use id for deduplication. Use sequence when you need to reconstruct event order for one call. Retries and manual resend do not change either field, but network delivery order may differ from sequence order. Historical events created before sequencing was finalized can show no sequence in the Inspector.
Common sequence patterns
The following successful-call examples show durable event-generation order. HTTP delivery order can differ, so use sequence rather than arrival order.
| Pattern | Event sequence |
|---|---|
| Standard ringing | 0 call.created → 1 call.ringing → 2 call.answered → 3 call.completed |
| Early media | 0 call.created → 1 call.early_media → 2 call.answered → 3 call.completed |
| Both progress signals | 0 call.created → 1 call.ringing → 2 call.early_media → 3 call.answered → 4 call.completed |
Ringing and early-media events are independent and optional. A call can emit either one, both, or neither depending on carrier and network signaling.
Call object
data.call is a snapshot of the call when the event is generated.
| Field | Description |
|---|---|
id | Primary MiniVoice call resource ID. Use it with MiniVoice call APIs. |
application_id | Application associated with the call. |
direction | Call direction, such as inbound or outbound. |
from | Source address when available. |
to | Destination address when available. |
status | Call status when the event was generated. |
carrier_call_id | Optional upstream telephony correlation identifier for troubleshooting and tracing. |
Fields can be omitted when unavailable or not relevant.
MiniVoice call ID and carrier call ID
data.call.id is the primary MiniVoice resource identifier. Use it when calling MiniVoice APIs and when relating webhooks, recordings, and call records.
data.call.carrier_call_id is optional. Its source and format can vary with routing or carrier, and it is not a MiniVoice resource ID. Do not build API requests around it.
Metadata
When present, data.metadata contains supported customer-supplied call metadata preserved through event generation and delivery retries.
"metadata": {
"order_id": "42"
}
Raw telephony channel variables are not part of this contract. MiniVoice also excludes its internal recording-control flag.
Completion fields
| Field | Description |
|---|---|
reason | Implemented terminal reason when available. |
duration_seconds | Total MiniVoice call duration. |
billable_seconds | Connected/billable media duration according to the current implementation. |
hangup.cause | Normalized telephony hangup cause. |
hangup.source | Party or system that initiated termination when MiniVoice can determine it. The current fallback unknown means the initiator could not be determined reliably. |
hangup.sip_code | Numeric final or relevant SIP response code when available, for example 200 or 486. |
Do not use the historical string field sip_status; current events expose numeric hangup.sip_code.
Public event types
The backend currently supports these public event names:
- Call lifecycle:
call.created,call.ringing,call.early_media,call.answered,call.failed,call.canceled,call.completed - Gather:
call.gathered - Recording:
recording.started,recording.completed,recording.failed - API action execution:
call.actions_refreshed,call.actions_executed
MiniVoice does not currently publish transcription, AI-processing, transfer, bridge, or conference completion webhooks.
Lifecycle summary
| Event | Meaning | Terminal |
|---|---|---|
call.created | Call resource created and processing begun | No |
call.ringing | Authoritative remote ringing progress observed | No |
call.early_media | Pre-answer media observed | No |
call.answered | Call answered | No |
call.failed | Call establishment failed | Yes |
call.canceled | Pre-answer call explicitly canceled | Yes |
call.completed | Answered call ended | Yes |
┌─→ call.failed
│
call.created ───────────┼─→ call.canceled
│ │
├─ call.ringing │
├─ call.early_media
│ │
└──────────────→ call.answered
│
└─→ call.completed
The progress branches are optional; the diagram does not require them before answer or a terminal outcome.
call.created
Sent when MiniVoice accepts and creates the call resource and queues or begins processing the call. It does not mean the destination is ringing or that dialing progress has been confirmed. A retry-created call can also include retry_of_call_id in data.
{
"id": "evt_20260901T170001.000000000_2c64980100e4a115",
"type": "call.created",
"created_at": "2026-09-01T17:00:01.000Z",
"sequence": 0,
"data": {
"call": {
"id": "call_20260901T170000.500000000",
"application_id": "app_20260321T130033.289233019",
"direction": "outbound",
"from": "+37045509630",
"to": "+37067613663",
"status": "queued"
},
"metadata": {
"order_id": "42"
}
}
}
call.ringing
Sent when MiniVoice observes the remote side enter an authoritative ringing state before answer. This normally corresponds to SIP 180 Ringing without early media. It is non-terminal, does not mean the call was answered, and does not start answered-call billing. Some carrier and call paths do not produce this event.
{
"id": "evt_20260907T191001.500000000_2ec638caf5a3bf91",
"type": "call.ringing",
"created_at": "2026-09-07T19:10:01.500Z",
"sequence": 1,
"data": {
"call": {
"id": "call_20260907T191000.500000000",
"application_id": "app_20260321T130033.289233019",
"direction": "outbound",
"from": "+37045509630",
"to": "+37067613663",
"status": "ringing",
"carrier_call_id": "upstream-correlation-id"
},
"sip_code": 180
}
}
sip_code is optional. It may contain 180 only when MiniVoice receives an authoritative matching signaling value; it is omitted when the underlying event does not expose a verified numeric value.
call.early_media
Sent when media is received before the call has been answered. Early media can be network-provided ringback, a carrier announcement, an intercept message, or an early IVR. It is distinct from remote ringing and from answer, is non-terminal, and does not start answered-call billing.
{
"id": "evt_20260907T191002.123000000_9f892497a346bb18",
"type": "call.early_media",
"created_at": "2026-09-07T19:10:02.123Z",
"sequence": 1,
"data": {
"call": {
"id": "call_20260907T191000.500000000",
"application_id": "app_20260321T130033.289233019",
"direction": "outbound",
"from": "+37045509630",
"to": "+37067613663",
"status": "in_progress",
"carrier_call_id": "upstream-correlation-id"
},
"sip_code": 183
}
}
sip_code is optional. It may contain 183 only when authoritative signaling exposes a matching verified numeric value.
Ringing, early media, and answer
| Event | Meaning |
|---|---|
call.ringing | Remote ringing progress was observed |
call.early_media | Pre-answer media was observed |
call.answered | The call was actually answered |
call.ringing ≠ call.early_media ≠ call.answered. Neither progress event means the call is connected. call.answered remains the authoritative answer transition.
Implementation note
MiniVoice maps FreeSWITCH CHANNEL_PROGRESS to call.ringing and CHANNEL_PROGRESS_MEDIA to call.early_media. Integrations should depend on the public meanings above, not the internal event names.
call.answered
Sent when the call is answered.
{
"id": "evt_20260901T170008.250000000_b11344999aa18961",
"type": "call.answered",
"created_at": "2026-09-01T17:00:08.250Z",
"sequence": 2,
"data": {
"call": {
"id": "call_20260901T170000.500000000",
"application_id": "app_20260321T130033.289233019",
"direction": "outbound",
"from": "+37045509630",
"to": "+37067613663",
"status": "in_progress",
"carrier_call_id": "eb3ca595-8c80-4590-8997-310682ce0a5f"
}
}
}
call.completed
Sent after a call reaches the completed state.
{
"id": "evt_20260901T173552.838849267_47feba5c0ce326f4",
"type": "call.completed",
"created_at": "2026-09-01T17:35:52.838Z",
"sequence": 2,
"data": {
"call": {
"id": "call_20260901T173513.576013050",
"application_id": "app_20260321T130033.289233019",
"direction": "outbound",
"from": "+37045509630",
"to": "+37067613663",
"status": "completed",
"carrier_call_id": "eb3ca595-8c80-4590-8997-310682ce0a5f"
},
"reason": "completed",
"duration_seconds": 12,
"billable_seconds": 5,
"hangup": {
"cause": "NORMAL_CLEARING",
"source": "unknown",
"sip_code": 200
}
}
}
call.failed
Sent after a call fails. Some early failures contain only reason; terminal signaling failures can also contain durations and hangup.
{
"id": "evt_20260901T174100.120000000_88fbb9e2f10c207d",
"type": "call.failed",
"created_at": "2026-09-01T17:41:00.120Z",
"sequence": 2,
"data": {
"call": {
"id": "call_20260901T174050.000000000",
"application_id": "app_20260321T130033.289233019",
"direction": "outbound",
"from": "+37045509630",
"to": "+37067613663",
"status": "failed",
"carrier_call_id": "9d0ead14-9636-445c-9ab4-c5ae25df4329"
},
"reason": "busy",
"duration_seconds": 10,
"billable_seconds": 0,
"hangup": {
"cause": "USER_BUSY",
"source": "unknown",
"sip_code": 486
}
}
}
call.canceled
Sent when a pre-answer outbound call is explicitly canceled through POST /v1/calls/:id/cancel. Its canonical reason is api_canceled.
{
"id": "evt_20260907T191100.123000000_b53fb606790bc10a",
"type": "call.canceled",
"created_at": "2026-09-07T19:11:00.123Z",
"sequence": 2,
"data": {
"call": {
"id": "call_20260907T191000.500000000",
"application_id": "app_20260321T130033.289233019",
"direction": "outbound",
"from": "+37045509630",
"to": "+37067613663",
"status": "canceled",
"carrier_call_id": "upstream-correlation-id"
},
"reason": "api_canceled"
}
}
carrier_call_id can be omitted when cancellation occurs before an upstream correlation ID exists. Representative paths include call.created → call.canceled, call.created → call.ringing → call.canceled, and call.created → call.early_media → call.canceled.
This event is different from call.failed, which reports establishment failures such as busy, rejected, timeout, unreachable, or carrier/provider failure. Do not classify normal establishment failure as cancellation.
If the call has already been answered, using the same call-control endpoint to end it produces call.completed, not call.canceled. In that event, reason is api_hangup and hangup.cause is API_HANGUP.
call.gathered
Sent when a gather action completes or times out.
{
"id": "evt_20260901T170020.000000000_d3b46c59842524cd",
"type": "call.gathered",
"created_at": "2026-09-01T17:00:20.000Z",
"sequence": 3,
"data": {
"call": {
"id": "call_20260901T170000.500000000",
"application_id": "app_20260321T130033.289233019",
"direction": "inbound",
"from": "+37045509630",
"to": "+37067613663",
"status": "in_progress"
},
"step": 0,
"digits": "1",
"status": "completed",
"timed_out": false,
"min_digits": 1,
"max_digits": 1,
"timeout_seconds": 5,
"finish_on_key": "#"
}
}
digits contains the implemented DTMF input. A timeout uses status: "timeout" and timed_out: true.
Recording events
recording.started, recording.completed, and recording.failed contain a nested data.recording object.
{
"id": "evt_20260901T170100.000000000_d16fe09e7d91240a",
"type": "recording.completed",
"created_at": "2026-09-01T17:01:00.000Z",
"sequence": 4,
"data": {
"call": {
"id": "call_20260901T170000.500000000",
"application_id": "app_20260321T130033.289233019",
"direction": "inbound",
"from": "+37045509630",
"to": "+37067613663",
"status": "completed",
"carrier_call_id": "eb3ca595-8c80-4590-8997-310682ce0a5f"
},
"recording": {
"id": "rec_call_20260901T170000.500000000",
"recording_type": "call",
"status": "completed",
"duration_seconds": 60,
"size_bytes": 960044,
"failure_reason": ""
}
}
}
The recording object contains only fields produced by the current recording builder: id, recording_type, status, failure_reason, duration_seconds, and size_bytes. It does not include a recording URL.
Action events
call.actions_refreshed is emitted when the public action-refresh endpoint fetches and stores a new action list; data.actions contains that list. call.actions_executed is emitted by the public action-execution endpoint; data.result and data.log contain the execution result. These events describe explicit API action operations and are not substitutes for call lifecycle events.
Historical payload compatibility
The Webhook Inspector preserves and displays the exact body originally sent. Events generated before the canonical schema was finalized can therefore contain older fields such as:
event_id,event_type, orevent- a top-level
call provider_call_idbillsec_secondssip_status
New integrations should implement the canonical id, type, created_at, sequence, and data envelope. Parse historical shapes only when you specifically need compatibility with stored older deliveries.