Accepted versus delivered
Accepted versus delivered is the difference between a provider taking responsibility for a message and a receiving server storing it, the first observable at send time and the second reported later, or never.
A dashboard that says “sent” is hiding at least four different facts behind one word, and the gap between them is where an agent quietly does the wrong thing. It resends a message that already went out. It tells a user the mail arrived, twenty minutes before it bounces. It closes a ticket because an API returned HTTP 200.
Two different claims, made by two different parties
Accepted is a claim by the sending provider. Its API returned a 2xx with a submission ID, so the message is now that provider’s problem rather than ours. It is the strongest thing knowable synchronously, and it is much weaker than most integrations treat it: it says a message was handed over, not that anybody received it.
Delivered is a claim by the recipient’s mail server, relayed back asynchronously as a provider callback. It says a receiving system accepted the message for a specific address. It arrives minutes or hours later, and for some recipients it never arrives at all, because not every receiving system reports.
The two live in different places in the data model, which is the clearest signal that they are not degrees of the same thing. State lives on the message. Per-recipient delivery is tracked separately, on the recipient rows, and that is the only place the word “delivered” ever appears.
The states between them
An outbound message has six legal states in the schema, and one of them, submitting,
is never written to the message in this build; only the internal send intent carries it.
| State | Written by | Event |
|---|---|---|
queued | The API request handler | message.queued |
submitting | Nobody, on the message | none |
accepted | The send job | message.accepted |
failed | The send job | message.failed |
submission_unknown | The send job | message.submission_unknown |
canceled | The send job | not emitted in this build |
queued proves that your own request was well-formed, authorized, within limits and
durably stored. That is genuinely useful, and it is entirely self-reported. No byte
has left the building.
submission_unknown is the state most systems do not model, and it is the honest
answer to a large class of real outcomes: a 5xx from the provider, a connection
error, a timeout, a 409, or a 2xx with no submission ID in the body. In each case the
provider may or may not have committed the message, so recording “failed” would be a
lie in one direction and “accepted” a lie in the other. The mapping from every
provider response to a state is tabulated in
what an email status actually proves.
Which one usage follows
Acceptance. One outbound unit is debited per unique recipient when the state becomes
accepted, not when the message is queued and not when it is delivered, because
acceptance is the last point anyone on this side can observe directly.
There is one reversal path. If the provider later reports the address as suppressed,
the unit is written back and the period’s count is decremented, and the next send to
that address is refused at request time with HTTP 422 recipient_suppressed, marked
non-retryable. A suppressed address is a standing fact about the relationship rather
than a transient error, and it is the one case worth surfacing to a human
immediately.
What neither status proves
Inbox placement. delivered means a receiving mail server said yes. What that server
did next is invisible from here: it may have filed the message in a spam folder,
applied a rule that archived it, or shown it in a promotions tab nobody opens. No
email provider can report placement, because the receiving system does not say.
Nothing in this product guarantees inbox placement, and any vendor claiming otherwise
is describing something they cannot observe.
Human attention. Sending providers report email.opened and email.clicked callbacks,
and both depend on tracking that a recipient’s client may refuse or a privacy proxy may
fake. This product does not pass either on as a customer event, and neither would be
evidence that a person read anything.
The practical rule for an agent: treat accepted as “handed over, stop retrying”,
treat a bounce or a suppression as “tell a human”, and never let a status be the
thing that closes a task. The event surface those signals arrive on is documented
under events and webhooks.