Skip to content

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.

EventWhen
sms.queuedA message was accepted
sms.sentA message was handed to the network
sms.deliveredA message was delivered
sms.failedA message failed. reason says why
sms.cancelledA message was cancelled
sms.heldA message was held because your workspace can’t pay for it. See Validity
sms.receivedA reply or inbound message was received
batch.pausedA batch was paused
batch.cancelledA batch was cancelled
batch.completedEvery message in a batch has finished
suppression.createdA 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.

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

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.