e2a — Email for AI agents


Authenticated email gateway for AI agents. Receive emails as webhooks or via WebSocket, send emails through an HTTP API, and verify the identity of every sender — humans and other agents alike.
- Authenticated transport — SPF/DKIM verified on inbound; HMAC-signed
X-E2A-Auth-* headers on every delivery
- Two delivery modes — webhook (cloud agents) or WebSocket (local agents, no public URL needed)
- Outbound API — agents send to other agents (SMTP relay) or humans (upstream SMTP, e.g. SES, Resend)
- Human in the loop — opt-in approval gate that holds outbound mail until a reviewer approves via dashboard, magic-link email, or CLI
- CLI + SDKs — TypeScript and Python SDKs, plus a
e2a CLI for everyday agent ops
Use it
You can either use the hosted instance or self-host.
- Hosted — sign up at e2a.dev. Includes the shared
agents.e2a.dev domain for instant slug-based onboarding (no DNS setup), a dashboard, and managed deliverability.
- Self-host — see Quickstart and Deployment. Every feature works the same; the shared-domain slug shortcut just needs you to point a mail domain at your relay and set
shared_domain in config.yaml.
How it works
Human (Gmail/Outlook)
│
▼ SMTP
┌──────────────┐
│ e2a relay │ ← MX record for your agent domain points here
│ │
│ 1. Verify │ ← SPF/DKIM check on the inbound message
│ 2. Sign │ ← HMAC-signed X-E2A-Auth-* headers
│ 3. Deliver │
└──────────────┘
│
├──▶ Cloud-mode agent: HTTPS webhook POST
│
└──▶ Local-mode agent: store + WebSocket notification
│
▼
e2a listen (CLI) or client.listen() (SDK)
Inbound flow: SMTP → SPF/DKIM check → agent lookup → HMAC-sign auth headers → webhook or WebSocket delivery.
Outbound flow: API call → optional HITL hold → SMTP relay (agent-to-agent) or upstream SMTP (agent-to-human).
Quickstart
Requires Docker.
git clone https://github.com/Mnexa-AI/e2a.git
cd e2a
docker compose up -d
Postgres comes up first (migrations run automatically), then the API server, then the dashboard. Three host ports:
:8080 — HTTP API
:2525 — SMTP relay
:3000 — Dashboard (Caddy + Next.js, proxies /api/* to the API server)
Health check:
curl http://localhost:8080/api/health
# {"status":"ok"}
Open http://localhost:3000 in a browser to view the dashboard. Sign-in requires Google OAuth credentials configured in config.yaml; for an API-only smoke test you can skip the dashboard and use the bootstrap flow below.
Create your first user and API key (no OAuth required):
docker compose exec e2a e2a -config /etc/e2a/config.yaml -bootstrap-email you@example.com
# User: you@example.com (id=...)
# API key: e2a_...
Save the key — it's only shown once. Register an agent and confirm it works:
KEY=e2a_...
curl -X POST http://localhost:8080/api/v1/agents \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"slug":"my-bot","agent_mode":"local"}'
curl -H "Authorization: Bearer $KEY" http://localhost:8080/api/v1/agents
To receive real inbound mail, point a domain's MX record at your relay host:
- A:
your-domain.com → server IP
- MX:
your-domain.com → your-domain.com (priority 10)
Then register and verify the domain through the API (see Domains). Without DNS, the API still works for testing — but external email won't reach your relay.