You're viewing the agent-readable version of this page. Switch to human view

MultiMail: Email for AI Agents

MultiMail gives you a real email address. Send and receive email as markdown, 15x fewer tokens than raw HTML email. Human oversight is handled for you.

OpenAPI spec: GET https://api.multimail.dev/v1/openapi.json for full machine-readable API details.

Base URL: https://api.multimail.dev

Auth: Authorization: Bearer mm_live_...

What this does for you

Verify before you communicate

Every outbound email from a MultiMail address includes a signed X-MultiMail-Identity header. The header contains a base64url-encoded JSON payload and an ECDSA P-256 signature, separated by a dot. To verify: decode both parts, fetch the public key from GET /.well-known/multimail-signing-key, and verify the signature using ECDSA with SHA-256. Identity is delivered exclusively via this signed header. No separate lookup endpoint is needed or available.

Reputation data is delivered separately via the X-MultiMail-Reputation header on every outbound email.

Getting started

1. Sign up

Web setup (recommended): After your account is approved, follow the setup link in your approval email. The guided flow walks you through account creation, activation, and client configuration.

API signup: Alternatively, create an account programmatically:

POST /v1/account
{
  "operator_name": "Greenline Studios",
  "slug": "greenline",
  "accepted_tos": true,
  "accepted_operator_agreement": true,
  "accepted_anti_spam_policy": true,
  "email_use_type": "transactional",
  "oversight_email": "ops@greenline.studio"
}

Response is { "status": "confirmation_sent", "message": "..." } — identical whether or not an account was created (anti-enumeration). Check your oversight email for an activation code, then confirm via POST /v1/confirm to activate; the confirm response includes your mailbox address (e.g. agent@greenline.multimail.dev) and your API key (shown once). Manage your account at /dashboard.

Note: operator_name is published in the signed X-MultiMail-Identity header on every outbound email and visible to email recipients.

2. Upgrade (optional)

PlanPriceMailboxesEmails/moStorage
StarterFree2200100 MB
Builder$9/mo55,00010 GB
Pro$29/mo2530,00050 GB
Scale$99/mo100150,000150 GB

See full pricing with annual discounts and subscribe →

// Stripe (card) — supports monthly or annual billing
POST /v1/billing/checkout
{ "plan": "builder", "interval": "annual" }  // builder | pro | scale

// More payment options coming soon

Returns { "url": "https://..." }. Complete payment at that URL or pass it to your operator.

3. Send an email

POST /v1/mailboxes/{mailbox_id}/send
{
  "to": ["recipient@example.com"],
  "subject": "Weekly summary",
  "markdown": "## Completed\n\n- Task A\n- Task B\n\nAll items resolved."
}

Returns 200 if sent, 202 if held for operator approval (gated mode), or 403 if the mailbox is in read_only mode.

4. Read your inbox

GET /v1/mailboxes/{mailbox_id}/emails?status=unread

Returns emails as markdown. Each email includes thread_id, from, to, subject, and attachment metadata.

5. Reply in-thread

POST /v1/mailboxes/{mailbox_id}/reply/{email_id}
{
  "markdown": "Acknowledged. I'll follow up tomorrow."
}

Oversight modes

Each mailbox has an oversight mode set via the API or by an admin. Modes form a trust ladder: start restrictive, upgrade as you earn trust.

If you receive a 202, the email is queued. Do not retry. The oversight address will be notified.

Requesting an upgrade

If your mailbox is in a restrictive mode and you need more autonomy, request an upgrade:

POST /v1/mailboxes/{mailbox_id}/request-upgrade
{ "target_mode": "gated_all" }

This emails a one-time upgrade code to your operator. Ask your operator to share it with you, then:

POST /v1/mailboxes/{mailbox_id}/upgrade
{ "code": "A7X-29K" }

Codes expire after 24 hours and can only be used once. No code is needed for downgrades. Use PATCH /v1/mailboxes/:id to move to a more restrictive mode at any time.

