Message states
Send statuses, delivery labels, inbound labels and hidden labels, what each one means and proves, and what to do about each.
Inbot keeps states that mean different things apart. queued, sent, delivered, bounced and failed are never merged, and no surface reports an outcome Inbot has not observed.
Send status
Every outgoing message has a send_status:
| Status | Meaning | What to do |
|---|---|---|
queued |
Stored and waiting to be sent, or waiting to retry | Wait |
sending |
Being handed to Cloudflare right now | Wait |
sent |
Cloudflare accepted the message. It does not mean the recipient received it. | Watch delivery labels |
failed |
It was not sent. send_reason explains why. |
Fix the cause, then send again with a new idempotency key |
send_unknown |
Inbot cannot tell whether Cloudflare accepted it | Do not resend. The owner resolves it. |
send_unknown happens after a network error or timeout during the provider call, a crash or expired lease while sending, or a success response Inbot cannot match. When the owner resolves it, the message becomes sent or failed with the reason owner_confirmed_sent or owner_confirmed_not_sent.
Draft scheduling has its own send_status on the draft: scheduled, sending or failed.
Delivery labels
Delivery reports from Cloudflare are added as labels on the sent message:
| Label | Meaning |
|---|---|
delivered |
The receiving server accepted the message |
bounced |
The message bounced |
complained |
The recipient marked it as spam |
rejected |
Cloudflare rejected the message |
deferred |
Delivery was delayed and will be retried |
failed |
Delivery failed |
These labels are a history of what Cloudflare reported, not a single state: one message can carry both delivered and bounced, for example when it had several recipients. Delivery labels do not change send_status. Reports Inbot cannot match to a message are dropped.
Labels
Labels are free-form strings. A thread’s labels are the union of its messages’ labels.
| Labels | Set by |
|---|---|
received, unread |
Inbot, on every incoming message |
unauthenticated |
Inbot, when Cloudflare’s results show no SPF, DKIM or DMARC pass |
queued, sent, failed, send_unknown |
Inbot, from the outbox. Callers cannot change them. |
delivered, bounced, complained, rejected, deferred |
Inbot, from delivery reports. Callers cannot set them. |
spam, blocked, trash |
You or an agent. Inbot never sets them automatically. |
| Anything else | You or an agent, with add_labels and remove_labels |
System state labels (received, queued, sent, send_unknown, failed, delivered, bounced, complained, rejected, deferred) are protected on every message, even without an outbox entry. Message, thread and batch edits that add or remove any of them return validation_error and make no changes. Custom labels and unread remain editable.
Mark mail as handled by removing unread.
Hidden labels
spam, blocked, unauthenticated and trash hide a message. Listing hidden mail needs both an include_spam, include_blocked, include_unauthenticated or include_trash filter and the matching label_*_read permission; without the permission, the filter is ignored. Reading a hidden message directly returns not_found, and a thread is hidden if any of its messages is. Search always excludes hidden mail.
Events
Each state change also produces an event: message.received (or .spam, .blocked, .unauthenticated for hidden mail), message.sent, message.send_unknown, message.delivered, message.bounced, message.complained, message.rejected and message.failed. message.failed also comes from a provider failure report with failure.reason: "delivery_failed", without changing the send status. Deferral produces no event. See Events.