Self-hosted, open-source email infrastructure for AI agents — an AgentMail-compatible API that gives autonomous agents their own real email inboxes they can send from, receive into, search, label, and reply to, entirely over a REST API.
Inspired by AgentMail. This is an independent, MIT-licensed reimplementation you can run on your own infrastructure.
Traditional email (Gmail, IMAP/SMTP) is built for humans: OAuth flows, per-seat pricing, rate limits, no clean programmatic identity. Agents need the opposite — spin up an inbox in milliseconds, a first-class address on the internet, two-way threaded conversations, webhooks, and search, all via an API key. OpenAgentMail provides that, self-hosted.
- Inboxes — create thousands of programmatic inboxes (
name@your-domain), each a real mailbox. - Send / receive / reply — full two-way email with RFC-correct threading.
- Threads & messages — conversation grouping,
extracted_text(quoted history stripped), attachments. - Drafts — human-in-the-loop: create → review → send.
- Labels + AI auto-labeling — classify inbound mail with natural-language rules.
- Semantic search — vector search over all messages (pgvector + embeddings).
- Webhooks — signed (HMAC-SHA256), retried event delivery.
message.received,message.sent,message.delivered, … - Realtime — Server-Sent Events stream for live agents and the console.
- Web console — Next.js + shadcn/ui with the sidebar block.
- TypeScript SDK — AgentMail-compatible client.
- Local-first — runs fully offline (MailHog relay, local embeddings). Flip env vars to go to production.
apps/
api/ Hono REST API + inbound SMTP server + webhook worker + SSE (@oam/api)
web/ Next.js + shadcn/ui console (@oam/web)
packages/
core/ types, crypto, email parsing/threading/extraction, AI (@oam/core)
db/ Drizzle schema + Postgres/pgvector client + migrations (@oam/db)
sdk/ TypeScript client SDK (@oam/sdk)
Data model mirrors AgentMail: organization → inbox → thread → message → attachment,
plus drafts, labels, webhooks, and vector embeddings.
cp .env.example .env
docker compose up --buildThis starts Postgres+pgvector, MailHog (outbound capture, UI at
http://localhost:8025), the API (http://localhost:3001), and the console
(http://localhost:3000). On first boot the API migrates the DB and seeds a root
API key — copy it from the api container logs:
docker compose logs api | grep "am_live_"Open the console at http://localhost:3000, go to Settings, and paste the key.
Requires Bun 1.3+, Node 20+ (for tsx/drizzle-kit), and a
pgvector Postgres (e.g. docker compose up -d postgres mailhog). Tasks are
orchestrated with Turborepo.
bun install
cp .env.example .env
bun db:generate # generate SQL migrations from the schema
bun db:migrate # apply them (also creates the pgvector extension)
bun db:seed # prints your root API key — save it
bun dev # turbo: api on :3001, web on :3000, inbound SMTP on :2525export KEY=am_live_...
# Create an inbox
curl -X POST localhost:3001/v0/inboxes -H "authorization: Bearer $KEY" \
-H 'content-type: application/json' -d '{"username":"support"}'
# Send a message
curl -X POST localhost:3001/v0/inboxes/<inbox_id>/messages/send \
-H "authorization: Bearer $KEY" -H 'content-type: application/json' \
-d '{"to":"someone@example.com","subject":"Hi","text":"Hello from my agent"}'Or with the SDK (see examples/agent.ts):
import { AgentMailClient } from "@oam/sdk";
const client = new AgentMailClient({ apiKey: process.env.AGENTMAIL_API_KEY! });
const inbox = await client.inboxes.create({ username: "support" });
await client.inboxes.messages.send(inbox.inbox_id, {
to: "customer@example.com",
subject: "Welcome",
text: "An AI agent will reply here.",
});The inbound SMTP server listens on :2525. To test receiving, point a sender
at it (e.g. swaks --server localhost:2525 --to support@agentmail.local) or
configure your domain's MX record to your host in production.
| Resource | Endpoints |
|---|---|
| Inboxes | POST/GET /v0/inboxes, GET/PATCH/DELETE /v0/inboxes/:id, POST /v0/inboxes/:id/api-keys |
| Messages | GET /…/messages, GET /…/messages/:id, POST /…/messages/send, POST /…/messages/:id/reply, PATCH /…/messages/:id (labels) |
| Threads | GET /…/threads, GET /…/threads/:id, GET /v0/threads (org-wide) |
| Drafts | POST/GET /…/drafts, GET/DELETE /…/drafts/:id, POST /…/drafts/:id/send |
| Attachments | GET /…/attachments/:id |
| Labels | GET/POST /v0/labels, DELETE /v0/labels/:id |
| Webhooks | GET/POST /v0/webhooks, PATCH/DELETE /v0/webhooks/:id |
| Search | POST /v0/search (semantic) |
| Events | GET /v0/events/stream (SSE) |
Auth: Authorization: Bearer <api_key>. Keys are org-level or inbox-scoped.
- Resend (recommended): set
OUTBOUND_DRIVER=resend,INBOUND_DRIVER=resend,RESEND_API_KEY, verifyEMAIL_DOMAINin Resend Domains. PointAPI_BASE_URLat a public HTTPS URL, runbun run register-resend-webhook, setRESEND_WEBHOOK_SECRET. Agents use the OpenAgentMail API; Resend sends/receives mail. - SMTP fallback:
INBOUND_DRIVER=smtp+ MX to your host;OUTBOUND_DRIVER=smtp+SMTP_OUTBOUND_*. - AI: set
OPENAI_API_KEYto use real embeddings + LLM labeling/extraction (falls back to local otherwise). - Storage: set
STORAGE_DRIVER=s3withS3_*for attachment storage.
MIT