Skip to main content

Webhook Events Reference

Payload shape for every event the platform emits. The names and samples on this page mirror what the dispatcher actually sends — generated from WEBHOOK_EVENT_TYPES in packages/shared/src/constants.ts and the dispatchTenantWebhook / buildWebhookJobs call sites. For the full catalog with one-liner descriptions, see Webhook Events.

Event Envelope

Every webhook delivery wraps the event in a standard envelope:
When truncated: true, data carries _truncated: true, _original_bytes, and a small set of identifier fields (id, message_id, conversation_id, contact_id, campaign_id, channel) so consumers can locate the resource.

Catalog Index

Every webhook event Orbit emits — 185 in total — grouped by surface. Each row links to its long-form payload section below; combined headings (e.g. campaign create / update / delete share one section) point to the first member.

Message Events

Contact Events

Campaign Events

Voice Events

Agent Events

Flow Events

Verification Events

Number / Porting Events

Conversation Events

List / Segment Events

Webhook Endpoint Events

Billing Events

Channel Failover Events

Push Events

Number Masking (Proxy) Events

WhatsApp Events

Supervisor Events

SIP Trunk Events

CDP Events

Other


Message Events

message.created

Fired when an outbound message is accepted and queued for delivery.

message.sent

Fired when the message is handed off to the carrier / provider.

message.delivered

message.failed

Terminal failure reported by the carrier (or a synthesised failure covering undelivered / rejected).

message.read

message.received

Inbound message from a recipient.

Contact Events

contact.created

contact.updated

contact.deleted

contact.merged

contact.bulk_imported

Fired once per bulk import batch (CSV upload or POST /v1/contacts/bulk), not once per row. The payload summarises the whole batch.
Delivery semantics: at-least-once. This event rides the standard webhook delivery queue (BullMQ with attempts: 10 and exponential backoff starting at 30s — see apps/api/src/lib/webhook-queue.ts WEBHOOK_DELIVERY_DEFAULT_JOB_OPTIONS). Transient receiver errors (5xx, timeouts) trigger automatic retries, so the same contact.bulk_imported payload can be delivered more than once. Orbit does not guarantee exactly-once delivery on any webhook.Your consumer must be idempotent. Recommended dedupe key, in order of preference:
  1. Envelope id — the top-level event id is unique per delivery attempt’s logical event and is the canonical idempotency key for every Orbit webhook (id is stable across retries of the same event). Persist seen id values for at least 7 days (the longest the queue can hold a retry window: 10 attempts × 30s exponential ≈ 8.5h plus the dead-letter replay window).
  2. data.import_batch_id — when non-null, import_batch_id is the natural key for the underlying batch. If you write summary counters into your own DB, key them on import_batch_id so a replay does not double-add the totals.
If neither key is available (import_batch_id: null and a malformed envelope), treat the delivery as a no-op observability signal and fetch authoritative state via GET /v1/contacts/import-jobs.

contact.opted_in / contact.opted_out

Fired when a recipient sends a START / STOP keyword on a registered channel.

contact.recertification_due

FCC RMD annual consent recertification window opened on a contact.

contact.segment_changed

Predictive scorer flipped a contact’s segment label (e.g. champion → at_risk, passive → engaged). Useful for triggering re-engagement plays from a CRM.

Campaign Events

campaign.created / campaign.updated / campaign.deleted

campaign.started

campaign.completed

campaign.paused / campaign.resumed

campaign.drip_step.completed

Single drip step finished sending to its slice.

campaign.drip.completed

Entire drip campaign finished its full schedule.

Voice Events

call.initiated

When the call is bridged through Jambonz, the payload also carries provider and direction context:

call.answered

call.completed

call.failed

call.transferred

call.dtmf_sent

Mid-call DTMF inject through the API.

call.recording.paused / call.recording.resumed

SIPREC fork toggled mid-call.

call.recording.ready

call.transcription.ready

call.synthesis.ready (legacy inline pipeline)

call.synthesized (post-call synthesis pipeline)

Richer payload from the async synthesis scheduler. Gated on tenant opt-in.

call.ringing

Fires when an inbound call is detected by the SIP edge but before it is answered. Useful for “live caller is dialing in” UI updates without waiting for the post-answer call.answered event.
Trigger condition: carrier sends INVITE to the SIP edge. Retry behaviour: standard webhook retry (exponential 1m → 5m → 15m → 1h → 6h, 5 attempts max). Idempotent — multiple ring callbacks during a single call can fire if the call is being routed via multiple ring-group legs. Note (current implementation): ringing is currently bundled with the call.initiated event for inbound calls. The standalone call.ringing event is emitted by the underlying carrier webhook but only forwarded to tenant subscribers when the org explicitly subscribes. If call.ringing is missing from your delivery, fall back to listening for call.initiated with status: "ringing".

call.early_media

Fires when the carrier begins streaming pre-answer audio — a SIP 180 Ringing response carrying SDP. This is the moment ringback tone, IVR prompts, voicemail greetings, or answering-machine-detection (AMD) audio start playing into the bridge BEFORE the destination has formally answered (200 OK). Useful for IVR routing decisions, AMD branching, and accurate “Ringing…” → “Audio streaming” UX transitions on softphone clients.
Trigger condition: carrier sends a 180 Ringing with an application/sdp body, OR a 183 Session Progress with SDP. Distinct from call.ringing (which fires on 180 Ringing WITHOUT SDP, i.e. headers-only ringback). Retry behaviour: standard webhook retry (exponential 1m → 5m → 15m → 1h → 6h, 5 attempts max). The event is fire-and-forget — a subsequent call.answered event always follows on the same call_id once the destination picks up. Note (current implementation): the event is published both on the tenant SSE channel (events:tenant_<id>) for live dashboard updates AND via signed webhooks for subscribers who opt in. The SSE channel is what powers the browser softphone’s “Audio streaming” label and the <audio> element’s autoplay-unblock call; the webhook is the durable delivery path for backend systems.

