Common patterns for agent email

Copy-paste recipes for the most common MultiMail integrations. Polling, webhooks, approval queues, and more.



Pattern 1

Polling for new emails

Poll GET /v1/mailboxes/:id/emails with status=unread on an interval. Use since_id to avoid re-processing emails you've already seen.

Python
import requests, time API_KEY = "mm_live_..." MAILBOX = "01JMXK9F2A..." BASE = "https://api.multimail.dev" headers = {"Authorization": f"Bearer {API_KEY}"} last_id = None while True: params = {"status": "unread"} if last_id: params["since_id"] = last_id resp = requests.get( f"{BASE}/v1/mailboxes/{MAILBOX}/emails", headers=headers, params=params ) emails = resp.json()["emails"] for email in emails: process(email) last_id = email["id"] time.sleep(30)
curl
# One-shot poll for unread emails curl "https://api.multimail.dev/v1/mailboxes/01JMXK.../emails?status=unread" \ -H "Authorization: Bearer mm_live_..." # Incremental: only emails after a known ID curl "https://api.multimail.dev/v1/mailboxes/01JMXK.../emails?status=unread&since_id=em_4bNx..." \ -H "Authorization: Bearer mm_live_..."

Pattern 2

Webhook-driven inbox

Register a webhook to receive a POST when email arrives. No polling, no delays. Webhooks include an HMAC-SHA256 signature for verification.

create webhook
curl -X POST https://api.multimail.dev/v1/webhooks \ -H "Authorization: Bearer mm_live_..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.com/hooks/email", "events": ["email.received"], "mailbox_id": "01JMXK9F2A..." }' # Response includes signing_secret for HMAC verification

When an email arrives, MultiMail sends this payload to your URL:

webhook payload
{ "event": "email.received", "timestamp": "2026-03-09T14:22:00.000Z", "data": { "email_id": "em_4bNx8kT2wRm6pJcQ9vYs", "from": "[email protected]", "to": ["[email protected]"], "subject": "Need help with billing", "direction": "inbound", "has_attachments": false } } # Header: X-MultiMail-Signature: sha256=a1b2c3...

Use the email_id from the payload to fetch the full email with GET /v1/mailboxes/:id/emails/:email_id. Failed deliveries retry up to 3 times with exponential backoff.


Pattern 3

Approval queue flow

In gated_send mode, outbound emails return 202 and queue for human review. The operator approves or rejects via API or dashboard. Do not retry on 202 -- the email is queued, not failed.

agent sends (gets 202)
curl -X POST https://api.multimail.dev/v1/mailboxes/01JMXK.../send \ -H "Authorization: Bearer mm_live_..." \ -H "Content-Type: application/json" \ -d '{ "to": ["[email protected]"], "subject": "Invoice #1042", "markdown": "Attached is your invoice for March.\n\nTotal: $2,400.00" }' # Response: 202 Accepted # {"id": "em_7kPq...", "status": "pending_send_approval", ...}
operator lists pending emails
curl "https://api.multimail.dev/v1/oversight/pending" \ -H "Authorization: Bearer mm_live_oversight_key..." # Returns array of emails awaiting approval
operator approves
curl -X POST https://api.multimail.dev/v1/oversight/decide \ -H "Authorization: Bearer mm_live_oversight_key..." \ -H "Content-Type: application/json" \ -d '{ "email_id": "em_7kPq2xR9vBn3mJfL4wYs", "action": "approve" }' # To reject instead: # {"email_id": "em_7kPq...", "action": "reject", "reason": "Wrong amount"}

Pattern 4

Contact management

Build an address book from incoming emails. Search contacts before sending to avoid duplicates and personalize outreach.

add a contact from an incoming email
curl -X POST https://api.multimail.dev/v1/contacts \ -H "Authorization: Bearer mm_live_..." \ -H "Content-Type: application/json" \ -d '{ "name": "Alice Chen", "email": "[email protected]", "tags": ["client", "enterprise"] }'
search before sending
curl "https://api.multimail.dev/v1/contacts?query=alice" \ -H "Authorization: Bearer mm_live_..." # Response: # { # "contacts": [ # { # "id": "ct_8mNx3kR7vBm2nJeL", # "name": "Alice Chen", # "email": "[email protected]", # "tags": ["client", "enterprise"] # } # ] # }

Pattern 5

Thread tracking

Every email includes a thread_id. Use it to follow full conversations, check for unanswered messages, and reply in context.

get full thread
curl "https://api.multimail.dev/v1/mailboxes/01JMXK.../threads/th_9aWx3kR7vBm2nJeL5wZt" \ -H "Authorization: Bearer mm_live_..."
response
{ "thread_id": "th_9aWx3kR7vBm2nJeL5wZt", "participants": ["[email protected]", "[email protected]"], "message_count": 4, "last_activity": "2026-03-09T15:30:00.000Z", "has_unanswered_inbound": true, "emails": [ { "id": "em_7kPq...", "from": "[email protected]", "subject": "Weekly summary", "markdown": "## Completed\n\n- Deployed v2.1...", "direction": "outbound", "received_at": "2026-03-09T10:00:00.000Z" }, { "id": "em_4bNx...", "from": "[email protected]", "subject": "Re: Weekly summary", "markdown": "Can you share the deploy logs?", "direction": "inbound", "received_at": "2026-03-09T14:22:00.000Z" } ] }
reply in thread
# Threading headers are set automatically when you reply curl -X POST https://api.multimail.dev/v1/mailboxes/01JMXK.../reply/em_4bNx8kT2wRm6pJcQ9vYs \ -H "Authorization: Bearer mm_live_..." \ -H "Content-Type: application/json" \ -d '{"markdown": "Here are the deploy logs:\n\n```\nv2.1 deployed at 09:42 UTC\nAll health checks passing\n```"}'

Pattern 6

Oversight mode upgrade

Agents start in a restrictive mode and request more autonomy as they build trust. The upgrade flow: agent requests, operator receives a code via email, agent applies the code.

step 1: agent requests upgrade
curl -X POST https://api.multimail.dev/v1/mailboxes/01JMXK.../request-upgrade \ -H "Authorization: Bearer mm_live_..." \ -H "Content-Type: application/json" \ -d '{"target_mode": "monitored"}' # Response: 200 OK # {"message": "Upgrade code sent to operator"} # Operator receives email with one-time code
step 2: agent applies code from operator
curl -X POST https://api.multimail.dev/v1/mailboxes/01JMXK.../upgrade \ -H "Authorization: Bearer mm_live_..." \ -H "Content-Type: application/json" \ -d '{"code": "A7X-29K"}' # Response: 200 OK # {"oversight_mode": "monitored"}

Codes expire after 24 hours and can only be used once. Downgrades don't need a code -- use PATCH /v1/mailboxes/:id to move to a more restrictive mode at any time.

downgrade (no code needed)
curl -X PATCH https://api.multimail.dev/v1/mailboxes/01JMXK... \ -H "Authorization: Bearer mm_live_..." \ -H "Content-Type: application/json" \ -d '{"oversight_mode": "gated_send"}'

Full API reference · MCP server quickstart · REST API quickstart · [email protected]