Webhooks are configured per sending list in the dashboard under your list’s Delivery Forwarding settings. Each webhook subscribes to one or more event types, and you can send a test event from the dashboard to verify your endpoint.
Event Types
Request Format
Events are batched: each POST contains anevents array with one or more events that occurred in the same processing cycle. Cycles run every few seconds, so events from the same short window arrive together in one POST (see Delivery Semantics). Each event has a type, an ISO-8601 timestamp, and a data object whose fields depend on the event type.
Delivery Event
Sent when a delivery status update is received for a message you sent.Not every route currently emits
delivery events. Lists on routes without delivery-event support emit reply and unsubscribe events only.Delivery Statuses
delivered is the success terminal state. The exact vocabulary varies by carrier route — some routes pass the carrier’s own status through verbatim, so you may also see intermediate values (e.g. sent, accepted). Treat any value other than delivered as non-success, and any value you don’t recognize as non-terminal.
Reply Event
Sent when a contact sends an inbound SMS to your list number. Every inbound message emits areply event — including opt-out keywords. When a message is an opt-out, an unsubscribe event is emitted in addition to the reply, so a contact who texts STOP produces both a reply and an unsubscribe (typically in the same batch). To act on opt-outs, key off the unsubscribe event — do not assume a reply means the contact stayed subscribed. Replies are also stored and viewable in the dashboard under Messages > Replies. Like every event, it arrives inside the events array (see Request Format).
message_id is an opaque identifier — do not assume a fixed length or format. Store them as variable-length strings.from is your sending list number; to is the contact who replied; text is their message body.
Unsubscribe Event
Sent when a contact is opted out — an opt-out keyword reply, a carrier spam complaint, or repeated hard delivery failures. The opt-out matcher is intentionally broad: it recognizes STOP, STOPALL, UNSUBSCRIBE, QUIT, CANCEL, END, REMOVE, OPTOUT and many common variants and phrasings (e.g. “stop texting”, “take me off the list”, “no more texts”), plus spam-complaint and abuse language — treat these as examples, not an exhaustive list. Like every event, it arrives inside theevents array (see Request Format).
active: false) on the corresponding list. Future sends are blocked automatically. See Compliance for the auto-removal rules.
Re-subscription does not emit a dedicated event. When a previously opted-out contact opts back in (an opt-in keyword such as START, UNSTOP, SUBSCRIBE, OPTIN), they are re-subscribed and sending resumes — but there is no
resubscribe webhook event. The only signal you receive is the ordinary reply event carrying their inbound text (e.g. "Start"). If you mirror subscription state from webhooks, detect opt-ins from the reply body — do not expect a counterpart to the unsubscribe event.Delivery Semantics
- Batching cadence: events are collected and flushed on a short polling cycle — roughly every 5 seconds — so you normally receive an event within seconds of the underlying status change. This is best-effort, not a guaranteed interval: a quiet window produces no POST, and a burst is grouped into a single POST (tens of events).
- Timeout: your endpoint must respond within 10 seconds. Return a 2xx status quickly and process events asynchronously.
- No retries: event delivery is fire-and-forget. A non-2xx response or timeout is logged but not retried.
- At-least-once, and statuses can be revised: carrier receipts can be redelivered upstream, and a later receipt can revise a message’s status (e.g.
deliveredcorrected tofailed).message_ididentifies the original send — not a unique event. To drop true duplicates, dedupe onmessage_id+status(orprovider_message_id+status) and let the latest received status win. - Authentication: if your webhook has a secret configured, every request includes it in the
X-Webhook-Secretheader — compare it against your stored value before trusting the payload.
Next Steps
Webhooks Integration
Configure webhook endpoints
Send Message
Send a message to trigger events