Giving an Agent an Inbox Without Giving It Your Mailbox
$ grep -n "^##" 2026-08-agent-inbox-without-your-mailbox.md
A dedicated receiving address is a useful default when an agent needs selected messages, because it limits the mail the integration can expose.
Giving an assistant access to a personal mailbox starts with an awkward access decision. The useful messages are mixed with years of correspondence the task may never need. If the job is to process documents deliberately sent to the assistant, a separate inbox gives that collection a clear boundary.
This site’s implementation receives mail at agents@cutler.sg. It uses a Cloudflare Email Worker, an application inbox and tools for reading or replying. That path needs no OAuth grant to my personal mailbox.
It also introduces several obligations that an address alone cannot solve.
The mail enters through a narrow relay
Mail sent to the dedicated address reaches the Cloudflare Email Worker. It relays the body and attachment metadata to the application, then uploads attachment bytes separately. The agent reads through inbox tools.
Cloudflare’s email handler interface supplies the envelope, headers and raw MIME stream, and can forward a message to a verified destination. The Worker parses that MIME and sends structured requests to the application.
The relay credential differs from the credential agents use for their tools. The Worker needs to push incoming mail into storage. Possessing that credential does not confer the separate agent API’s sending authority.
This is a useful separation, but it needs precise wording. The current agent credential exposes both inbox and sending tools; it is not a read-only inbox role. A dedicated address limits the available mail. It does not, by itself, limit what an authorised agent can do next.
For an assistant whose entire job is document summarisation, I would expose only reading. OWASP uses this exact capability distinction: an email summariser may need to read messages without functionality for deleting or sending them. That is a tool and permission decision in addition to the routing decision.
A trusted relay can carry an untrusted message
Anyone who can send mail to the address can contribute content to this inbox. Authenticating the Worker proves which integration submitted the payload. It does not turn the sender’s text into an instruction from the user.
The application labels email bodies, previews and attachments as untrusted when returning them through tools. It also sanitises headers and filenames. Those measures do different jobs: removing a newline from a header can prevent malformed output, while a warning tells the assistant how to interpret external text. Neither makes the contents safe to obey.
An attachment asking the assistant to upload the other attachments somewhere remains an external request. Parsing the PDF successfully does not authorise that transfer. OWASP’s prompt-injection guidance covers precisely this risk from external documents and other inputs.
The reply tool tells the agent to act only on an independent user request. In the current implementation, that instruction is not backed by a server-side approval receipt. An optional recipient policy can restrict outbound destinations, but it must actually be configured. These are reasons to inspect the available tools, not assume the warning has closed every route out.
The OAuth and data-loss discussion applies here too: decide what may be read and what may leave as separate questions.
The message exists; the attachment may not
The less obvious complication is partial delivery.
The application stores the message and its attachment metadata first. I would make confirmed storage a requirement of the intake contract: return success only after checking the write result, and return 503 on storage failure so the Worker can retry or attempt fallback.
Attachment bytes arrive afterwards, one request per part. Consider an illustrative message with two files. The first upload succeeds; the second is rejected. The inbox still contains the message and the first file. For this partial-upload case, I would add a fallback: have the Worker attempt to forward the original MIME message to a configured inbox.
Rendering diagram...
A simplified tool result might look like this:
{
"subject": "Documents for review",
"attachments": [
{ "filename": "brief.pdf", "downloadable": true },
{ "filename": "appendix.pdf", "downloadable": false }
]
}
Those are fictional document names, not inbox data. The important field is downloadable. A filename in the metadata is insufficient evidence that the agent can read the file.
The agent’s next answer should preserve the gap: it can review the brief, but it has not reviewed the appendix. Inventing an account of the missing appendix would turn a visible transport problem into an invisible reasoning error.
With that fallback, the destination can contain the complete original while the application holds a partial copy. That duplication is a consequence of the proposed recovery design. Fallback is itself an attempt: an invalid destination or delivery failure can defeat it. Forwarding does not guarantee that mail cannot be lost.
Limits belong in the reading contract
The Worker bounds attachment count and aggregate encoded size. I would also limit each relay request by its complete serialised JSON size, including metadata and base64 content. A ceiling of 4,000,000 UTF-8 bytes, for example, would be a budget for the request body, not a four-megabyte allowance for the original file.
Separating uploads avoids placing every attachment in one oversized request. It does not make one oversized attachment fit. If relay cannot complete, preserving the original through fallback is preferable to silently pretending all parts arrived.
Retention creates another partial state. Message records have a 30-day TTL; attachment bytes have seven days. Index eviction can remove them earlier. A listed message may therefore outlive the documents it describes. The reading tools check which attachment bytes remain available, rather than infer availability from the message’s age or metadata.
Duplicate handling also has limits. Repeated messages with the same normalised Message-ID overwrite the stored record. A message without that header receives a generated ID, so repeated delivery lacks the same stable key. This reduces some duplicates; it is not an exactly-once mailbox.
A local mocked suite can exercise these recovery decisions: incomplete uploads, size limits and missing IDs. Those tests cannot verify Email Routing configuration or prove delivery to a live fallback inbox.
Before connecting an agent, decide which messages belong in its incoming collection and whether the task requires any sending capability. Then test a message whose second attachment cannot be stored. The agent should give an honest partial reading and identify the missing attachment. If you add the proposed fallback, verify its delivery separately.
$ subscribe --newsletter
Practical AI engineering, in your inbox
Field notes for technical leaders building agents, evaluation systems, governance, and production infrastructure.
Related
Whose Leak Is It? DLP When an AI Agent Holds Your OAuth Token
I assumed an MCP agent on my OAuth token could only see what I could see — but API access can differ from browser permissions, and onward transfers need their own controls.
When Should an Agent Ask Permission?
A useful agent acts within delegated authority, prepares consequential actions for review, and asks again when the action changes.
What I Need Before I Approve an Agent’s Work
A review bundle should connect the requirement to evidence for the exact candidate and make the remaining decision explicit.