The message lifecycle
| Status | What it means | What triggers it |
|---|---|---|
scheduled | Accepted but holding until scheduledAt | A send() with a future scheduledAt |
queued | Accepted and ready to dispatch | The send() call, or a scheduled message reaching its fire time |
rendered | Template + variables resolved | Background dispatcher |
sent | A provider accepted the message | Successful provider dispatch |
delivered | The provider confirmed delivery | Provider webhook |
failed | Delivery failed | Provider webhook, a config error, or retries exhausted |
opted_out | Recipient is unsubscribed/suppressed | Provider webhook, one-click unsubscribe link, or send skipped because the recipient previously opted out |
blocked | Delivery halted by outbound abuse detection | Phishing scan flagged the content with high confidence; the message is never handed to a provider. The message timeline records a generic notice; detection details are operator-only and not exposed via the customer API. |
canceled | Delivery intentionally stopped before dispatch | messages.cancel() or the dashboard cancel action |
The status enum also includes
dispatched. In the current pipeline a message moves
queued → rendered → sent, and the moment a provider accepts it is recorded as a
dispatched event on the message’s timeline while the status itself becomes
sent. Read sent as “handed to the provider.”rendered → sent → delivered) without calling a provider, so your logs look the same
as live without anything leaving the building.
Querying messages
List messages newest-first with cursor pagination, and filter bystatus, channel,
template, or your own metadata:
Engagement (opens & clicks)
For email, SenderKit also records provider-reported opens and link clicks asopenedAt / clickedAt on the message — each set once, on the
first occurrence. These are engagement signals, not lifecycle states: they
never change a message’s status, and delivered remains the terminal
happy-path status regardless of whether the recipient later opens it or clicks
a link.
Subscribe to the message.opened / message.clicked webhook events
to be notified as they happen, or read the fields directly via
messages.get/messages.list.
Retention
Messages are retained for a limited window and then deleted:| Plan | Retention |
|---|---|
| Free | 3 days |
| Starter | 30 days |
| Pro | 90 days |
Sending
How a message gets created and dispatched.
Channels & Providers
Who delivered the message and how failures surface.