Handling attachments

Webhooks

Configure webhooks on your mailbox to receive push notifications instead of polling. Two webhook types are available:

Set either via POST /v1/mailboxes or PATCH /v1/mailboxes/:id. Both must use HTTPS.

Webhook payload

All webhooks deliver a POST request with a JSON body:

{
  "event": "email.received",
  "timestamp": "2026-02-26T12:00:00.000Z",
  "data": {
    "email_id": "em_abc123",
    "from": "sender@example.com",
    "to": ["agent@yourco.multimail.dev"],
    "subject": "Weekly report",
    "direction": "inbound",
    "has_attachments": false
  }
}

For email.pending_approval, the data object also includes an approval_url for one-click approve/reject.

Signature verification

Every webhook request includes an X-MultiMail-Signature header containing an HMAC-SHA256 signature of the raw request body. Verify it server-side to confirm the request came from MultiMail.

Retries and delivery history

Failed deliveries (non-2xx response) are retried up to 3 times with exponential backoff. View delivery history and debug failures via GET /v1/webhook-deliveries (requires admin scope).

If you don't use webhooks, poll GET /v1/mailboxes/:id/emails?status=unread instead.

MCP Server

If your framework supports the Model Context Protocol (MCP), use our MCP server instead of direct API calls. One config line gives you fifty email tools.

Option A: Remote server (recommended)

No install required. Connect directly to our hosted MCP server at mcp.multimail.dev. Authenticates via OAuth in the browser — paste your API key once and you're connected. Works with Claude.ai, Claude Desktop, Claude Code, and any client that supports remote MCP.

{
  "mcpServers": {
    "multimail": {
      "type": "url",
      "url": "https://mcp.multimail.dev/mcp"
    }
  }
}

Option B: Local server (stdio)

Run the server locally via npm. API key is passed as an environment variable. Works with all MCP-compatible clients. Package: @multimail/mcp-server

{
  "mcpServers": {
    "multimail": {
      "command": "npx",
      "args": ["-y", "@multimail/mcp-server"],
      "env": {
        "MULTIMAIL_API_KEY": "mm_live_...",
        "MULTIMAIL_MAILBOX_ID": "01KJ1NHN8J..."
      }
    }
  }
}

Environment variables (local server only)

Tools

list_mailboxes: List all mailboxes available to this API key. Returns each mailbox's ID, email address, oversight mode, and display name. No arguments.

send_email: Send an email with a markdown body. Supports scheduled delivery and idempotency. Args: to (string[]), subject (string), markdown (string), cc (string[], optional), bcc (string[], optional), attachments (array of {name, content_base64, content_type}, optional), idempotency_key (string, optional), send_at (ISO 8601 UTC string ending in Z, optional — schedules delivery for a future time), gate_timing ("gate_first" or "schedule_first", optional — controls whether human approval happens before or after scheduling), mailbox_id (string, optional). Returns { id, status, thread_id } where status is "pending_scan" or "scheduled". For gated mailboxes, transitions to "pending_send_approval". Do not retry or resend. Use idempotency_key to prevent duplicate sends (24h TTL).

check_inbox: List email summaries with filtering. Args: status (unread/read/archived/deleted/pending_send_approval/pending_inbound_approval/rejected/cancelled/send_failed/scheduled, optional), sender (string, partial match, optional), subject_contains (string, optional), date_after / date_before (ISO datetime, optional), direction (inbound/outbound, optional), has_attachments (boolean, optional), since_id (string, for incremental polling, optional), limit (number, max 100, optional), cursor (string, pagination cursor, optional), mailbox_id (string, optional). Returns array of { id, from, to, subject, status, received_at, has_attachments, delivered_at, bounced_at, bounce_type }. Does NOT include the email body. Call read_email for full content.

read_email: Get full email content including markdown body, attachment metadata, delivery timestamps, and tags. Args: email_id (string), mailbox_id (string, optional). Automatically marks unread emails as read. Returns delivered_at, bounced_at, bounce_type, approved_at, approved_by, and tags.

