Core Concepts
The mental model behind Tinbox, from inboxes and threads to the git archive, audiences, campaigns, the send ledger, capability tokens, and the agent-first design.
Tinbox is a self-contained email stack you deploy to your own Cloudflare account: an inbox, a transactional sending API, and newsletters, with no third-party relay in the path. This page is the orientation map. Each concept is a short paragraph; the guides it links to go deeper.
Inboxes and routing
An inbox is an address that receives mail. Mail arrives through Cloudflare Email Routing, hits the ingest worker, and lands in the inbox whose address it was sent to. Routing is structural: which addresses exist and where mail lands is git-checked config, compiled into the worker bundle, changed by a pull request and a deploy. Granting a teammate access to an existing inbox is operational: that happens at runtime, instantly, through the API. The two are deliberately separate systems.
Threads and messages
A message is a single received or sent email. Tinbox groups messages into
threads by owning the Message-ID, so replies stitch into a conversation
deterministically. Threads carry shared-inbox workflow state (open / closed,
assignment, unread) on top of the raw messages. Internal notes are a message
kind that never leaves the app. Read a thread with
tinbox thread <id> or the
get_thread tool; list them with
tinbox threads.
The git archive
Received and sent messages are immutable facts, so Tinbox projects them to a git
repository: an append-only archive/. The sync only ever appends new files, so
it cannot drift from the live state. Agents read mail history as plain files with
ripgrep, no credentials needed. The other direction, outbox/, is a one-way
authoring lane: PR-authored drafts and newsletters are born there and consumed
into the database on merge. Git is never a two-way mirror of live state. The
git-sync worker is an optional module.
Audiences, contacts, and members
An audience is a mailing list (the Mailchimp-style "list" concept). A contact is
a person; a member is that contact's membership in one audience, with a status
(subscribed, pending, unsubscribed, transactional) and per-audience merge
fields. You populate audiences by importing existing
lists or by capturing leads through hosted forms. Segments are saved
multi-condition filters over an audience. See
tinbox audiences and the
list_audiences tool.
Campaigns vs. transactional sending
Two different jobs share one sending substrate. Transactional email is one-to-one, sent by your other apps through the sending API: receipts, password resets, alerts. Campaigns (newsletters) are one-to-many: authored, reviewed, then fanned out to an audience. Campaigns carry a review gate, analytics, and one-click unsubscribe that transactional sends do not. See Send a newsletter.
The send ledger and idempotency
Every send, a single transactional message or a 4,000-recipient campaign, writes
a row to a ledger keyed by a stable idempotency key before dispatch, and
checks the ledger before each send. Queues retry, merges can fire twice, CI
re-runs; the ledger is what turns "retry-safe" into a guarantee instead of a
hope. The sending API mints an idempotency key for you (or you pass your own);
campaign fan-out keys one row per (campaign, recipient), so a re-run only
enqueues un-sent rows. It never double-sends.
For transactional SDK sends, a caller-supplied key is trimmed and stored as the
logical send id. A retry with the same key short-circuits a sent or pending
row, but can reclaim a failed or canceled row. There is no client-side
dedupe window: the ledger is the durable source of truth until rows are
intentionally pruned by operations.
Capability tokens and roles
Every API call, from the app, the CLI, or an MCP agent, funnels through one
authorize(caller, inbox, action) check. Access is two axes. Roles per
inbox: viewer (read), agent (read + draft + reply), admin (+ grant others
on that inbox). Capabilities granted explicitly on top: newsletter:send,
api:send, import. A token (tbx_…) carries a role and coarse scopes; it gets
no special powers over the human who minted it.
There are two common token families:
| Token | Created with | Grants access to |
|---|---|---|
| Inbox agent token | tinbox tokens create --inbox … | inbox mail: read threads, draft replies, send replies, MCP/agent workflows |
| Sending API key | Settings → API keys, or tinbox tokens create-send-key | transactional sending through tinbox/sdk, scoped to an identity, domain, or account |
Use the inbox token when a caller should operate on mailbox state. Use the sending API key when application code should send new transactional email.
Agent-first: every capability is a tool
Tinbox is built agent-first, human-supervised. Every capability is exposed as a
tool in one shared manifest, consumed by three surfaces: the in-app chat
assistant, the MCP server, and the
tinbox CLI. Read-only tools (search, summarize, list)
run freely; destructive ones (sending) are flagged so the client confirms first.
Irreversible structure stays human-gated; reversible actions stay
permission-gated. Browse them all in the tool reference.
Extend & automate
Tinbox is meant to be programmed. Past reading and sending mail, you teach a deployment new behavior with a small set of extension points. The rule of thumb: a skill annotates or acts on one resource (a message, a thread); a plugin is a bundle that adds a whole surface (routes, cron, send-hooks, its own tables).
| I want to… | Use | Where |
|---|---|---|
| Tag/annotate/draft on inbound mail | a reactive-mail skill | config.skills |
| Give the AI assistant a new tool | an agentic skill (agenticSkill()) | config.skills |
| Add a button to a thread | a command skill (commandSkill()) | config.skills |
| Customize the interactive chat assistant | config.assistant | tinbox.config.ts |
| Work the inbox on a schedule | autopilot | config.autopilot + Settings → Autopilot |
| Run a dedicated autonomous agent on one inbox | defineAgents | config.agents |
| Add routes / cron / send-hooks / own tables | a plugin | config.plugins |
| Wire a credential | a secret (requiresEnv) | Settings → Secrets |
The four AI surfaces are deliberately distinct: the assistant (config.assistant)
is the interactive chat/MCP agent; autopilot (config.autopilot) is that
assistant put on a cron with standing orders; agents (config.agents,
defineAgents) are dedicated autonomous agents each bound to one inbox; and agentic
skills are the tools all of them call. See Working with agents
for the full breakdown.
Next steps
- Send transactional email with the
tinbox/sdkclient. - Send a newsletter to an audience.
- Automation & agents — the four AI surfaces, plus chat, MCP, and the CLI.
- Import existing mail and lists.