SMS events
Every message moves through a lifecycle, and Bird emits an event at each step. This page is the full event vocabulary; how events are delivered to your endpoint (signatures, retries, replay) is covered in the Webhooks guide.
The delivery lifecycle, as a path through the event types:
- sms.accepted: Bird has the message and is preparing to hand it to a carrier
- sms.sent: the message was handed to the carrier; awaiting a delivery receipt
- One terminal event:
- sms.delivered: the carrier confirmed delivery to the handset
- sms.undelivered: a non-permanent non-delivery (handset off, unreachable)
- sms.failed: a permanent failure; the message will not be delivered
- sms.expired: the message's validity window elapsed before a terminal receipt arrived
The terminal events surface the carrier's delivery receipt, what SMS platforms call a delivery report or DLR.
The exception is sms.rejected: the message was refused (by a policy check, a charge that could not complete, or a carrier that turned it away) rather than attempted and lost. A message rejected during processing carries sms.rejected as its only event.
Bird SMS is outbound-only today. Replies to your messages are not yet received or captured, so no inbound event type exists.
The event type is an open enum: Bird may add new event types over time, so treat an unrecognized type as a future event rather than an error. Match the types you handle and ignore the rest.
The event envelope
Events arrive at your webhook endpoint in the Standard Webhooks nested envelope described in the Webhooks guide: three fields, type, timestamp, and a type-specific data object. The event's identity is not in the body: it rides in the webhook-id HTTP header, which is stable across retries of the same delivery and is your deduplication key.
| Field | Description |
|---|---|
| type | One of the event types on this page, e.g. sms.delivered |
| timestamp | When the event occurred (RFC 3339); sort by this, never by arrival order, since deliveries are not ordered |
| data | Event-specific payload (fields below) |
Every SMS event's data carries the identity base: sms_id, workspace_id, the to and from addresses, and the tags and metadata you set on the send, echoed on every event so you can route and correlate against your own records without an extra lookup (each is null when the send carried none).
Exemple de code
{
"type": "sms.delivered",
"timestamp": "2026-06-10T14:30:00Z",
"data": {
"sms_id": "sms_01krdgeqcxet5s7t44vh8rt9mg",
"workspace_id": "ws_01krdgeqcxet5s7t44vh8rt9mg",
"to": "+15551234567",
"from": "Bird",
"carrier": "Example Wireless",
"mcc_mnc": "310260",
"tags": [{ "name": "campaign", "value": "spring-2026" }],
"metadata": { "order_id": "ord_123" }
}
}Lifecycle events
sms.accepted
Fires when Bird accepts the send and begins preparing to hand it to a carrier. Payload: the identity base only.
sms.sent
Fires when Bird has handed the message to the carrier and is awaiting a delivery receipt. Payload adds carrier and mcc_mnc (the handling network and its mobile country/network code), each null when not yet known. To measure processing latency, compare this event's timestamp with sms.accepted's.
sms.delivered
The carrier confirmed the message reached the handset. Payload adds carrier and mcc_mnc, each null when the receipt didn't identify them.
Failure events
Each failure event's payload adds an error object: a Bird-stable code (for example unreachable or blocked_by_carrier), a human-readable description, the raw carrier_error_code when one was supplied, and occurred_at.
sms.undelivered
A non-permanent non-delivery: the handset was off or unreachable.
sms.failed
A permanent delivery failure; the message will not be delivered.
sms.rejected
The message was refused: by Bird's checks during processing, by a charge that could not complete, or by a carrier that turned it away. A rejection means the message was stopped, not attempted and lost in delivery. An exhausted wallet ends here with the error code insufficient_balance, and a message whose charge could not complete is not billed.
sms.expired
The message's validity window elapsed before a terminal delivery receipt arrived. Payload: the identity base only.
Reading a message's timeline
Webhooks are how events reach your systems. For a one-off look at a single message, the SMS log in the dashboard renders the same stream as a per-message timeline: every event with its timestamp, the carrier on delivery events, and the error detail on failures. A per-message events endpoint is not part of the public API today; to reconcile programmatically, read the message's current state with GET /v1/sms/messages/{message_id}, whose status, sent_at, delivered_at, and last_error fields reflect the latest event.
Next steps
- Webhooks & events: endpoint setup, signature verification, retries, and replay
- SMS log: the per-message timeline these events drive
- Sending SMS: the send payload whose tags and metadata echo on every event