reply_email: Reply to an email in its existing thread. Threading headers set automatically. Args: email_id (string), markdown (string), cc (string[], optional), bcc (string[], optional), attachments (array, optional), idempotency_key (string, optional), mailbox_id (string, optional). Returns { id, status, thread_id }. Same scanning and approval behavior as send_email.

download_attachment: Download an email attachment. Small files (<50KB) return inline base64. Larger files return a presigned download URL valid for 1 hour. Args: email_id (string), filename (string, from read_email attachment list), mailbox_id (string, optional).

get_thread: Get all emails in a conversation thread, ordered chronologically. Args: thread_id (string, from check_inbox or read_email), mailbox_id (string, optional). Returns participants, message_count, last_activity, has_unanswered_inbound, and the full email list.

cancel_message: Cancel a pending or scheduled email. Args: email_id (string), mailbox_id (string, optional). Works on emails with status pending_scan, pending_send_approval, pending_inbound_approval, or scheduled. Returns 409 if already sent. Idempotent on already-cancelled emails.

tag_email: Set, get, or delete key-value tags on emails. Tags persist across sessions. Args: email_id (string), action (set/get/delete), tags (Record<string, string>, for set), key (string, for delete), mailbox_id (string, optional). Use for priority flags, follow-up dates, extracted data, or any agent metadata.

add_contact: Add a contact to your address book. Args: name (string), email (string), tags (string[], optional). Build contacts organically from processed messages.

search_contacts: Search address book by name or email (partial match). Args: query (string, optional). Returns matching contacts with tags. Call with no query to list all.

update_mailbox: Update mailbox settings. All fields optional — only include what you want to change. Args: mailbox_id (string, optional), display_name (string, optional), oversight_mode (string, optional), auto_cc (string, optional), auto_bcc (string, optional), forward_inbound (boolean, optional), webhook_url (string, optional), oversight_webhook_url (string, optional), signature_block (string, optional).

update_account: Update account settings. Args: name (string, optional), oversight_email (string, optional), physical_address (string, optional). Requires admin scope.

delete_mailbox: Permanently delete a mailbox and all associated data. Args: mailbox_id (string). Requires admin scope. This action cannot be undone.

resend_confirmation: Resend the operator activation email with a new code. No arguments. Rate limited to 1 request per 5 minutes. Only works for unconfirmed accounts.

activate_account: Activate your account using the code from the confirmation email. Args: code (string, e.g. "SKP-7D2-4V8"). Accepts with or without dashes. Rate limited to 5 attempts per hour.

get_account: Get account status, plan, quota used/remaining, sending enabled, and enforcement tier. No arguments. Use for self-diagnosis when sends fail.

create_mailbox: Create a new mailbox. Requires admin scope and operator email approval. Returns 202 with an approval code sent to the oversight email; resubmit with the code to complete creation.

request_upgrade: Request an oversight mode upgrade. Args: mailbox_id (string, optional), target_mode (string). Sends upgrade request to operator. Requires admin scope.

apply_upgrade: Apply an upgrade code from operator. Args: mailbox_id (string, optional), code (string).

get_usage: Check quota and usage stats. Args: period (summary/daily, optional). Returns emails sent, received, storage used, plan limits.

list_pending: List emails awaiting oversight decision. No arguments. Requires oversight or oversight_read scope.

decide_email: Approve or reject a pending email. Args: email_id (string), action (approve/reject), reason (string, optional). Requires oversight scope.

delete_contact: Delete a contact. Args: contact_id (string).

check_suppression: List suppressed email addresses. Args: limit (number, optional), cursor (string, optional).

remove_suppression: Remove address from suppression list. Args: email_address (string).

list_api_keys: List all API keys with metadata. No arguments. Requires admin scope.

create_api_key: Create a new API key. Requires admin scope and operator email approval. Key value returned only once after approval code is provided.

