Skip to content

Events, 2026-10-01 format

Download .md

This is the 2026-10-01 event format, frozen. Endpoints created before release v1.84.0.0 keep receiving it, as does any endpoint created or changed with "api_version": "2026-10-01", and so does the event stream. A new endpoint gets v2, whose fields are in Events. How to receive either: Receive webhooks.

typewhendata
message.receivedsomeone texts or sends a WhatsApp message to one of your numbers (after STOP/START handling, on both channels)message_id, conversation_id, contact_id, channel_id, from, to, body, media[], media_failed, received_at, campaign_id, keyword, channel, quick_reply. channel is sms or whatsapp, absent on events written before WhatsApp messages were announced (absent means sms); quick_reply is { payload } for a WhatsApp button tap, else null, absent on those older events
message.senta text left for the carrier: your AI agent’s reply, a teammate’s reply or an API sendmessage_id, conversation_id, author (ai_agent / human / api), agent_id, to, from, channel_id, body, send_ids[], sent_at (from is the number it went out from, channel_id its channel)
message.status_updateda text’s or a WhatsApp message’s status changed. Campaign messages’ events reach only endpoints that name this type; "*" leaves them outSMS: channel ("sms"), status, send_id, campaign_id, message_id, to, from, state, display_status, segments, billed_credits, error, sent_at, delivered_at, updated_at. WhatsApp: channel ("whatsapp"), status, message_id, conversation_id, campaign_id, to, from, error, template, updated_at
campaign.starteda campaign was started; always the first event of its run (it may still wait for its sending hours)campaign_id, campaign_short_id, name, campaign_type, status, started_at
campaign.pauseda campaign stopped sending, for any reasonthe same five fields, plus reason, message, paused_at, resumes_at
campaign.resumeda paused campaign is sending againthe same five, plus resumed_at, previous_reason
campaign.completeda campaign finishedthe same five, plus completed_at, totals, billed_credits
campaign.canceleda campaign was canceledthe same five, plus canceled_at, in_flight, not_yet_sent
campaign.waitingan SMS campaign reached the end of its sending hours, or started outside them, and waits for them (not a pause)the same five, plus reason, waiting_at, resumes_at, tz
campaign.window_openedan SMS campaign’s sending hours started again and it goes on sendingthe same five, plus opened_at, tz
call.ringingan outbound call started ringing. A campaign’s call events reach only endpoints that name the type; "*" leaves them outcall_id, conversation_id, direction, from, to, agent_id, campaign_id, status, occurred_at
call.answereda call was answered (by a person or a machine)the same nine, plus answered_at
call.completeda call ended; status is completed, voicemail, no_answer or busythe same nine, plus answered_at, outcome, duration_sec, ended_at, disconnect_reason
call.faileda call failed (a system or carrier failure; status is failed)the same as call.completed

Every delivery’s body is an envelope with the payload under data:

{ "id": "evt_9c1f…", "type": "message.status_updated", "workspace_id": "00000000-0000-4000-8000-00000000000f",
"created_at": "2026-10-15T17:02:19.000Z", "seq": 48213,
"data": { "channel": "whatsapp", "status": "read",
"message_id": "00000000-0000-4000-8000-000000000013",
"conversation_id": "00000000-0000-4000-8000-000000000014", "campaign_id": null,
"to": "+6591234567", "from": "+16285550100", "error": null,
"template": { "id": "wt-0000000001", "name": "order_ready", "language": "en_US" },
"updated_at": "2026-10-15T17:02:19.000Z" } }

seq is the event’s position in your workspace’s log, the same number as the id: of its /v1/events frame.

event2026-10-01v2
every event{ id, type, workspace_id, created_at, seq, data }adds object, api_version, livemode
message.receivedchannel_id (UUID); channel and quick_reply missing on old eventschannel always (sms or whatsapp), quick_reply always (null unless a tap), channel_id (ch-…), channel_uuid, sender_id, campaign_short_id. message_id is still the thread message’s id: GET /v1/messages/{id} does not read inbound texts
message.sentmessage_id (thread message), send_ids[], agent_id (UUID), channel_id (UUID)message_id = the first part’s /v1 message id, message_ids[] = every part in order, channel: "sms", agent_id (ag-…) + agent_uuid, channel_id (ch-…) + channel_uuid, sender_id, metadata. send_ids and the thread id are gone
message.status_updatedSMS: send_id, thread message_id, state, display_status, error.meaning; channel/status missing on old events. WhatsApp: no segments, timesone shape for both channels: message_id (an SMS part’s id, or the WhatsApp message’s), channel always, status, from, to, conversation_id, campaign_id + campaign_short_id, sender_id, segments, billed_credits, error { code, category, message, action }, template, sent_at, delivered_at, read_at, updated_at, metadata
campaign.*campaign_type, status: "cancelled"channel (sms, whatsapp, voice), status: "canceled", metadata
campaign.completedtotals { recipients, sent, delivered, not_delivered, failed, unconfirmed, skipped, opted_out, replied }counts { recipients, pending, sent, delivered, failed, skipped, replied, opted_out }, the same buckets and nulls as GET /v1/campaigns/{id} (replied and opted_out are null for voice, opted_out for WhatsApp; counted when the campaign completed: a text still being handed to the carrier then counts as failed here, and GET shows its later state)
call.*call_id, agent_id (ag-…)adds call_short_id (cv-…), channel: "voice", agent_uuid, campaign_short_id, metadata
conversation.created, turn.completed, confirmation.*, spend.recorded, run.completed, meta_*, webhook.testdata unchanged; only the envelope

The 2026-10-01 payloads keep the words they were first published with, so code that reads them keeps working. Where they differ from the /v1 objects, map them like this:

in an eventon /v1how
status: "cancelled" (campaign events)status: "canceled"the same state, spelled two ways
campaign_type: "sms"channel: "sms"
campaign_type: "whatsapp_template"channel: "whatsapp"
campaign_type: "voice_ai" (or voicemail_drop)channel: "voice"
campaign_id (UUID)a campaign’s idthe same UUID; campaign_short_id is its short_id (ca-…)
agent_id (UUID, on message.sent; call events already carry the ag-…)an agent’s id (ag-…)GET /v1/agents lists each agent’s uuid
channel_id (UUID)a senderGET /v1/senders lists each number’s channel_uuids
send_id (SMS), message_id (WhatsApp)a message’s idthe same id; read it with GET /v1/messages/{id}
conversation_idconversation_idthe same UUID
display_status (SMS status events)a message’s or a campaign recipient’s statusnot_yet_sent → queued, sending → sending, sent_awaiting → sent, delivered → delivered, not_delivered → undelivered, failed → failed, unconfirmed → unconfirmed, skipped → skipped. Events also carry status, already in /v1 words

A campaign recipient’s status is the word its message or call has on /v1 (see Recipients), so an event’s status and a recipient row agree.

  • message.sent’s send_ids lists every send that carried the message, parts that were skipped or unconfirmed included: read each send’s status for the truth.
  • SMS status events carry the engine’s state and display_status, and error is { code, category, meaning, action }. Events written before channel and status were added do not have them: treat a missing channel as sms.
  • A cancel is spelled cancelled in status, and campaign.completed carries totals rather than counts.