Conference Events

Conference lifecycle events fire from the conference service in apps/api/src/routes/voice/. The events ride the same delivery + retry semantics as call events.

conference.created

Fires when POST /api/v1/voice/conferences succeeds.

conference.participant_joined

Fires when a new participant successfully joins (after 200 OK to the SIP INVITE for that leg).

conference.participant_left

Fires when a participant disconnects — either via DELETE API call, BYE from the carrier, or the conference moderator’s end-conference action.
reason values: normal_disconnect (BYE from participant), kicked (DELETE via API), conference_ended (moderator ended), network_failure (RTP timeout).

conference.ended

Fires when the conference’s active participant count drops to zero, or moderator hits end-conference. Always the LAST event for a conference id.
ended_reason values: moderator_ended, last_participant_left, max_duration_reached.

Recording Events

recording.started

Fires when POST /api/v1/voice/calls/{id}/recording/start (or the per-call record:true flag) starts a fresh recording.

recording.completed

Alias for call.recording.ready (same payload + same recording_url). Documented separately because the call-id may not be available for conference recordings — those carry conference_id instead.

recording.failed

Fires when an upstream recording attempt fails (storage write failure, codec mismatch, AMD aborted the recording before any audio was captured).

voicemail.received

voicemail.transcript_updated

Fires when an originally-missing voicemail transcript is recovered by the retry pipeline.

recording.retention_deleted

Fires 24h before a retention-driven GCS purge so tenants can mirror the asset to their own archive. Payload carries a signed download URL valid until purge_scheduled_at.

Agent Events

agent.created / agent.updated / agent.deleted / agent.deployed

agent.conversation.started

agent.conversation.ended

agent.handoff.requested

Agent escalated to a human operator.

agent.handoff_occurred

Agent delegated to another specialist agent through the transfer_to_agent tool.

agent.calendar_event.created

Fires from the agent’s create_calendar_event MCP tool.

Flow Events

flow.created / flow.updated / flow.published / flow.deleted

flow.executed

Coarse terminal signal. status is completed, failed, or timeout.

flow.execution.started

flow.execution.completed

flow.execution.failed


Verification Events

The platform uses the verification.* prefix (not verify.*).

verification.sent

When a SIM-swap soft warning is detected, sim_swap_warning: true is included alongside the payload above.

verification.approved

For Silent Verification (SIM-card-network-binding flow), channel is "silent".

verification.failed

When the failure is a SIM-swap block, reason: "sim_swap_detected" plus last_swap_date are included.

Number / Porting Events

number.purchased

reclaimed: true is included when the number was re-claimed from the parking pool.

number.released

number.ported

porting.request.cancelled

Customer aborted an in-flight port.

Conversation Events

conversation.created


List / Segment Events


Webhook Endpoint Events


Billing Events

balance.low

balance.topped_up

credits.purchased (deprecated alias of balance.topped_up)

Same data plus a legacy credits field. Prefer balance.topped_up for new integrations.

Channel Failover Events

channel.failover

Fired when a messaging channel’s primary provider failed and Orbit automatically switched to a backup.

Push Events

push.delivered

Mobile / web SDK acknowledged a push was rendered on-device.

push.opened

User tapped the push and the SDK reported the open.

Number Masking (Proxy) Events

proxy.session.created

proxy.session.closed

proxy.message.forwarded


WhatsApp Events

WhatsApp events mirror Meta WABA webhook fields. Each fires after Orbit ingests the corresponding Meta callback and persists the upstream change. Payloads carry the relevant waba_id / phone_number_id so consumers can correlate to their own Meta tooling.

WhatsApp Calling

The lifecycle events (whatsapp.call.connected, _accepted, _terminated) share the same payload shape; the legacy whatsapp.call.received is also dispatched on every transition for backward-compatible consumers.
whatsapp.call.permission_granted:
whatsapp.call.permission_revoked:

WhatsApp Templates

Sample for whatsapp.template.approved (others share the same shape with the relevant Meta payload echoed under details):
whatsapp.template.rejected includes details.reason with Meta’s rejection text.

WhatsApp Account / Business / Phone

whatsapp.account.update, whatsapp.account.banned, whatsapp.account.restricted, whatsapp.account.alert, whatsapp.account.review_update, whatsapp.account.settings_update, whatsapp.business.status_update, whatsapp.phone.quality_update, whatsapp.phone.name_update, whatsapp.capability.update, whatsapp.security.alert, whatsapp.flow.status_change, whatsapp.quality.changed — all share the shape:

WhatsApp Data Subject Requests

whatsapp.data.delete_request follows the same shape.

WhatsApp v25 fields

whatsapp.automatic_events, whatsapp.history, whatsapp.partner_solutions, whatsapp.payment_configuration_update, whatsapp.smb.app_state_sync, whatsapp.smb.message_echoes, whatsapp.tracking_events, whatsapp.user.preferences, whatsapp.group.lifecycle_update, whatsapp.group.participants_update, whatsapp.group.settings_update, whatsapp.group.status_update — each carries the original Meta event payload under details:

Subscribing to Events

Register a webhook and specify which events to receive:
Use "events": ["*"] to subscribe to all event types. Subscribing to a name not listed on this page returns a 422.

See also