Platform

Why is my message accepted but not delivered?

The word doing the damage is "accepted". It reads like a delivery confirmation and it is a receipt from Bird: we have taken the send and started preparing it. Nothing has reached a person, a handset or a mail server at that point.

So a message sitting at accepted is not stuck between two places. It is at the beginning.

What actually happens between accepted and delivered?

There is at least one step in between, and it is the one worth instrumenting.

On email, email.accepted fires once per requested recipient and is the first event in every stream. Then email.processed means the message is built and queued for delivery to the recipient's mail server. Only email.delivered means the receiving server took responsibility for it.

On SMS, the same shape with different words: sms.accepted, then sms.sent when the message has been handed to the carrier, then sms.delivered when the carrier's receipt says it arrived.

The useful consequence is that these two boundaries let you separate whose time is whose. Comparing processed against accepted gives you our own processing time; comparing delivery against the handover gives you the carrier's or the mailbox provider's. If a message is slow, that pair of subtractions tells you which half to chase, and one of them is ours to answer for.

Why is it still not delivered?

Because "not delivered yet" and "will never be delivered" look identical from a single event, and there are four different endings.

On email the send either lands, gets deferred and retried, which resolves later to delivered or bounced, is bounced by the receiving server, or is rejected with no delivery attempt made at all. That last one is the case people misread most: a rejection is not a failed attempt, it is the absence of one, and its reason lives on our side rather than the recipient's.

On SMS the endings are undelivered for a non-permanent failure, failed for a permanent one, rejected when the message was refused rather than attempted, and expired when the carrier stopped trying. Which error code rides along is what identifies the cause, and why carriers filter SMS covers reading those.

Is there one lifecycle I can learn?

No, and that is the honest answer rather than a gap in the documentation.

Each channel has its own vocabulary because each has its own delivery reality. Email has deferrals because mail servers ask you to come back later; SMS has expiry because a carrier gives up on its own schedule; WhatsApp and voice have their own terminal states again. A single flattened model would be wrong for all four.

What does transfer is the shape: an acceptance from us, a handover, a delivery confirmation from whatever is downstream, and a set of endings where the code rather than the event names the cause. Learn that, then read the guide for the channel you are actually debugging: email events, SMS events, WhatsApp events and voice events.

Can a delivered message stop being delivered?

Yes, and it is worth knowing before you treat delivered as terminal in your own data.

A receiving mail server can accept a message at SMTP time and send a bounce report afterwards, which arrives as an out-of-band bounce. When that report classifies as a bounce, the server has retracted its earlier acceptance and the recipient moves from delivered to bounced. A hard one also suppresses the address.

Reports that do not classify as a bounce, an auto-reply for instance, are recorded on the timeline and leave the status alone.

The practical rule is the one that follows from events being unordered anyway: store the status as something that can move, keyed on the latest event by its own timestamp, rather than as a value you write once when the happy path finishes.

Développez sur le même réseau.

Une clé API de test est disponible immédiatement. La production est activée dès que vous ajoutez un moyen de paiement et vérifiez un expéditeur.

Commencez avec un seul canal.
Ajoutez les autres quand vous êtes prêt.

Une clé API de test est disponible immédiatement. L'accès production se débloque dès que vous ajoutez un moyen de paiement et vérifiez un expéditeur.

Vous utilisez Claude Code, Cursor ou Codex ? Copiez un prompt de configuration et votre agent installe la CLI Bird et les compétences pour vous. Choisissez le vôtre :

Cursor