Cloudsteading
Agentic Inbox screenshot

Agentic Inbox

Own-domain email with mailbox storage and an AI draft assistant

Agentic Inbox is a self-hosted Fastmail/Gmail alternative built on Cloudflare (Durable Objects, R2, Workers, Workers AI). Paid services required. Inspect the source and license in the linked repository.

Source & license

Upstream license: Apache-2.0

License TL;DR

You can use, change and sell it, including in closed-source products. When sharing copies, include the license, keep required notices and mark changed files. It includes a contributor patent grant with conditions, but no trademark permission or warranty.

Explain Apache 2.0 in plain English →

Summary of the main license. Separate packages and assets can have different terms.

Inspect repository ↗Read this project’s actual license ↗

Repository owner

@cloudflare

See the upstream repository for the original creator and contributors.

Maintain this project? Maintainer verification →

Cloudflare hosting

Paid services required

For ordinary outbound mail, Workers Paid starts at $5 USD per account/month; Email Service includes 3,000 sends/month then $0.35/1,000. Workers AI inference, mailbox storage and request usage have separate quotas. An owned domain and Cloudflare Access configuration are required.

Hosting requirements
  • Production Access is one shared trust boundary: every authorized user and connected MCP tool can operate on every mailbox. Storage isolation is not per-mailbox authorization.
  • Enable Email Routing, Email Service and Workers AI; provision your own R2 and SQLite Durable Objects. Model token usage can exceed the 10,000-neuron/day allowance.
  • Source and configuration review establishes a deployment path and conditional costs; this candidate was not executed or load-tested.
Check current pricing ↗
Sources checked 01/10/2026

