AgentMail compatibility
What works the same as AgentMail's /v0 API, what differs, what is missing, and what MachineMail adds.
MachineMail’s /v0 API is wire-compatible with AgentMail’s. Code written against AgentMail’s API or SDKs works against MachineMail by changing the base URL and the API key.
The rule
Wherever AgentMail’s published API defines a path, an enum value or a field name, MachineMail uses exactly that. MachineMail-only features use their own names and never change the meaning of an AgentMail field.
In practice:
- JSON is snake_case:
organization_id,api_key_id,pod_id,inbox_id,thread_id,message_id,draft_id,webhook_id,domain_id. inbox_idis the email address, as in AgentMail.- Pagination is
limitandpage_token; responses carrycount,limitandnext_page_token. client_idon create is an idempotency key: the sameclient_idreturns the existing resource.- Enums match AgentMail: domain
status(NOT_STARTED,PENDING,INVALID,FAILED,VERIFYING,VERIFIED), DNS recordstatus(MISSING,INVALID,VALID) andtype(TXT,CNAME,MX), inboxstatus(active,paused), draftsend_status(scheduled,sending,failed), listdirection(send,receive) andtype(allow,block). - API key permissions use AgentMail’s names (
inbox_read,message_send, …) and its object-of-booleans format.
Switching from AgentMail
Create an API key in the MachineMail dashboard (or let your agent onboard itself through
https://api.machinemail.dev/auth.md).Point your client at
https://api.machinemail.dev. With AgentMail’s SDKs, set the base URL option to that value and the API key to yourmm_live_...key.Use addresses on
machinemail.dev, or add your own domain withPOST /v0/domains.
Supported
| Area | Endpoints |
|---|---|
| Inboxes | list, create, get, update (including pause), delete, search |
| Messages | list, get, send, reply, reply-all, forward, update labels, delete, raw, attachments, batch-get, batch-update, search |
| Threads | list, get, update labels, delete, search; at /v0/threads and /v0/inboxes/{inbox_id}/threads |
| Drafts | list, create, get, update, delete, send; scheduled sends with send_at |
| Domains | list, create, get, update, delete, verify, zone-file |
| Webhooks | list, create, get, update, delete |
| Lists | allow and block lists at /v0/lists, /v0/pods/{pod_id}/lists and /v0/inboxes/{inbox_id}/lists |
| Pods | list, create, get, update, delete |
| API keys | list, create, get, update, delete; organization, pod and inbox keys |
| Organization | GET /v0/organizations |
| Metrics | /v0/metrics/events, /v0/metrics/usage, /v0/metrics/rates |
| Scoped routes | every resource under /v0/pods/{pod_id}/...; webhooks, lists, metrics, events and API keys under /v0/inboxes/{inbox_id}/... |
Differences
| Topic | AgentMail | MachineMail |
|---|---|---|
| Error body | AgentMail’s error shapes | One envelope on every /v0 route: {"error": {"type", "message", "param"?}}. See Errors. |
| Agent sign-up | /v0/agent/sign-up, /v0/agent/human, /v0/agent/verify | The auth.md protocol: POST /agent/identity, POST /agent/identity/claim, POST /oauth2/token. See Authentication. |
| Webhook signatures | AgentMail’s own signing scheme | X-MachineMail-Signature: t=...,v1=... (HMAC-SHA256 over <t>.<body>) plus X-MachineMail-Event, -Event-Id, -Delivery-Id. See Webhook events. |
| Webhook body | event payload | Same keys at the top level, plus type: "event", event_id, event_type, timestamp, and the payload again under data. |
| Creating keys | body may carry pod_id / inbox_id | The route sets the scope: POST /v0/pods/{pod_id}/api-keys, POST /v0/inboxes/{inbox_id}/api-keys. pod_id and inbox_id in the body are ignored. |
| Status codes | 200 on create (per AgentMail’s spec) | 201 on create and on sends, 200 when a client_id matches. Deletes return {"ok": true}. |
| Inbox events | GET /v0/inboxes/{inbox_id}/events is a label-event log | The same path returns MachineMail’s event log narrowed to the inbox. |
| Labels | System labels are uppercase: INBOX, SENT, UNREAD, SPAM, BLOCKED, UNAUTHENTICATED, QUARANTINE, TRASH. Other labels are free strings. | |
| Recipients | no published per-message cap | At most 50 recipients per message (deployment default) across to, cc and bcc, each address counted once; more is a 400 validation error. See Limits. |
| Organization | billing and limit fields | inbox_limit, domain_limit, billing and authentication fields are not returned. Inbox count is not a quota. |
Not available
| AgentMail feature | Status |
|---|---|
Calendar (/v0/inboxes/{inbox_id}/calendar/...) | Out of scope |
Apps and accounts (/v0/apps, /v0/accounts, .../accounts) | Out of scope |
Custom webhook headers (.../webhooks/{webhook_id}/headers) | Not yet; headers on create is ignored |
Domain setup-link | Not planned; MachineMail offers Domain Connect instead (GET /v0/domains/{domain_id}/domain-connect) |
POST /v0/inboxes/{inbox_id}/authorize | Not yet |
Draft and thread attachment routes (/drafts/{draft_id}/attachments/..., /threads/{thread_id}/attachments/...) | Not yet; use the message attachment route |
Open tracking (track_opens, message.opened) | Not available; track_opens is ignored |
| Fields accepted but ignored | forward_of and reply_all on draft create, attachment and label changes on draft update, quarantine_threshold on inboxes, subdomains_enabled, tracking_enabled and allow_conflicting_provider on domains |
MachineMail extensions
| Extension | Where |
|---|---|
| Trust reports | trust on every inbound message, trust_level on list items, GET /v0/inboxes/{inbox_id}/messages/{message_id}/trust. Verdicts verified, known, unknown, suspicious, quarantined, with SPF/DKIM/DMARC results, classifier scores for spam, phishing and prompt injection, link and attachment analysis, and an agent-safe trust.content.text. |
| Quarantine | Mail that looks like prompt injection or phishing gets the QUARANTINE label and the message.received.quarantined event, and is hidden from lists unless asked for. |
| Ephemeral inboxes | ttl_seconds, ephemeral, expires_at on inboxes; inbox.created and inbox.expired events. |
| Catch-all domains | catch_all and catch_all_ttl_seconds on domains: mail to an unknown address creates an inbox. Also inbound_enabled, feedback_enabled, nameservers, dns_provider, dns_provider_url. |
| Domain Connect | GET /v0/domains/{domain_id}/domain-connect returns a one-click DNS setup link where the DNS provider supports it. |
| Event log and streams | GET /v0/events (with after=<event_id> cursors), GET /v0/events/{event_id}, server-sent events at GET /v0/events/stream, WebSocket at /v0/ws. |
| Delivery inspection and replay | GET /v0/webhooks/{webhook_id}/deliveries, POST .../deliveries/{delivery_id}/replay, POST /v0/webhooks/{webhook_id}/test. |
| Suppressions | /v0/suppressions: addresses that bounced, complained or were added by hand. |
| Export | GET /v0/inboxes/{inbox_id}/export?format=mbox|jsonl. |
| Extra event types | message.received.quarantined, message.received.blocked, inbox.created, inbox.expired, draft.sent, webhook.test. |
| Extra fields | direction, delivery_status, received_via and pod_id on messages; organization_id on inboxes, domains and keys. |
| Remote MCP server | /mcp, with agent-safe tools such as wait_for_message. See MCP tools. |
| Agent onboarding | auth.md (/auth.md, /agent/identity, /oauth2/token), /v0/agent-registrations. |
| Raw injection | POST /inbound/raw with the inbound_raw permission, for testing with real MIME. |
| Lists | A reply list direction, and entry_type (email or domain) on entries. |