Events
An event is recorded every time something happens to a message or batch. You can have events sent to you as webhooks, or fetch them from the events feed. v2 events replace the v1 delivery receipt and inbound webhooks.
Event types
Section titled “Event types”| Event | When |
|---|---|
sms.queued | A message was accepted |
sms.sent | A message was handed to the network |
sms.delivered | A message was delivered |
sms.failed | A message failed. reason says why |
sms.cancelled | A message was cancelled |
sms.held | A message was held because your workspace can’t pay for it. See Validity |
sms.received | A reply or inbound message was received |
batch.paused | A batch was paused |
batch.cancelled | A batch was cancelled |
batch.completed | Every message in a batch has finished |
suppression.created | A number was added to your suppression list, for example after a STOP reply |
For sms.* events, data is the message as it was when the event happened. For batch.* events it is the batch, and for suppression.created the suppression.
Webhooks
Section titled “Webhooks”Create a webhook with the events you want:
{ "url": "https://yourcompany.com/webhooks/connect", "events": ["sms.delivered", "sms.failed", "sms.received"]}The response includes the webhook’s secret, which is only shown once. See Webhooks for verifying signatures, retries and what happens when your endpoint keeps failing. A delivery looks like this:
{ "id": "358120561368793088", "type": "sms.delivered", "occurredAt": "2026-10-10T09:30:04Z", "apiVersion": "2026-10-01", "workspaceId": "301245883010879488", "data": { "id": "357913724415131648", "direction": "outbound", "status": "delivered", "...": "the rest of the message" }}Each delivery has a Webhook-Id header that stays the same if we retry it. Use it to ignore a webhook you have already handled. Reply with any 2xx status to tell us you received it. Otherwise we retry.
API reference: POST /v2/webhooks
Events feed
Section titled “Events feed”GET /v2/events lists your events, oldest first. Use it instead of webhooks, or to catch up on anything you missed. Events are kept for 7 days.
Without a cursor, the feed starts one hour ago, or at since if you send it. Every response has a nextCursor, even when there are no new events. Store it and send it as cursor on your next request to get only what’s new.
For example, to collect replies, call GET /v2/events?types=sms.received every few seconds, passing the last nextCursor each time.
A cursor older than 7 days gets a 410 Gone. Start again with since.