Skip to content
machinemail

Switching from AgentMail

MachineMail serves AgentMail's /v0 API. Change the base URL and the API key, keep your code and the AgentMail SDKs, and pick up trust reports, catch-all and ephemeral inboxes.

MachineMail implements AgentMail’s /v0 API. Wherever AgentMail defines a path, a field name or an enum value, MachineMail uses the same one. Switching is a base URL and an API key.

The change

  1. Get a MachineMail key

    Sign up at machinemail.dev/signup and copy the API key the dashboard shows after your first sign-in. Keys start with mm_live_. See the Quickstart.

  2. Point your client at MachineMail

    mail.ts
    import { AgentMailClient } from 'agentmail';
    
    const mail = new AgentMailClient({
      apiKey: process.env.MACHINEMAIL_API_KEY,
      baseUrl: 'https://api.machinemail.dev', // the only change
    });
    
    const inbox = await mail.inboxes.create({ username: 'support-agent', displayName: 'Support agent' });
    await mail.inboxes.messages.send(inbox.inboxId, {
      to: ['ada@example.com'],
      subject: 'Hello',
      text: 'Sent through the AgentMail SDK, served by MachineMail.',
    });
    mail.py
    import os
    from agentmail import AgentMail
    
    mail = AgentMail(
        api_key=os.environ["MACHINEMAIL_API_KEY"],
        base_url="https://api.machinemail.dev",  # the only change
    )
    
    inbox = mail.inboxes.create(username="support-agent", display_name="Support agent")
    mail.inboxes.messages.send(
        inbox.inbox_id,
        to=["ada@example.com"],
        subject="Hello",
        text="Sent through the AgentMail SDK, served by MachineMail.",
    )
    Terminal
    # Before
    curl https://api.agentmail.to/v0/inboxes -H "Authorization: Bearer $AGENTMAIL_API_KEY"
    
    # After
    curl https://api.machinemail.dev/v0/inboxes -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
  3. Update webhook verification

    MachineMail signs webhooks with its own X-MachineMail-Signature header. If you verify AgentMail webhooks with Svix, replace that check. See Verify webhook signatures.

  4. Update your MCP config

    If your agent uses MCP, point it at https://mcp.machinemail.dev/mcp with the MachineMail key as a Bearer token. Tool names differ from AgentMail’s MCP server. See MCP server and MCP tools.

Note

MachineMail’s test suite runs the official agentmail npm package against a real MachineMail server, covering inboxes, messages (send, reply, reply-all, forward, labels, delete), threads, drafts, webhooks and domains. The Python SDK calls the same paths, but it is not part of that suite.

What carries over

AgentMail featureOn MachineMail
Inboxes: create, list, get, update, delete, searchSame paths. inbox_id is the email address, as on AgentMail.
Messages: list, get, send, reply, reply-all, forward, update labels, delete, raw, attachments, batch-get, batch-update, searchSame paths and fields.
Threads, at /v0/threads and /v0/inboxes/{inbox_id}/threadsSame.
Drafts, including send_at schedulingSame.
Webhooks: create, list, get, update, deleteSame, with a different signature header.
Domains: create, list, get, update, delete, verify, zone-fileSame, plus MachineMail fields.
Lists at /v0/lists/{direction}/{type}, also under pods and inboxesSame.
Pods, and pod-nested routes under /v0/pods/{pod_id}/...Same.
API keys with AgentMail permission names, at org, pod and inbox levelSame.
GET /v0/organizations, GET /v0/auth/meSame, plus usage counters.
Metrics: /v0/metrics/events, /usage, /ratesSame paths, with MachineMail event types.
WebSocket at /v0/ws with subscribe messagesSame protocol.
client_id idempotency on createsSame.

What MachineMail adds

These are extra fields and endpoints. AgentMail clients ignore what they do not know, so they do not break anything.

  • trust on every inbound message, plus trust_level on list items and GET /v0/inboxes/{inbox_id}/messages/{message_id}/trust. See Trust.
  • A QUARANTINE label. Mail that tries to instruct your agent, or looks like phishing, gets QUARANTINE instead of INBOX and a message.received.quarantined event. Lists hide it unless you pass include_quarantine=true.
  • Ephemeral inboxes with ttl_seconds, ephemeral and expires_at, and an inbox.expired event when they are deleted.
  • Catch-all domains with catch_all, catch_all_ttl_seconds and inbound_enabled on domains, plus DNS host detection and Domain Connect. See Domains.
  • Webhook delivery log: GET /v0/webhooks/{webhook_id}/deliveries, replay and test.
  • An event log and SSE stream: GET /v0/events and GET /v0/events/stream.
  • Suppressions at /v0/suppressions, filled automatically from bounces and complaints.
  • Export at GET /v0/inboxes/{inbox_id}/export?format=mbox|jsonl.
  • Agent self-registration through auth.md.

In the TypeScript SDK, extension fields come through untouched and in snake_case, so read them with a cast:

trust.ts
const message = await mail.inboxes.messages.get(inboxId, messageId);
const trust = (message as any).trust; // { level, score, content: { text }, reasons, ... }
if (trust?.level === 'quarantined') return;

What is different

AreaDifference
Error bodies{"error": {"type", "message", "param"}} with the matching HTTP status. type is one of not_found, validation, unauthorized, forbidden, rate_limited, conflict, internal, not_implemented. See Errors.
Webhook signaturesX-MachineMail-Signature: t=<unix>,v1=<hex>, an HMAC-SHA256 of <t>.<body> with the webhook secret.
Webhook and stream bodiesAgentMail’s shape (type: "event", event_type, event_id, and the message, thread or send objects at the top level) plus a data object holding the same payload and a timestamp.
Event typesAdds message.received.quarantined, inbox.created, inbox.expired, draft.sent and webhook.test. There is no message.opened and there are no calendar events.
Hidden labelsList calls hide SPAM, BLOCKED, TRASH, UNAUTHENTICATED and QUARANTINE unless you pass the matching include_* flag.
PermissionsAgentMail’s permission names, plus inbound_raw for POST /inbound/raw.
Agent sign-up/v0/agent/sign-up, /v0/agent/human and /v0/agent/verify are not available. Agents register through /auth.md instead.
Inbox event logGET /v0/inboxes/{inbox_id}/events returns MachineMail’s event log for that inbox, not AgentMail’s label-event log.

Not available yet

These AgentMail endpoints return 404 on MachineMail:

  • Apps and accounts (/v0/apps, /v0/accounts and the inbox and pod variants)
  • Calendar (/v0/inboxes/{inbox_id}/calendar/...)
  • Custom webhook headers (.../webhooks/{webhook_id}/headers)
  • Hosted domain setup links (/v0/domains/{domain_id}/setup-link)
  • POST /v0/inboxes/{inbox_id}/authorize
  • Attachment downloads through threads and drafts (/threads/{thread_id}/attachments/{attachment_id}, /drafts/{draft_id}/attachments/{attachment_id}). Fetch attachments through the message instead.

The AgentMail compatibility reference lists every operation.

Moving existing mail

There is no importer. Your AgentMail data, and addresses on AgentMail’s own domain, stay with AgentMail. A practical migration:

  1. Create the new inboxes on MachineMail, on machinemail.dev or on your own domain.
  2. If you use a custom domain, add it to MachineMail and switch its MX, SPF and DKIM records to the ones MachineMail gives you. See Set up a custom domain.
  3. Recreate your webhooks. Each gets a new secret.
  4. Keep the old AgentMail inboxes readable until in-flight threads are finished.

Next