# emitd — AI-Readable API Reference emitd is a developer-first transactional email API built on Cloudflare's edge (Rust/WASM Workers) with AWS SES as the sending backbone. One HTTPS call sends an email; a native MCP endpoint gives agents the same power with zero glue code. Full machine-readable spec: /openapi.yaml (OpenAPI 3.1, all endpoints + schemas). ## Quick Start ```bash curl -X POST https://api.emitd.com/v1/email \ -H "Authorization: Bearer esk_live_..." \ -H "Content-Type: application/json" \ -d '{ "from": "hello@yourdomain.com", "to": ["user@example.com"], "subject": "Welcome!", "html_body": "

Hello from emitd!

" }' ``` Response: `{"message_id": "...", "status": "queued"}` ## Authentication Every request sends an API key as a Bearer token: ``` Authorization: Bearer esk_live_... ``` Keys are created in the console. Two permission levels: `full_access` and `sending_access`; a key can optionally be scoped to a single sending domain. ## Base URL `https://api.emitd.com` ## Core endpoints ### Email - `POST /v1/email` — send one message. Required: `from`, plus at least one of `to`/`cc`/`bcc` (50 recipients max), and either `subject` + `html_body`/ `text_body` or `template_alias` (+ `template_model`). Optional: `reply_to`, `tags`, `scheduled_at` (epoch ms, future), `attachments[]` (`filename` + `content_type` + base64 `content` or `r2_key`), `message_stream` (`outbound` default | `broadcast`). - `POST /v1/email/batch` — up to 100 messages per call, per-message results. - `PATCH /v1/email/{id}` — reschedule a scheduled message (`scheduled_at`). - `DELETE /v1/email/{id}` — cancel a scheduled message. - Idempotency: `POST /v1/email` and `/v1/email/batch` accept an `Idempotency-Key` header; a retried key replays the original result without sending or billing again. ### Messages & activity - `GET /v1/messages` / `GET /v1/messages/{id}` — delivery status per message. - `GET /v1/email/test-events` — events recorded in sandbox/test mode. ### Audience - `/v1/contacts` — CRUD, `POST /v1/contacts/batch`, CSV import/export jobs. - `/v1/audiences` — lists + members (bulk add supported). - `/v1/segments` — filter DSL over contacts (preview + count). - `/v1/broadcasts` — create, send, cancel, stats. - `/v1/automations` — multi-step programs (send / wait / branch), activate, pause, enroll. - `/v1/suppressions` — list, add, bulk add/remove, delete, instant check. ### Other - `/v1/templates` — create, list, versions; `{{ variable }}` substitution (logic-free, HTML-escaped). - `/v1/webhooks` — create, list, delete, replay. - `/v1/inbound` — received email (Cloudflare Email Routing), raw MIME in R2. - `POST /v1/attachments` — upload once, reference by `r2_key` in sends. - `GET /health` — service health. Pagination on all list endpoints: `?limit=` (default 25, max 100) + `?cursor=`. ## Event types (webhooks) `sent`, `delivery`, `delivery_delayed`, `failed`, `bounce`, `complaint`, `suppressed`, `open`, `click`, `scheduled`, `cancelled`. Payload shape: ```json { "type": "bounce", "message_id": "msg_2h8Kd0Rk9Qa", "ses_message_id": "0100018f...", "recipients": ["user@example.com"], "timestamp": 1783386190864 } ``` Signature: `X-Webhook-Signature: sha256=` — HMAC-SHA256 of the raw body with your webhook secret (returned exactly once at creation). Non-2xx responses are retried with exponential backoff. ## Errors Stable snake_case codes: `unauthorized` (401), `validation_failed` (422, with `detail.code` such as `no_body` / `invalid_recipient`), `rate_limit_exceeded` (429), `quota_exceeded` (429). Suppressed recipients return `200 {"status": "suppressed"}` rather than an error. ## Plans & quotas | Plan | $/mo | Emails/month | Emails/day | |----------|------|--------------|------------| | Free | 0 | 10,000 | 500 | | Starter | 15 | 50,000 | 5,000 | | Growth | 75 | 500,000 | 50,000 | | Business | 150 | 1,000,000 | 100,000 | | Scale | 300 | 2,500,000 | 250,000 | Paid plans meter overage at $0.40 per 1,000 emails (opt-in). ## Sandbox mode Send to reserved test recipients to simulate outcomes deterministically — no real email, every webhook fires: - `delivered@sandbox.relay.dev` → simulated delivery - `bounced@sandbox.relay.dev` → hard bounce + auto-suppression - `complained@sandbox.relay.dev` → complaint + auto-suppression - `suppressed@sandbox.relay.dev` → blocked by suppression list `+labels` work (`bounced+ci@...`); inspect results via `GET /v1/email/test-events`. ## MCP (Model Context Protocol) emitd hosts a native MCP server — stateless JSON-RPC 2.0 at `POST /mcp` on the API origin, authenticated with the same Bearer API key: ``` POST /mcp {"jsonrpc": "2.0", "method": "tools/call", "params": {"name": "send_email", "arguments": {...}}, "id": 1} ``` Tools: `send_email`, `list_messages`, `get_message`, `list_domains`, `list_templates`, `get_deliverability`. Every tool runs inside the same quota, rate-limit, suppression, and key-permission guardrails as the REST API. ## SDKs SDKs for nine languages (Node `@emitd/sdk`, Python `emitd`, Go, Ruby, PHP, Java, C#, Rust, Elixir) plus an `emitd` CLI are generated from the OpenAPI spec. The raw HTTPS API above is always sufficient — no SDK required.