Skip to content

Repository files navigation

e2a runbooks

Example agents that show what you can build with e2a — one focused runnable project per workflow/framework pairing. Each is a small, complete project: clone it, fill in a .env, point a webhook at it, and email it.

e2a gives an AI agent a real email address: verified inbound mail over signed webhooks, replies that stay in-thread, and a human approval gate before the agent sends anything. These eight demonstrate eight different things you can do with that.

These are examples, not products. Each one is deliberately small enough to read in a sitting, and each README ends with a Simplifications worth knowing section listing exactly what it leaves out — in-memory deduplication, hardcoded routing tables, no OCR, no calendar. Copy from them; don't deploy them as-is.

Runbooks

Runbook Agent Framework e2a surface it exercises
mastra/ Support agent — answers in-thread, with an approval gate before it sends Mastra — built-in server, memory, HITL primitives Threading, memory, reviews
openai-agents/ Receptionist — answers what it can, forwards the rest to the right desk OpenAI Agents SDK — handoffs are first-class here forward, update_labels
ecommerce/ Ecommerce support — looks up orders, answers delivery questions, and routes changes to a human OpenAI Agents SDK — constrained tools make safe support actions explicit inbound.from_event, email.reply, labels
claude-agent-sdk/ AI SRE — triages monitoring alerts, recommends, never acts Claude Agent SDK — the ops/coding agent SDK Verification-as-gate, reviews (account-scoped)
langchain/ Contract review — reads an emailed PDF, replies with a structured risk summary LangChain v1 — document work is its origin attachments, get_attachment
crewai/ Escalation desk — a crew triages, investigates, and answers from the specialist's own inbox CrewAI — multi-agent crews, so multiple identities make sense Multiple agents, cross-identity conversation_id
pydantic-ai/ Scheduling secretary — negotiates a meeting time over many round-trips, storing nothing Pydantic AI — typed outputs, and no session store, so the thread really is the state conversations, messages.get
langgraph/ Supplier follow-up deskstarts the conversation: chases open purchase orders, reads the replies, escalates slips LangGraph — a declared state machine, which is what a multi-day chase is contacts.outreach (the mailbox as the queue)

Find an example by use case

Start with the job you want the agent to do, then choose the framework whose shape fits it:

What you want to build Start here What the example demonstrates
Support or escalation agent mastra/ or crewai/ Threaded support replies, human approval, specialist routing, and multiple agent identities
AI receptionist openai-agents/ Handoffs from a front desk to the right human desk
Scheduling agent pydantic-ai/ Multi-round-trip scheduling where the email conversation is the state
Ecommerce or order-support agent ecommerce/ Read-only order lookup, threaded support, and constrained human review for fulfillment changes
Procurement agent langgraph/ Outbound supplier follow-up, state transitions, and escalation
Contract or document-review agent langchain/ Authenticated attachments, PDF extraction, and structured results
SRE or alert-triage agent claude-agent-sdk/ Sender verification, least privilege, and mandatory human approval

This table is deliberately use-case-first: it is also the index to use when linking from a tutorial, framework example, directory listing, or AI answer. The ecommerce runbook uses synthetic order records so it can be cloned and tested without a commerce account; replace the store with an authenticated, read-only integration before exposing real order data.

Distribution-ready links

Use the smallest relevant link when sharing the project:

Each runbook is intentionally a runnable starting point, not a hosted product. Link to the specific directory when demonstrating a framework integration; link to the repository root when sharing the collection.

The mastra/ runbook is the fully-worked reference — same core, plus tests, structured tool errors, and the outbound approval path. The others are deliberately minimal.

Seven of the eight wait for mail. langgraph/ is the outbound one, and it is the only one that has to answer a question no inbound email will ever arrive to ask: who is overdue for a follow-up, and who already answered?

Each pairs a use case with the framework that suits it, and each exercises an e2a surface the others don't. Each lives in its own directory with its own dependency manifest and pinned SDK versions, so one framework's churn never breaks another.

Using one

git clone https://github.com/tokencanopy/e2a-runbooks

# TypeScript runbooks
cd e2a-runbooks/mastra && npm install && cp .env.example .env && npm run dev

# Python runbooks
cd e2a-runbooks/openai-agents
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt && cp .env.example .env
uvicorn app:app --port 8000

Each runbook's README carries its own quickstart, configuration table, and deployment notes. One caveat worth knowing up front: crewai/ needs Python <3.14 — CrewAI 1.x declares that upper bound and will not install on 3.14. The other Python runbooks have no upper bound.

What these are for

Reading an API reference tells you which calls exist. It doesn't tell you the things that actually break an email agent, which is what these examples encode — each was driven end-to-end against a stand-in e2a API before being published, and every lesson below is one that broke a runbook first:

  • Verify the signature on raw bytes. Parse first and re-serialize and the HMAC will not match.
  • In an async handler, use the async client. The sync E2AClient raises RuntimeError when called from inside a running event loop, so a sync client in an async def webhook fails on the first inbound email — not later, under load. The Python runbooks use AsyncE2AClient and await every call. The same applies to your agent framework's runner: Runner.run_sync() and Crew.kickoff() block or raise inside a loop; use Runner.run() and kickoff_async().
  • Only inbound mail should wake the agent. e2a emits the full lifecycle; without a guard, your own delivery receipt triggers a reply, which produces another receipt.
  • Webhook delivery is at-least-once. Claim the event id before running the agent — the failure being prevented is a second reply in someone's inbox.
  • Inbound email is untrusted input. Pass the authentication verdict to the model and instruct it to treat message bodies as data, not instructions. An agent with an inbox and no provenance is a prompt-injection surface.
  • Gate sending in infrastructure, not in a prompt. e2a can hold outbound mail for human approval, so "wait for a human" is not something the model can be talked out of.
  • A conversation gives you the skeleton, not the content. conversations.get() returns message summaries without body text; rebuilding what was actually said costs one fetch per message. An agent that needs history has to pay for it — see pydantic-ai/.
  • For outbound agents, schedule off what the server saw, not what you wrote. An agent that initiates has to decide who is due for a follow-up, and the obvious design — send, then write next_action_at forward — sends twice whenever the write fails after the send. Filtering the sweep on e2a's server-maintained last_outbound_at closes that gap, because it moves with the message that was actually accepted. See langgraph/.

Conventions

Each runbook:

  • is a standalone project — its own dependency manifest, its own pinned SDK versions, verified against the latest published releases;
  • reads all configuration from the environment, with a documented .env.example;
  • verifies the webhook signature on raw bytes, filters to email.received, and replies in-thread;
  • passes the SPF/DKIM/DMARC verdict into the prompt and instructs the agent to treat message bodies as data;
  • uses only synthetic addresses (example.com, agents.localhost, .example). No real inboxes, customers, or keys.

The reference runbook (mastra/) goes further: transport separated from logic so the handler is testable without a server or credentials, tests covering signature rejection / event filtering / duplicate suppression, structured tool errors, and the outbound approval path. The minimal runbooks stay one file and say what they simplified.

Contributing

Adding a runbook is a directory, not a repo. Follow the conventions above, verify every SDK symbol against the installed package rather than from memory, pin to the latest published version, and add a row to the table.

License

Apache-2.0 — see LICENSE and NOTICE.

About

Deployable reference agents that use e2a — one runbook per framework. An AI agent with a real email address: verified inbound webhooks, in-thread replies, and a human approval gate before it sends.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages