# EmailMate — agent + SDK guide EmailMate is SES email infrastructure. REST for apps, MCP for agents, dashboard for humans. | Surface | URL | |---------|-----| | REST | `https://www.emailmate.dev/v1` | | Human docs | https://www.emailmate.dev/docs | | OpenAPI / Scalar | https://www.emailmate.dev/v1/openapi.json · `/api-reference` | | MCP (send rails) | `https://mcp.emailmate.dev/mcp` | | MCP (inbox) | `https://mcp.emailmate.dev/mcp?sets=inbound` | | This guide | https://www.emailmate.dev/llms.txt (also `AGENTS.md` in npm `emailmate`) | Auth: `Authorization: Bearer ` (`em_…`; legacy `re_` / `ml_` still resolve). ## SDK (TypeScript) ```bash npm install emailmate # bun add emailmate ``` ```ts import { EmailMate } from "emailmate"; const em = new EmailMate(process.env.EMAILMATE_API_KEY!); await em.emails.send({ from: "Acme ", to: "user@example.com", subject: "Welcome", html: "

You're in.

", text: "You're in.", }); ``` Coming from Resend: `import { Resend } from "emailmate"` — same `emails.send` shape. Resources: `emails` · `domains` · `inboxes` · `apiKeys` · `templates` · `webhooks` · `audiences` · `contacts` · `segments` · `broadcasts` · `newsletters` · `outreach` · `suppressions` · `gdpr`. React Email: `import { renderTemplate } from "emailmate/templates"`. ## Send ``` POST /v1/emails { from, to, subject, html, text, bucket? } ``` - Always include a `text` part with `html`. - Security mail (OTP, verify, password reset, receipts): **omit `bucket`**. - Marketing / digests: set `bucket` (e.g. `"marketing"`). Opt-out + List-Unsubscribe are enforced. - `from` must be on a **verified domain you own**. Domain-scoped keys fail closed on mismatch. - Batch: `POST /v1/emails/batch` (array, max 100). - Template: `POST /v1/emails` with `{ template, data, to, subject? }`. - List / get: `GET /v1/emails` · `GET /v1/emails/:id`. - `PATCH /emails/:id` and `POST /emails/:id/cancel` return **501** — v1 sends are immediate. ## Inbox Named mailbox on **your** verified domain. REST `id` is the address (`agent@acme.com`), not an opaque key. Catch-all is receive-only. ``` POST /v1/domains/:id/inbound enable Cloudflare Email Routing catch-all POST /v1/inboxes { username?, display_name?, domain } GET /v1/inboxes/:id/threads POST /v1/inboxes/:id/messages POST /v1/inboxes/:id/messages/:mid/reply ``` ```ts const box = await em.inboxes.create({ domain: "acme.com", username: "agent" }); await em.inboxes.messages.send(box.id, { to, subject, text }); ``` ## Domains and keys ``` POST /v1/domains { name } then publish DNS; poll GET until status=verified POST /v1/api-keys prefer domain-scoped sending_access ``` ## Webhooks HTTPS only. HMAC-SHA256 in `X-EmailMate-Signature`. Secret (`whsec_…`) shown once on create. ``` POST /v1/webhooks GET /v1/webhooks GET /v1/webhooks/:id PATCH /v1/webhooks/:id DELETE /v1/webhooks/:id POST /v1/webhooks/:id/test ``` Events include `email.delivered` · `email.bounced` · `email.complained` · `email.received`. ## Suppressions ``` GET /v1/suppression POST /v1/suppression { email, reason? } DELETE /v1/suppression/:id POST /v1/suppressions/sync { email, preferences } // per-bucket prefs from your settings UI ``` ```ts await em.suppressions.sync({ email: user.email, preferences: { marketing: false, weeklyDigest: true }, }); ``` ## Errors ```json { "statusCode": 401, "name": "authentication_error", "message": "…" } ``` | status | name | cause | |--------|------|-------| | 401 | authentication_error | Bad or missing Bearer | | 403 | authorization_error | Domain lock or unverified From | | 422 | validation_error | Bad body | | 429 | rate_limit_error | Honor `Retry-After` | | 501 | not_implemented | Scheduled update / cancel | Retry only **5xx / 429**. Do not retry 2xx or other 4xx. ## Limits Free 3,000/mo · 100/day · 3 domains. Pro $10 / 50k. Scale $40 / 100k. Caps pause sending (no overage). | Plan | Default | Agent / MCP key | |------|---------|-----------------| | free | 2/s · 100/h · 500/d | 1/s · 50/h · 200/d | | pro | 10/s · 1k/h · 20k/d | 5/s · 500/h · 5k/d | | Scale | 20/s · 5k/h · 50k/d | 10/s · 2k/h · 20k/d | BYOK SES: dashboard → Settings → SES. You pay AWS. ## Rules 1. Always send `text` with `html`. 2. Security mail: no `bucket`. Everything else: set a `bucket`. 3. Send only from verified domains; prefer domain-scoped keys. 4. Never log or commit the API key. 5. Inbox MCP is opt-in (`?sets=inbound`). Default MCP is send rails only.