Who owns an email before it is sent?
An agent can read a message and save a draft without sending anything. That distinction sounds simple, but it becomes useful only when each stage names the state it owns, the external effect it can cause, and the evidence needed before moving forward.
This article inspects Cloudflare’s Agentic Inbox repository at commit 48039bb…4c80 and uses a fictional message. The repository was not installed or run. No account, mailbox, domain, model, attachment, or email delivery was used.
One message, several owners
Imagine an inbound email called Request #104 with a harmless text attachment named requirements.txt. The sequence below is an original teaching map reconstructed from pinned source. It is not a runtime trace.
- 01ReceiveEmail Routing invokes the Worker’s email handler with a raw message stream.
- 02Store attachment bytesThe inspected inbound path writes attachments to R2 under keys derived from message, attachment, and sanitized filename identities.
- 03Persist message stateA mailbox Durable Object then stores message, folder, thread, header, and attachment-metadata rows in SQLite.
- 04Read into contextThe browser, email agent, or MCP tools can retrieve selected mailbox and thread state.
- 05DraftA reply becomes a separate message record in the Drafts folder. It is durable, but it is not delivered.
- 06ConfirmA person or calling workflow decides whether the exact recipient, subject, body, and attachments should cross the send boundary.
- 07Send requestA send tool verifies or transforms the body, then calls the outbound email binding; after that succeeds, the inspected tool path writes a Sent-folder record.
The storage boundary is not the authorization boundary
The pinned repository creates separate Durable Object identities for mailboxes and stores email state in each object’s SQLite database. Attachment metadata is stored with the email records, while attachment bytes are stored separately in R2. That is a useful state partition, but it does not mean each signed-in person is authorized for only one mailbox.
The repository README explicitly says that anyone who passes the shared Cloudflare Access policy can access every mailbox in the application, including through its MCP endpoint. It describes Cloudflare Access as the single trust boundary and says there is no per-mailbox authorization. Storage isolation and user authorization answer different questions.
A draft is evidence of writing, not evidence of sending
The inspected agent surface exposes tools for reading messages, searching, creating drafts, moving messages, and discarding drafts. Its agent-specific tool set does not include a send tool, and its default prompt says the operator reviews and sends drafts from the interface. Draft updating exists in the shared tool path and is exposed by the separate MCP surface, which also exposes send operations whose descriptions tell a client to call them only after confirmation.
That wording is useful guidance, not a durable approval record by itself. In the inspected schema, there is no confirmation snapshot binding an approver to an exact recipient, subject, body, attachment set, and send request. A system that needs that assurance would have to record and enforce it explicitly.
| State | What exists | What has happened | Evidence question |
|---|---|---|---|
| Received | Parsed message plus mailbox identity | Inbound delivery reached the handler | Was failure retried, rejected, or silently accepted? |
| Persisted | SQLite message row and attachment metadata; R2 objects for attachment bytes | Mailbox state changed | Can partial failure leave metadata and objects out of sync? |
| Drafted | A Drafts-folder message linked to the original/thread | No outbound delivery | Can the reviewer inspect the exact proposed payload? |
| Confirmed | A decision in the surrounding workflow | Still no send merely because prose says “confirm” | Is the decision bound to immutable content and actor identity? |
| Accepted for send | An outbound provider response | The external send call returned successfully | Does this prove provider acceptance only, or recipient delivery? |
| Recorded as sent | A Sent-folder record | Local state records the successful send path | Could provider success and local persistence diverge? |
Two return paths matter
Return to composition
In the inspected update path, failed body verification occurs before the old draft is deleted. Replacement then deletes the old draft before creating the new one, so a later write failure is a separate partial-failure case—not an atomic preservation guarantee.
Return with an error
An error response is not proof of either delivery or non-delivery; reconcile provider evidence before retrying. Provider acceptance followed by a failed local Sent-folder write leaves uncertain local record state and is not a safe automatic retry. The inspected source shows an order; this article does not claim that failure was executed.
Use the map on another system
For every transition, record the state owner, stable identity, permitted writer, external effect, confirmation binding, failure return path, and evidence. A downloadable blank worksheet keeps those questions separate without pre-filling an approval.
Download the blank message-ownership worksheet
Sources and limits
Repository source checked at commit 48039bb…4c80: the pinned README, application entry, mail routes, schema, attachment helper, tool paths, agent surface, and MCP surface. Cloudflare’s official Email Service, Durable Objects SQLite, R2, and Agents documentation supports only the attributed platform vocabulary.
The repository declares Apache-2.0; dependency, model, service, and generated-output terms were not audited. No reliability, exactly-once delivery, transactional storage, security, authorization sufficiency, recipient delivery, adoption, or personal-use result is claimed.