AgentMail compatibility · MachineMail Docs
The rule
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. For the products rather than the API, see MachineMail vs AgentMail.
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 across to, cc and bcc, each address counted once; more is a 400 validation error. See Limits. |
| Authentication results | authentication_results withholds SPF unless its domain aligns with the sender, and can report verdicts sealed by a trusted forwarder (arc_sealer) |
Same field, values and domain keys, and the same withholding: SPF only when its domain aligns with the sender, DKIM and DMARC only when From names one domain. The verdicts are the ones recorded by the receiving provider or MachineMail’s receiving server. ARC results are not used, so arc_sealer is not sent. |
| Organization | billing and limit fields | domain_limit is the plan’s custom-domain allowance. inbox_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 |
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 | 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, 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. |
| Webhook custom headers | headers on PATCH /v0/webhooks/{webhook_id} replaces or clears the whole set (AgentMail’s /headers endpoints, also served, merge). The webhook object lists header_names and headers_updated_at; values are never returned. |
| Suppressions | /v0/suppressions: addresses that bounced, complained or were added by hand. |
| Export | `GET /v0/inboxes/{inbox_id}/export?format=mbox |
| 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. |