MAILBOX_ADDRESS; use a separate Worker, D1 database, R2 bucket, and secret for each mailbox that needs isolation.
Account setup
The step-by-step guide, including what is free on Cloudflare and what is not, is Receive mail on your domain with Cloudflare. In short:MAILBOX_ADDRESS and MAILBOX_API_SECRET (declared in .dev.vars.example), and runs pnpm run deploy, which deploys and then applies the D1 migration.
Without the button, install Node.js 24 and pnpm 10.7.1, then from this directory:
pnpm install --frozen-lockfileandpnpm wrangler login.cp .dev.vars.example .dev.vars, then setMAILBOX_ADDRESSto the complete mailbox address andMAILBOX_API_SECRETto a unique random value of 32–4096 characters (openssl rand -hex 32).pnpm wrangler deploy --secrets-file .dev.vars. Wrangler creates thexem-cloudflare-mailboxD1 database and R2 bucket if they don’t exist and writes the database ID back towrangler.jsonc.pnpm wrangler d1 migrations apply MAILBOX_DB --remote.- Create a Cloudflare Email Routing rule for that exact address whose action sends to this Worker.
MAILBOX_ADDRESSmust match it exactly, ignoring case. - In Settings → SMTP senders → Connect Cloudflare inbox, enter the Worker URL, exact mailbox address, and the same shared secret. Xem confirms the signed
/v1/mailbox/identityresponse before saving. It returns protocolversion: 1only after the D1 migration is available.
.dev.vars also feeds pnpm wrangler dev. Apply the migration locally with pnpm wrangler d1 migrations apply MAILBOX_DB --local, then deliver a test message with curl -X POST "http://localhost:8787/cdn-cgi/handler/email?from=a@example.net&to=<MAILBOX_ADDRESS>" --data-binary @message.eml.
Storage and recovery
Ingestion first writespending/<id>.eml, then parses MIME and writes deterministic message objects before atomically committing the D1 message and attachment rows. It deletes the pending object only after that commit. A per-minute scheduled handler retries one pending object per run and advances a private cursor so a transient failure cannot block the backlog; transient failures remain pending without an expiry. Malformed MIME and messages with more than 32 attachments move to quarantine/<id>.eml for operator inspection and manual recovery; they are never automatically deleted.
Inspect the pending/ and quarantine/ prefixes through the authenticated Cloudflare dashboard or an account-scoped S3-compatible R2 client. Do not make the bucket public to inspect it. After correcting a parser or operational problem, copy the quarantined raw object and its from, to, and receivedAt custom metadata back to its original pending/<id>.eml key and let the scheduled handler retry it. Byte-identical messages with the same normalized envelope sender and recipient produce the same deterministic ID and deduplicate; distinct bytes remain separate messages.
The mailbox exposes only INBOX, supports metadata search across subject, sender, recipients, and RFC Message-ID, and permits only independent read (\\Seen) and starred (\\Flagged) changes. It has no expunge or delete API.