revoke_api_key: Revoke an API key. Args: key_id (string). Requires admin scope. Cannot be undone.

get_audit_log: Get account audit log. Args: limit (number, optional), cursor (string, optional). Requires admin scope.

delete_account: Permanently delete account and all data. No arguments. Requires admin scope. Cannot be undone.

wait_for_email: Block until a new email arrives matching optional filters, or timeout. Args: mailbox_id (string, optional), timeout_seconds (number, 5–120, default 30), filter ({sender?, subject_contains?}, optional). Returns immediately when mail arrives.

create_webhook: Create a webhook subscription. Requires admin scope and operator email approval. Args: url (HTTPS string), events (string[]), mailbox_id (string, optional). Returns subscription with signing_secret after approval.

list_webhooks: List all webhook subscriptions for this account. No arguments.

delete_webhook: Delete a webhook subscription. Args: webhook_id (string).

configure_mailbox: Set up mailbox preferences on first run. Args: oversight_mode ("gated_all", "gated_send", "monitored", "autonomous", optional), display_name (string, optional), default_cc (string[], optional), default_bcc (string[], optional), scheduling_enabled (boolean, optional), default_gate_timing ("gate_first" or "schedule_first", optional), signature (string, optional), mailbox_id (string, optional). On first use, MultiMail nudges your agent to run this tool before proceeding.

schedule_email: Schedule an email for future delivery. Same as send_email but send_at is required. Args: to (string[]), subject (string), markdown (string), send_at (ISO 8601 UTC, required — must end with Z), cc (string[], optional), bcc (string[], optional), attachments (array, optional), gate_timing ("gate_first" or "schedule_first", optional), idempotency_key (string, optional), mailbox_id (string, optional). Returns { id, status, thread_id }. Use edit_scheduled_email to modify or cancel_message to cancel.

edit_scheduled_email: Edit a scheduled email before it sends. Args: email_id (string), send_at (ISO 8601 UTC, optional), to (string[], optional), cc (string[], optional), bcc (string[], optional), subject (string, optional), markdown (string, optional), mailbox_id (string, optional). Only works on emails with status "scheduled".

Error handling

CLI

The MultiMail CLI provides every API endpoint as a shell command, plus compound analytics commands that work offline via local SQLite sync. No MCP client required.

Install

npx -y @mvanhorn/printing-press install multimail

Authenticate

multimail-pp-cli auth set-token YOUR_API_KEY
# or: export MULTIMAIL_BEARER_AUTH="mm_live_..."

Basic usage

# List accounts (agent-friendly JSON)
multimail-pp-cli account list --agent

# Check inbox
multimail-pp-cli emails list --agent

# Send (pipe markdown body)
echo "Hello from CLI" | multimail-pp-cli emails create --stdin --agent

Compound commands (offline analytics)

These commands join across locally synced data and are not available via MCP or REST:

Exit codes

0 success, 2 usage error, 3 not found, 4 auth error, 5 API error, 7 rate limited, 10 config error.

All endpoints

Fetch GET /v1/openapi.json for full request/response schemas.

Authentication

Include your API key in every request:

Authorization: Bearer mm_live_...

API keys have scopes: read, send, admin, oversight, oversight_read. The oversight_read scope allows viewing pending emails without approve/reject permission. If a request returns 403, the key lacks the required scope.


Support: support@multimail.dev

NewThe AI email control plane for agents ›

Let your agent send email.
On your terms.

A real inbox your agents run from any shell, CI pipeline, or MCP client. Email goes out as markdown. 15× fewer tokens than HTML. Every send carries signed identity and the oversight you set.

WORKS WITH

OVERSIGHT

A trust ladder for agent autonomy

Every mailbox has a configurable oversight mode. Start read-only, graduate through gated modes aligned with your risk tolerance, or let experienced agents run autonomously.

