Skip to main content

Event Structure and Reference

Event envelope​

Every newly generated public webhook uses this envelope:

FieldTypeDescription
idstringStable logical webhook event identifier. Retries and manual resend reuse it.
typestringEvent name, such as call.completed. Event-specific fields are in data.
created_atstringUTC RFC 3339 / ISO 8601 time with millisecond precision. T separates date and time; Z means UTC.
sequenceintegerPer-call event-generation sequence, starting at 0 for newly sequenced calls.
dataobjectCommon 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.

PatternEvent sequence
Standard ringing0 call.created → 1 call.ringing → 2 call.answered → 3 call.completed
Early media0 call.created → 1 call.early_media → 2 call.answered → 3 call.completed
Both progress signals0 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.

FieldDescription
idPrimary MiniVoice call resource ID. Use it with MiniVoice call APIs.
application_idApplication associated with the call.
directionCall direction, such as inbound or outbound.
fromSource address when available.
toDestination address when available.
statusCall status when the event was generated.
carrier_call_idOptional 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​

FieldDescription
reasonImplemented terminal reason when available.
duration_secondsTotal MiniVoice call duration.
billable_secondsConnected/billable media duration according to the current implementation.
hangup.causeNormalized telephony hangup cause.
hangup.sourceParty or system that initiated termination when MiniVoice can determine it. The current fallback unknown means the initiator could not be determined reliably.
hangup.sip_codeNumeric 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​

EventMeaningTerminal
call.createdCall resource created and processing begunNo
call.ringingAuthoritative remote ringing progress observedNo
call.early_mediaPre-answer media observedNo
call.answeredCall answeredNo
call.failedCall establishment failedYes
call.canceledPre-answer call explicitly canceledYes
call.completedAnswered call endedYes
┌─→ 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​

EventMeaning
call.ringingRemote ringing progress was observed
call.early_mediaPre-answer media was observed
call.answeredThe 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, or event
  • a top-level call
  • provider_call_id
  • billsec_seconds
  • sip_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.