Repository snapshot: 48039bb. Hosting eligibility reflects the deployment documentation and listed assumptions.

  • gmail ↗

    An **AI-powered Email Agent** can read your inbox, search conversations, and draft replies -- built with the [Cloudflare Agents SDK](https://developers.cloudflare.com/agents/) and [Workers AI](https://developers.cloudflare.com/workers-ai/).

  • fastmail ↗

    An **AI-powered Email Agent** can read your inbox, search conversations, and draft replies -- built with the [Cloudflare Agents SDK](https://developers.cloudflare.com/agents/) and [Workers AI](https://developers.cloudflare.com/workers-ai/).

  • workers ↗

    { "$schema": "node_modules/wrangler/config-schema.json", "name": "agentic-inbox", "compatibility_date": "2025-11-28", "main": "./workers/app.ts", "observability": { "enabled": true }, "compatibility_flags": [ "nodejs_compat" ], "vars": { // Production deploys must also define POLICY_AUD and TEAM_DOMAIN. // TEAM_DOMAIN may be the base Access URL or the full /cdn-cgi/access/certs URL. // The worker now fails closed outside local development if Access is no

  • r2 ↗

    S": "example.com", "EMAIL_ADDRESSES": [] }, "send_email": [ { "name": "EMAIL", "remote": true } ], "r2_buckets": [ { "binding": "BUCKET", "bucket_name": "agentic-inbox", "preview_bucket_name": "agentic-inbox" } ], "ai": { "binding": "AI" }, "durable_objects": { "bindings": [ { "name": "MAILBOX", "class_name": "MailboxDO" }, { "name": "EMAIL_AGENT", "class_name": "EmailAgent" }, { "name": "EMAIL_MCP", "class_name": "EmailMCP"

  • durable-objects ↗

    ntic-inbox", "preview_bucket_name": "agentic-inbox" } ], "ai": { "binding": "AI" }, "durable_objects": { "bindings": [ { "name": "MAILBOX", "class_name": "MailboxDO" }, { "name": "EMAIL_AGENT", "class_name": "EmailAgent" }, { "name": "EMAIL_MCP", "class_name": "EmailMCP" } ] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": [ "MailboxDO" ] }, { "tag": "v2", "new_sqlite_classes": [ "EmailAgent" ] }, {

  • workers-ai ↗

    ": { "enabled": true }, "compatibility_flags": [ "nodejs_compat" ], "vars": { // Production deploys must also define POLICY_AUD and TEAM_DOMAIN. // TEAM_DOMAIN may be the base Access URL or the full /cdn-cgi/access/certs URL. // The worker now fails closed outside local development if Access is not configured. "DOMAINS": "example.com", "EMAIL_ADDRESSES": [] }, "send_email": [ { "name": "EMAIL", "remote": true } ], "r2_buckets": [ { "binding": "BUCKET", "buck

  • paid ↗

    { "$schema": "node_modules/wrangler/config-schema.json", "name": "agentic-inbox", "compatibility_date": "2025-11-28", "main": "./workers/app.ts", "observability": { "enabled": true }, "compatibility_flags": [ "nodejs_compat" ], "vars": { // Production deploys must also define POLICY_AUD and TEAM_DOMAIN. // TEAM_DOMAIN may be the base Access URL or the full /cdn-cgi/access/certs URL. // The worker now fails closed outside local development if Access is not configured. "DOMAINS": "example.com", "EMAIL_ADDRESSES": [] }, "send_email": [ { "name": "EMAIL", "remote": true } ], "r2_buckets": [ { "binding": "BUCKET", "bucket_name": "agentic-inbox", "preview_bucket_name": "agentic-inbox" } ], "ai": { "binding": "AI" }, "durable_objects": { "

  • paid ↗

    The Workers Paid plan includes Workers, Pages Functions, Workers KV, Hyperdrive, and Durable Objects usage for a minimum charge of $5 USD per month for an account. The plan includes increased initial usage allotments, with clear charges for usage that exceeds the base plan. There are no additional charges for data transfer (egress) or throughput (bandwidth).

  • paid ↗

    | Storage | 10 GB-month / month |

  • paid ↗

    Durable Objects are available both on Workers Free and Workers Paid plans.

  • paid ↗

    Our free allocation allows anyone to use a total of **10,000 Neurons per day at no charge**. To use more than 10,000 Neurons per day, you need to sign up for the [Workers Paid plan](https://developers.cloudflare.com/workers/platform/pricing/#workers). On Workers Paid, you will be charged at $0.011 / 1,000 Neurons for any usage above the free allocation of 10,000 Neurons per day.

  • paid ↗

    | **Outbound emails (Email Sending)** | Not available | 3,000 included per month, then $0.35 per 1,000 emails |

  • paid ↗

    | Class A Operations | 1 million requests / month |

  • paid ↗

    | Class B Operations | 10 million requests / month |

  • paid ↗

    | SQL Stored data <sup>5</sup> | 5 GB (total) | 5 GB-month, + $0.20/ GB-month |

  • Apache-2.0 ↗

    Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial own

  • architecture ↗

    { "$schema": "node_modules/wrangler/config-schema.json", "name": "agentic-inbox", "compatibility_date": "2025-11-28", "main": "./workers/app.ts", "observability": { "enabled": true }, "compatibility_flags": [ "nodejs_compat" ], "vars": { // Production deploys must also define POLICY_AUD and TEAM_DOMAIN. // TEAM_DOMAIN may be the base Access URL or the full /cdn-cgi/access/certs URL. // The worker now fails closed outside local development if Access is not configured. "DOMAINS": "example.com", "EMAIL_ADDRESSES": [] }, "send_email": [ { "name": "EMAIL", "remote": true } ], "r2_buckets": [ { "binding": "BUCKET", "bucket_name": "agentic-inbox", "preview_bucket_name": "agentic-inbox" } ], "ai": { "binding": "AI" }, "durable_objects": { "

  • architecture ↗

    { "$schema": "node_modules/wrangler/config-schema.json", "name": "agentic-inbox", "compatibility_date": "2025-11-28", "main": "./workers/app.ts", "observability": { "enabled": true }, "compatibility_flags": [ "nodejs_compat" ], "vars": { // Production deploys must also define POLICY_AUD and TEAM_DOMAIN. // TEAM_DOMAIN may be the base Access URL or the full /cdn-cgi/access/certs URL. // The worker now fails closed outside local development if Access is no

  • architecture ↗

    S": "example.com", "EMAIL_ADDRESSES": [] }, "send_email": [ { "name": "EMAIL", "remote": true } ], "r2_buckets": [ { "binding": "BUCKET", "bucket_name": "agentic-inbox", "preview_bucket_name": "agentic-inbox" } ], "ai": { "binding": "AI" }, "durable_objects": { "bindings": [ { "name": "MAILBOX", "class_name": "MailboxDO" }, { "name": "EMAIL_AGENT", "class_name": "EmailAgent" }, { "name": "EMAIL_MCP", "class_name": "EmailMCP"

  • architecture ↗

    ntic-inbox", "preview_bucket_name": "agentic-inbox" } ], "ai": { "binding": "AI" }, "durable_objects": { "bindings": [ { "name": "MAILBOX", "class_name": "MailboxDO" }, { "name": "EMAIL_AGENT", "class_name": "EmailAgent" }, { "name": "EMAIL_MCP", "class_name": "EmailMCP" } ] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": [ "MailboxDO" ] }, { "tag": "v2", "new_sqlite_classes": [ "EmailAgent" ] }, {

  • architecture ↗

    ": { "enabled": true }, "compatibility_flags": [ "nodejs_compat" ], "vars": { // Production deploys must also define POLICY_AUD and TEAM_DOMAIN. // TEAM_DOMAIN may be the base Access URL or the full /cdn-cgi/access/certs URL. // The worker now fails closed outside local development if Access is not configured. "DOMAINS": "example.com", "EMAIL_ADDRESSES": [] }, "send_email": [ { "name": "EMAIL", "remote": true } ], "r2_buckets": [ { "binding": "BUCKET", "buck

Upstream screenshot · cloudflare/agentic-inbox repository contributors ↗. Depicts the upstream project. We have not deployed and tested a fresh installation here.

What it can replace

Compare the workflow you need. These mappings describe overlap; full feature parity requires a separate comparison.

Gmail logoGmail ↗

Own-domain webmail, message organization, attachments and reply drafting; no claim of IMAP compatibility, migration or complete hosted mailbox parity.

See supporting source ↗
Fastmail logoFastmail ↗

Own-domain webmail, message organization, attachments and reply drafting; no claim of IMAP compatibility, migration or complete hosted mailbox parity.

See supporting source ↗
external SaaS target
varies
external SaaS target
varies

How it works

The shape of Agentic Inbox on Cloudflare, and how it stacks up against the rented tools it replaces.

Architecture

Diagram of deployment declarations at the reviewed commit. Each app has its own entrypoint; declared resources do not prove runtime calls. Follow file and line sources below.

View upstream source ↗

Configuration and workflow sources

Reviewed commit 48039bb6785a. Files were read as data; upstream applications and CI jobs were not executed.

Deployment configuration · 1 files
wrangler.jsonc ↗

Cloudflare Workers · compatibility 2025-11-28

agentic-inbox · default

Entrypoint: ./workers/app.ts

  • BUCKET → R2
  • MAILBOX → Durable Objects · class MailboxDO
  • EMAIL_AGENT → Durable Objects · class EmailAgent
  • EMAIL_MCP → Durable Objects · class EmailMCP
  • AI → Workers AI
  • EMAIL → Send Email

Named environments are separate deployments. Bindings are shown only where declared. Configured routes are URL patterns, not verified application endpoints.

Runtime source · handlers, binding usage and workflow steps

Observed TypeScript/JavaScript declarations from Worker entrypoints and resolved relative imports. Calls and workflow steps may run conditionally; their listed order is not a proven end-to-end request flow. Router declarations may be mounted under a prefix or may not be registered. This shows code wiring, not a successful deployment or runtime test. Dynamic wiring, aliases and generated code may not resolve.

workers/app.ts ↗
  • L112 · fetch handler exported
  • L113 · email handler exported · calls receiveEmail, console.error
  • L46 · app.use("*")
  • L86 · app.all("/mcp")
  • L89 · app.all("/mcp/*")
  • L94 · app.route("/")
  • L97 · app.all("/agents/*")
  • L104 · app.all("*")
  • L31 · getAccessUrls calls (conditional paths may differ): teamUrl.pathname.endsWith

Environment references: env.MODE · env.DEV

workers/index.ts ↗
  • L69 · app.use("/api/*")
  • L84 · app.use("/api/v1/mailboxes/:mailboxId/*")
  • L88 · app.get("/api/v1/config")
  • L97 · app.get("/api/v1/mailboxes")
  • L102 · app.post("/api/v1/mailboxes")
  • L119 · app.get("/api/v1/mailboxes/:mailboxId")
  • L126 · app.put("/api/v1/mailboxes/:mailboxId")
  • L135 · app.delete("/api/v1/mailboxes/:mailboxId")
  • L145 · app.get("/api/v1/mailboxes/:mailboxId/emails")
  • L168 · app.post("/api/v1/mailboxes/:mailboxId/emails")
  • L214 · app.post("/api/v1/mailboxes/:mailboxId/drafts")
  • L230 · app.get("/api/v1/mailboxes/:mailboxId/emails/:id")
  • L238 · app.put("/api/v1/mailboxes/:mailboxId/emails/:id")
  • L244 · app.delete("/api/v1/mailboxes/:mailboxId/emails/:id")
  • L252 · app.post("/api/v1/mailboxes/:mailboxId/emails/:id/move")
  • L260 · app.get("/api/v1/mailboxes/:mailboxId/threads/:threadId")
  • L264 · app.post("/api/v1/mailboxes/:mailboxId/threads/:threadId/read")
  • L271 · app.post("/api/v1/mailboxes/:mailboxId/emails/:id/reply")
  • L272 · app.post("/api/v1/mailboxes/:mailboxId/emails/:id/forward")
  • L276 · app.get("/api/v1/mailboxes/:mailboxId/folders")
  • L278 · app.post("/api/v1/mailboxes/:mailboxId/folders")
  • L286 · app.put("/api/v1/mailboxes/:mailboxId/folders/:id")
  • L292 · app.delete("/api/v1/mailboxes/:mailboxId/folders/:id")
  • L299 · app.get("/api/v1/mailboxes/:mailboxId/search")
  • L314 · app.get("/api/v1/mailboxes/:mailboxId/emails/:emailId/attachments/:attachmentId")
  • L47 · slugify calls (conditional paths may differ): replace, toLowerCase, text.toString
  • L53 · intQuery calls (conditional paths may differ): c.req.query, Number, Number.isNaN
  • L60 · boolQuery calls (conditional paths may differ): c.req.query
  • L332 · streamToArrayBuffer calls (conditional paths may differ): stream.getReader, reader.read, reader.cancel, result.set
  • L348 · receiveEmail calls (conditional paths may differ): streamToArrayBuffer, parse, map, a.toLowerCase, filter, parsedEmail.to.map, allRecipients.find, allowedAddresses.includes, console.log, crypto.randomUUID, env.BUCKET.head, env.MAILBOX.get, env.MAILBOX.idFromName, replace, env.BUCKET.put, attachmentData.push, s.match, split, s.trim, extractMsgId

Environment references: c.env.DOMAINS · c.env.EMAIL_ADDRESSES · c.env.BUCKET · c.env.MAILBOX · c.env.EMAIL · env.EMAIL_ADDRESSES · env.BUCKET · env.MAILBOX · env.EMAIL_AGENT

workers/mcp/index.ts ↗
  • L27 · mcpText calls (conditional paths may differ): JSON.stringify
  • L36 · mcpError calls (conditional paths may differ): JSON.stringify
  • L47 · mcpResult calls (conditional paths may differ): JSON.stringify, mcpText

Environment references: env.BUCKET

workers/agent/index.ts ↗
  • L94 · getSystemPrompt calls (conditional paths may differ): env.BUCKET.get, obj.json, settings.agentSystemPrompt.trim
  • L110 · createEmailTools calls (conditional paths may differ): defineTool, z.object, describe, default, z.string, z.number, toolListEmails, toolGetEmail, toolGetThread, optional, toolSearchEmails, email, toolDraftEmail, toolDraftReply, z.boolean, toolMarkEmailRead, toolMoveEmail, toolDiscardDraft

Environment references: env.BUCKET · env.AI

workers/email-sender.ts ↗
  • L40 · sendEmail calls (conditional paths may differ): Object.keys, params.attachments.map, binding.send
workers/lib/attachments.ts ↗
  • L24 · storeAttachments calls (conditional paths may differ): crypto.randomUUID, replace, atob, Uint8Array.from, c.charCodeAt, bucket.put, results.push
workers/lib/email-helpers.ts ↗
  • L23 · getMailboxStub calls (conditional paths may differ): ns.idFromName, ns.get
  • L37 · listMailboxes calls (conditional paths may differ): bucket.list, list.objects.map, replace, obj.key.replace
  • L53 · validateSender calls (conditional paths may differ): toLowerCase, Array.isArray, to.join, mailboxId.toLowerCase, fromEmail.split
  • L85 · generateMessageId calls (conditional paths may differ): crypto.randomUUID
  • L99 · buildReferencesChain calls (conditional paths may differ): JSON.parse, filter
  • L121 · buildThreadingHeaders calls (conditional paths may differ): join, references.map
  • L139 · resolveOriginalEmail calls (conditional paths may differ): stub.getEmail
  • L156 · escapeHtml calls (conditional paths may differ): replace, text.replace
  • L171 · textToHtml calls (conditional paths may differ): replace, escapeHtml
  • L182 · stripHtmlToText calls (conditional paths may differ): trim, replace, html.replace
  • L201 · buildQuotedReplyBlock calls (conditional paths may differ): escapeHtml, formatEmailDate, stripHtmlToText, replace
  • L232 · getFullEmail calls (conditional paths may differ): stub.getEmail, stripHtmlToText
  • L248 · getFullThread calls (conditional paths may differ): threadStub.getThreadEmails, emails.map, stripHtmlToText, enriched.sort, getTime

Environment references: env.MAILBOX

workers/routes/reply-forward.ts ↗
  • L24 · handleReplyEmail calls (conditional paths may differ): c.req.param, SendEmailRequestSchema.parse, c.req.json, stub.getEmail, c.json, resolveOriginalEmail, buildReferencesChain, validateSender, generateMessageId, checkSendRateLimit, storeAttachments, stub.createEmail, toLowerCase, Array.isArray, cc.join, bcc.join, toISOString, JSON.stringify, to.join, join
  • L115 · handleForwardEmail calls (conditional paths may differ): c.req.param, SendEmailRequestSchema.parse, c.req.json, stub.getEmail, c.json, resolveOriginalEmail, validateSender, generateMessageId, checkSendRateLimit, storeAttachments, stub.createEmail, toLowerCase, Array.isArray, cc.join, bcc.join, toISOString, JSON.stringify, to.join, c.executionCtx.waitUntil, catch

Environment references: c.env.BUCKET · c.env.EMAIL

shared/folders.ts ↗
  • L61 · getFolderDisplayName calls (conditional paths may differ): folderId.toLowerCase, toUpperCase, folderId.charAt, folderId.slice
workers/lib/mailbox.ts ↗

    Environment references: c.env.BUCKET · c.env.MAILBOX

    workers/lib/tools.ts ↗
    • L48 · toolListMailboxes calls (conditional paths may differ): listMailboxes
    • L54 · toolListEmails calls (conditional paths may differ): getMailboxStub, stub.getEmails
    • L71 · toolGetEmail calls (conditional paths may differ): getMailboxStub, getFullEmail
    • L84 · toolGetThread calls (conditional paths may differ): getMailboxStub, getFullThread
    • L95 · toolSearchEmails calls (conditional paths may differ): getMailboxStub, searchEmails
    • L119 · toolDraftReply calls (conditional paths may differ): getMailboxStub, params.body.trim, verifyDraft, textToHtml, crypto.randomUUID, stub.getEmail, buildQuotedReplyBlock, stub.createEmail, mailboxId.toLowerCase, params.to.toLowerCase, toISOString
    • L198 · toolDraftEmail calls (conditional paths may differ): getMailboxStub, params.body.trim, verifyDraft, textToHtml, crypto.randomUUID, stub.getEmail, stub.createEmail, mailboxId.toLowerCase, toLowerCase, toISOString
    • L274 · toolUpdateDraft calls (conditional paths may differ): getMailboxStub, stub.getEmail, crypto.randomUUID, verifyDraft, stub.deleteEmail, stub.createEmail, mailboxId.toLowerCase, toLowerCase, toISOString
    • L330 · toolMarkEmailRead calls (conditional paths may differ): getMailboxStub, stub.updateEmail
    • L343 · toolMoveEmail calls (conditional paths may differ): getMailboxStub, stub.moveEmail
    • L359 · toolDiscardDraft calls (conditional paths may differ): getMailboxStub, stub.getEmail, stub.deleteEmail
    • L378 · toolDeleteEmail calls (conditional paths may differ): getMailboxStub, stub.deleteEmail
    • L393 · toolSendReply calls (conditional paths may differ): getMailboxStub, checkSendRateLimit, stub.getEmail, buildReferencesChain, mailboxId.split, generateMessageId, verifyDraft, buildQuotedReplyBlock, sendEmail, buildThreadingHeaders, console.error, stub.createEmail, mailboxId.toLowerCase, params.to.toLowerCase, toISOString, JSON.stringify
    • L472 · toolSendEmail calls (conditional paths may differ): getMailboxStub, checkSendRateLimit, mailboxId.split, generateMessageId, verifyDraft, sendEmail, console.error, stub.createEmail, mailboxId.toLowerCase, params.to.toLowerCase, toISOString

    Environment references: env.BUCKET · env.AI · env.EMAIL

    workers/durableObject/migrations.ts ↗
    • L17 · applyMigrations calls (conditional paths may differ): sql.exec, migration.sql.trim, migrationSql.replace, migration.name.replace, storage.transactionSync, run
    • L77 · txn calls (conditional paths may differ): sql.trim, test
    workers/lib/ai.ts ↗
    • L24 · isPromptInjection calls (conditional paths may differ): trim, stripHtmlToText, ai.run, toUpperCase, result.includes, console.warn, console.error
    • L106 · splitQuotedBlock calls (conditional paths may differ): html.match, html.slice
    • L122 · verifyDraft calls (conditional paths may differ): body.trim, test, splitQuotedBlock, stripHtmlToText, replyText.trim, ai.run, cleaned.trim, normalizeWhitespace, console.warn, textToHtml, console.error
    • L191 · normalizeWhitespace calls (conditional paths may differ): trim, s.replace
    shared/dates.ts ↗
    • L14 · safeParse calls (conditional paths may differ): isNaN, d.getTime
    • L30 · formatListDate calls (conditional paths may differ): safeParse, date.toDateString, now.toDateString, date.toLocaleTimeString, date.getFullYear, now.getFullYear, date.toLocaleDateString
    • L58 · formatDetailDate calls (conditional paths may differ): safeParse, date.toLocaleDateString
    • L75 · formatShortDate calls (conditional paths may differ): safeParse, date.toLocaleTimeString
    • L92 · formatQuotedDate calls (conditional paths may differ): safeParse, date.toLocaleString
    Build and deployment pipeline · 0 GitHub Actions workflows

    Repository CI declarations, separate from runtime request processing. Job dependencies and conditions are shown as written; long commands are shortened with an ellipsis; a workflow file does not prove a recent successful run.

    No GitHub Actions workflow was found in the collected tree. Deployment may be manual or configured elsewhere.

    package.json ↗
    • build: react-router build
    • deploy: npm run build && wrangler deploy

    Full upstream document by @cloudflare · README.md · snapshot 48039bb

    Agentic Inbox

    A self-hosted email client with an AI agent, running entirely on Cloudflare Workers

    Agentic Inbox lets you send, receive, and manage emails through a modern web interface -- all powered by your own Cloudflare account. Incoming emails arrive via Cloudflare Email Routing, each mailbox is isolated in its own Durable Object with a SQLite database, and attachments are stored in R2.

    An AI-powered Email Agent can read your inbox, search conversations, and draft replies -- built with the Cloudflare Agents SDK and Workers AI.

    Agentic Inbox screenshot

    Read the blog post to learn more about Cloudflare Email Service and how to use it with the Agents SDK, MCP, and from the Wrangler CLI: Email for Agents.

    How to setup

    Important: Clicking the 'Deploy to Cloudflare' button is only one part of the setup. You must follow the After deploying steps as well. For a full step-by-step guide with screenshots, refer to this comment: https://github.com/cloudflare/agentic-inbox/issues/4#issuecomment-4269118513

    To set up

    1. Deploy to Cloudflare. The deploy flow will automatically provision R2, Durable Objects, and Workers AI. You'll be prompted for DOMAINS, which is the domain (yourdomain.com) you want to receive emails for (email@yourdomain.com).

      Deploy to Cloudflare

    2. Configure Cloudflare Access -- Enable one-click Cloudflare Access on your Worker under Settings > Domains & Routes. The modal will show your POLICY_AUD and TEAM_DOMAIN values. TEAM_DOMAIN can be either your Access team URL or the full .../cdn-cgi/access/certs URL. You must set these as secrets for your Worker.

    3. Set up Email Routing -- In the Cloudflare dashboard, go to your domain > Email Routing and create a catch-all rule that forwards to this Worker

    4. Enable Email Service -- The worker needs the send_email binding to send outbound emails. See Email Service docs

    5. Create a mailbox -- Visit your deployed app and create a mailbox for any address on your domain (e.g. hello@example.com)

    Troubleshooting Access

    1. If you see Invalid or expired Access token, that usually means POLICY_AUD or TEAM_DOMAIN secrets are incorrect.
    2. If you see Cloudflare Access must be configured in production, this application is intentionally enforcing Cloudflare Access so your inbox is not exposed to anyone on the internet.

    Features

    • Full email client — Send and receive emails via Cloudflare Email Routing with a rich text composer, reply/forward threading, folder organization, search, and attachments
    • Per-mailbox isolation — Each mailbox runs in its own Durable Object with SQLite storage and R2 for attachments
    • Built-in AI agent — Side panel with 9 email tools for reading, searching, drafting, and sending
    • Auto-draft on new email — Agent automatically reads inbound emails and generates draft replies, always requiring explicit confirmation before sending
    • Configurable and persistent — Custom system prompts per mailbox, persistent chat history, streaming markdown responses, and tool call visibility

    Stack

    • Frontend: React 19, React Router v7, Tailwind CSS, Zustand, TipTap, @cloudflare/kumo
    • Backend: Hono, Cloudflare Workers, Durable Objects (SQLite), R2, Email Routing
    • AI Agent: Cloudflare Agents SDK (AIChatAgent), AI SDK v6, Workers AI (@cf/moonshotai/kimi-k2.5), react-markdown + remark-gfm
    • Auth: Cloudflare Access JWT validation (required outside local development)

    Getting Started

    npm install
    npm run dev
    

    Configuration

    1. Set your domain in wrangler.jsonc
    2. Create an R2 bucket named agentic-inbox: wrangler r2 bucket create agentic-inbox

    Deploy

    npm run deploy
    

    Prerequisites

    Any user who passes the shared Cloudflare Access policy can access all mailboxes in this app by design. This includes the MCP server at /mcp -- external AI tools (Claude Code, Cursor, etc.) connected via MCP can operate on any mailbox by passing a mailboxId parameter. There is no per-mailbox authorization; the Cloudflare Access policy is the single trust boundary.

    Architecture

    ┌──────────────┐     ┌──────────────────┐     ┌─────────────────┐
    │   Browser    │────>│  Hono Worker     │────>│  MailboxDO      │
    │  React SPA   │     │  (API + SSR)     │     │  (SQLite + R2)  │
    │  Agent Panel │     │                  │     └─────────────────┘
    └──────┬───────┘     │  /agents/* ──────┼────>┌─────────────────┐
           │             │                  │     │  EmailAgent DO  │
           │ WebSocket   │                  │     │  (AIChatAgent)  │
           └─────────────┤                  │     │  9 email tools  │
                         │                  │────>│  Workers AI     │
                         └──────────────────┘     └─────────────────┘
    

    License

    Apache 2.0 -- see LICENSE.

    Frequently asked about Agentic Inbox

    What is Agentic Inbox?+

    Agentic Inbox is a self-hosted Fastmail/Gmail alternative built on the Cloudflare developer platform. Own-domain email with mailbox storage and an AI draft assistant

    What does Agentic Inbox replace?+

    Agentic Inbox is listed as an alternative to Fastmail, Gmail. Compare the features and tradeoffs before migrating.

    What Cloudflare primitives does Agentic Inbox use?+

    Agentic Inbox is built on Durable Objects, R2, Workers, Workers AI.

    How much does Agentic Inbox cost to run?+

    For ordinary outbound mail, Workers Paid starts at $5 USD per account/month; Email Service includes 3,000 sends/month then $0.35/1,000. Workers AI inference, mailbox storage and request usage have separate quotas. An owned domain and Cloudflare Access configuration are required. Production Access is one shared trust boundary: every authorized user and connected MCP tool can operate on every mailbox. Storage isolation is not per-mailbox authorization. Enable Email Routing, Email Service and Workers AI; provision your own R2 and SQLite Durable Objects. Model token usage can exceed the 10,000-neuron/day allowance. Source and configuration review establishes a deployment path and conditional costs; this candidate was not executed or load-tested. Check current Cloudflare pricing before deploying.

    Is Agentic Inbox open source?+

    The upstream repository declares the Apache-2.0 license. Read its terms at https://raw.githubusercontent.com/cloudflare/agentic-inbox/48039bb6785af34e592c2966f87cde2b255c4c80/LICENSE. Source code and contributor credit are available at https://github.com/cloudflare/agentic-inbox.

    Discussion · 0

    sign in to comment →
    No comments yet — be the first.