wzrdmail logowzrdmail docs

Migrate from AgentMail

wzrdmail is shape-compatible with AgentMail v0. Code written against AgentMail ports by changing a base URL and a key prefix — no schema change, no handler rewrite. This page is the cutover contract used by Air; it applies to any AgentMail integration.

What stays the same

What changes

AgentMailwzrdmail
https://api.agentmail.to/v0https://api.wzrd.tech/v0
AGENTMAIL_API_KEYWZRDMAIL_API_KEY
AgentMail key prefixwm_live_… / wm_test_…
Authorization: Bearer <key>same — or x-api-key: wm_…
whsec_ secret from AgentMailnew whsec_ secret returned once by POST /v0/webhooks
https://mcp.agentmail.to/mcphttps://mcp.mail.wzrd.tech/mcp
agentmail CLIwzrdmail (alias wm)
AgentMailClient (TS) / AgentMail (Python)WzrdMailClient (npm i wzrdmail) / WzrdMail (pip install wzrdmail)
official/email/agentmail skillplugins/agents skills (send-email, check-email, manage-inboxes, …)

Cutover steps

1. Provision inboxes

One pod per tenant: POST /v0/pods with { "client_id": "<user_id>" } (idempotent — a retry returns the same pod; GET /v0/pods lists them, DELETE /v0/pods/{pod_id} retires the pod and its inboxes). Then POST /v0/pods/{pod_id}/inboxes (or POST /v0/inboxes with "pod_id") with { "username", "client_id" }. Inboxes land on @wzrd.tech, which is pre-verified; re-runs with the same client_id return the existing inbox.

2. Register one webhook at your existing endpoint

POST /v0/webhooks
{ "url": "https://<your-app>/api/inbound/email", "event_types": ["message.received"], "pod_ids": ["pod_…"], "client_id": "inbound-v1" }

The response carries secret (whsec_…) once. Store it as WZRDMAIL_WEBHOOK_SECRET and hand it to your existing Svix verifier — the verification code does not change. Omit pod_ids to receive every pod in the organization.

3. Mint draft-only keys for sandboxes

Where AgentMail gave an agent a scoped key that could draft but not send:

POST /v0/api-keys
{ "name": "box-<user>", "inbox_id": "<user>@wzrd.tech", "permissions": ["read", "drafts"] }

The key sees only that inbox. create_draft / POST …/drafts succeed; …/messages/send, …/drafts/{id}/send, replies, forwards, POST /v0/inboxes, and every other inbox return 403 forbidden. Keep a send key in the control plane that reviews and sends drafts.

4. Swap the env vars

AGENTMAIL_API_KEY → WZRDMAIL_API_KEY, base URL → https://api.wzrd.tech, webhook secret → the new whsec_. If you gate the switch behind a flag (e.g. MAIL_PROVIDER=wzrdmail), both providers can run side by side until validation passes.

5. Repoint the agent's MCP and skills

Replace the AgentMail MCP entry with:

{ "mcpServers": { "wzrdmail": { "type": "http", "url": "https://mcp.mail.wzrd.tech/mcp", "headers": { "x-api-key": "${WZRDMAIL_API_KEY}" } } } }

Tool names are list_inboxes, list_messages, get_message, send_message, reply_to_message, reply_all_to_message, forward_message, list_threads, get_thread, search_threads, create_draft, update_draft, send_draft, get_attachment, and more (see the Integrations page). Install the native plugin in place of the AgentMail skill.

6. Verify

Send from a wzrdmail inbox, reply externally, and confirm your endpoint receives message.received with a valid signature and an extracted_text field. Then, with the draft-only key, confirm create_draft succeeds and send_draft returns 403.

Inbox-scoped API keys

POST /v0/api-keys accepts an optional inbox_id alongside pod_id and permissions:

Lists receive/block alias

POST   /v0/inboxes/{inbox_id}/lists/receive/block          { "pattern": "spam@example.com" }   # or { "entry": … } / { "address": … } / { "domain": "junk.example" }
GET    /v0/inboxes/{inbox_id}/lists/receive/block
DELETE /v0/inboxes/{inbox_id}/lists/receive/block/{entry}   # entry = lst_… id or the exact pattern

Rows are identical to POST /v0/inboxes/{inbox_id}/lists with { "kind": "block", "pattern" }. Blocked senders emit message.rejected instead of message.received.

Known divergences