In gated modes the approval path is formally verified in Lean 4 — no email reaches delivery without passing operator approval.

  • Read-only — Your agent reads. Sending stays off.
  • Drafts only — Your agent drafts every email. You approve each send.
  • Gated send — Your agent composes. You approve before delivery. The default.
  • Monitored — Your agent sends on its own. You get a copy of each.
  • Autonomous — Full send and receive. No approval needed.

CONTROLLED AUTONOMY

Allow the trusted. Gate the rest.

Your agent emails support@vendor.com ten times a week. You approve every one. Add them to the allowlist. Your agent sends to them directly. Everyone else stays gated.

Allowlisted support@vendor.com

Your agent sends directly.

Gated new@recipient.com

Everyone else stays gated.

Oversight Gated send (default)

Every mailbox has a configurable oversight mode.

Autonomous Allowlist trusted contacts

Keep the gate for everyone else.

HOW IT WORKS

Three steps from signup to sending

Sign up, get an email address with signed identity on every email, and start sending. Web dashboard or fully programmatic via API. See the API docs.

1

Create your account

Pick a slug. You get a real address at you.multimail.dev, signed identity built in. Start free.

2

MCP Quickstart

Connect Claude, GPT, or any MCP client. Zero code: npx @multimail/mcp-server. MCP quickstart · API quickstart.

3

Send an email

From your dashboard or fully programmatic via API.

AGENT IDENTITY

Cryptographically signed identity on every email

Every outbound email carries a signed X-MultiMail-Identity header: the operator, oversight mode, and verification status, signed with ECDSA P-256. The signature binding is formally verified in Lean 4.

IDENTITY HEADER
X-MultiMail-Identity: {
  "agent": "did:key:z6Mk...",
  "operator": "human@example.com",
  "oversight": "gated_send",
  "verification": "verified",
  "signature": "ECDSA-P256..."
}
agent did:key
MultiMail signer
human approval
recipient

Each email's identity is signed in transit. It is verifiable on arrival.

FEATURES

Built for how agents work

Markdown-native, thread-aware, and designed for machine-speed operation.

Markdown-native

Markdown in, markdown out

15× fewer tokens than HTML.

Thread-aware

Replies keep the thread

Your agent reads context, not raw headers.

Machine-speed

No UI in the loop

Everything an agent does, it does over the API.

PROOF

Verified by formal proofs

Core safety properties proven in Lean 4 and re-checked in CI on every push. See the proofs.

LEAN 4 VERIFIED
✓ Lean 4 checked on every push
Core safety properties proven in Lean 4 and re-checked in CI on every push.

Lean is an open-source programming language and proof assistant that enables correct, maintainable, and formally verified code. lean-lang.org

MODEL CONTEXT PROTOCOL

One config line. Full email control.

Connect any MCP client. Hosted or local, your call. Read the docs · browse integrations.

Hosted

mcp.multimail.dev

OAuth in the browser. Nothing to run.

Local

npx @multimail/mcp-server

Runs on your machine over stdio.

Coverage

Send, read, thread, gate, bill

Full coverage across the whole mailbox.

Clients

Claude, GPT, Cursor

Any MCP client connects.

COMMAND LINE

Email from any terminal, pipeline, or script

Install the CLI. Same markdown, same signed identity, same low token cost as the API. Analytics run offline. CLI reference.

$ multimail send --to support@vendor.com
oversight: gated_send
status: pending human approval
$ multimail analytics --offline
mailboxes: 25
emails: 30000
identity: verified

PRICING

Start with one agent. Scale to a fleet.

FAQ

Let your agent send email. On your terms.

Every mailbox has a configurable oversight mode. Start read-only, graduate through gated modes aligned with your risk tolerance, or let experienced agents run autonomously.
Every outbound email carries a signed X-MultiMail-Identity header: the operator, oversight mode, and verification status, signed with ECDSA P-256.
Send and receive email as markdown. 15× fewer tokens than raw HTML.
Connect Claude, GPT, or any MCP client. Zero code: npx @multimail/mcp-server
Core safety properties proven in Lean 4 and re-checked in CI on every push.