Your send request can succeed before the recipient's phone receives anything. A later carrier report supplies another result, which your application must handle separately from request acceptance.
What does a delivery receipt tell me?
A receipt reports delivery success or a delivery problem from the network's perspective.
An SMS delivery result can arrive while the phone sits unread in a drawer. It does not prove that the recipient saw the sender identity. It also does not establish that they read the complete text.
The network sends receipts separately from its response to a send request. It can also suppress some reports. The SMPP receipt reference documents these controls for carrier connections.
Which carrier receipts does Bird expose?
You receive public events for recognized delivery outcomes. Buffered receipts remain internal.
A buffered receipt says the carrier is holding the message. Bird records it in its internal event log without changing the public message status. You see no update in the public timeline or your webhooks.
| Carrier receipt status | Bird event |
|---|---|
delivered | sms.delivered |
expired | sms.expired |
rejected | sms.rejected |
delivery_failed with a recoverable reason | sms.undelivered |
delivery_failed with a permanent or unmapped reason | sms.failed |
buffered | No public event |
An unrecognized carrier status produces no event. A missing public update therefore does not establish that the carrier sent no receipt.
Why does one failure status produce different events?
Bird uses the carrier's failure reason to decide whether the underlying problem may clear.
| Carrier reason | Bird error code | Classification |
|---|---|---|
unavailable_subscriber | unreachable | Recoverable |
received_network_error | provider_unavailable | Recoverable |
capacity_limit_reached | provider_unavailable | Recoverable |
unknown_subscriber | invalid_destination | Permanent |
opted_out | recipient_opted_out | Permanent |
carrier_rejected | content_rejected | Permanent |
A recoverable failure produces sms.undelivered. The message is still final: Bird does not retry it automatically. Reaching the recipient requires another send after the underlying problem clears.
A permanent failure produces sms.failed. An unmapped reason also produces that event with the error code unknown. That result means Bird could not classify the carrier's reason. It does not independently prove that the destination is permanently unreachable.
Can I set when the carrier stops trying?
You cannot set the carrier's delivery deadline through Bird's send endpoint.
Bird rejects any supplied validity_period with HTTP 422 SMSUnsupportedFeature. That field would control how long the carrier keeps trying to deliver the text.
The corresponding response field is not returned. Bird produces sms.expired when a carrier reports expiry. Your application cannot infer the carrier's waiting period from an absent field.
How should I handle receipts in my application?
You should track delivery outcomes separately from request acceptance and from any action the recipient takes.
Treat a message without a final delivery result as pending. Read the error code with a failure event so you can investigate its cause. Preserve unfamiliar codes in your logs because the error-code set is extensible.
Use delivered events for a delivery metric. For a passcode or confirmation flow, record success when the recipient completes the action. A delivery receipt alone cannot supply that measurement.
SMS events documents the public event payloads and message timeline.
In short
Delivery does not establish reading.
A receipt reports the network's delivery outcome without confirming that a person saw the text.
Some receipts have no public event.
Bird records buffered receipts internally without changing the public status, timeline or webhook stream.
The reason determines the failure event.
Recoverable carrier reasons produce sms.undelivered; permanent or unmapped reasons produce sms.failed.
A recoverable reason does not trigger another send.
An undelivered message is final, so another attempt requires a separate send.