Send Your First Agent Email in 3 Minutes

MultiMail gives your AI agent a direct path from API key to delivered email — with gated approval built in so production sends go through human review by default.


Why this matters

Adding email to an agent workflow should take an afternoon, but the first integration typically stalls in three places: figuring out the correct authentication headers, getting the payload shape right, and deciding what happens when the agent sends something the human didn't expect. Teams often discover these gaps in production, after an agent has already sent an email that shouldn't have gone out.


How MultiMail solves this

MultiMail provides a REST API, an MCP server with 50 tools, and a Python SDK that share the same authentication model and mailbox primitives. The default oversight mode for new integrations is gated_send: your agent can read and draft emails autonomously, but sends are held in a queue until a human approves them. This means you can ship a first agent email integration without writing your own approval logic or review UI.

1

Create an API key

Generate a live or test API key from the MultiMail dashboard. Test keys (mm_test_...) route all sends to a sandbox and never reach real recipients, so you can exercise the full authentication, payload, and approval flow safely before going to production.

2

Connect your agent

Set the Bearer token in your agent's environment. For REST or SDK callers, add the Authorization header to every request. For MCP clients such as Claude Desktop, Cursor, or Windsurf, point the server config at https://mcp.multimail.dev/mcp with the same token.

3

Draft and queue the email

Call send_email with the from address (a mailbox you own on MultiMail), recipient, subject, and body. Under gated_send, the API returns a message_id and status: pending — the email is queued, not yet delivered.

4

Approve the send

MultiMail notifies the reviewer via webhook or email. The reviewer calls decide_email with action: approve to release the message, or action: reject to discard it. Only after approval does MultiMail hand the message off for delivery.

5

Confirm delivery

After approval, MultiMail delivers the email and emits an email.delivered webhook event. Your agent can subscribe to the webhook or poll read_email with the message_id to confirm delivery before proceeding to the next step in the workflow.


Implementation

Send an email via REST API
bash
curl -X POST https://api.multimail.dev/v1/send_email \
  -H "Authorization: Bearer mm_test_your_key_here" \
  -H "Content-Type: application/json" \
  -d &"cm">#039;{
    "from": "[email protected]",
    "to": "[email protected]",
    "subject": "Welcome from your AI agent",
    "body": "Hi Jordan, this is your first agent-sent email through MultiMail. Delivery is configured and ready."
  }&"cm">#039;

"cm"># Response:
"cm"># {
"cm">#   "message_id": "msg_01abc123def456",
"cm">#   "status": "pending",
"cm">#   "oversight_mode": "gated_send"
"cm"># }

A minimal POST to send_email using a test API key. The response confirms the message is queued pending human approval under gated_send.

Send an email via Python SDK
python
from multimail import MultiMailClient

client = MultiMailClient(api_key="mm_test_your_key_here")

message = client.send_email(
    from_address="[email protected]",
    to="[email protected]",
    subject="Welcome from your AI agent",
    body="Hi Jordan, this is your first agent-sent email through MultiMail. Delivery is configured and ready.",
    idempotency_key="onboarding-jordan-2026-04-19"
)

print(message.id)      "cm"># msg_01abc123def456
print(message.status)  "cm"># "pending" — queued for approval under gated_send

Use the multimail-sdk to send from within an agent function. The SDK handles retries and surfaces approval status on the returned object.

Send an email via MCP tool
json
{
  "tool": "send_email",
  "parameters": {
    "from": "[email protected]",
    "to": "[email protected]",
    "subject": "Welcome from your AI agent",
    "body": "Hi Jordan, this is your first agent-sent email through MultiMail. Delivery is configured and ready."
  }
}

"cm">// Tool response:
"cm">// {
"cm">//   "message_id": "msg_01abc123def456",
"cm">//   "status": "pending",
"cm">//   "approval_url": "https://app.multimail.dev/approve/msg_01abc123def456"
"cm">// }

For agents running inside an MCP-compatible client (Claude Desktop, Cursor, Windsurf), invoke send_email as a tool call. The MCP server holds the API key and enforces the configured oversight mode transparently.

Approve a queued message
python
from multimail import MultiMailClient

client = MultiMailClient(api_key="$MULTIMAIL_API_KEY")

result = client.decide_email(
    message_id="msg_01abc123def456",
    action="approve"
)

print(result.status)     "cm"># "sent"
print(result.delivered)  "cm"># True (after delivery confirmation)

Once a reviewer is ready to release the message, call decide_email with the message ID and action: approve. Use action: reject to discard the message without sending.

Handle delivery and bounce webhooks
python
from flask import Flask, request, abort
import hmac, hashlib

app = Flask(__name__)
WHOOK_SECRET = "whsec_your_webhook_secret"

@app.route("/webhooks/multimail", methods=["POST"])
def handle_webhook():
    sig = request.headers.get("X-MultiMail-Signature", "")
    expected = hmac.new(WHOOK_SECRET.encode(), request.data, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(sig, expected):
        abort(403)

    event = request.json
    if event["type"] == "email.delivered":
        print(f"Delivered: {event[&"cm">#039;data']['message_id']}")
    elif event["type"] == "email.bounced":
        print(f"Bounce: {event[&"cm">#039;data']['reason']}")
    return "", 200

Register a webhook endpoint to receive delivery confirmation. MultiMail POSTs a JSON payload when the message is delivered or bounced.


What you get

Approval workflow included, no code required

gated_send holds every outbound message in a queue until a human approves it. You get the full review workflow — notification, approve/reject UI, and state tracking — without building any of it yourself.

Test keys that never reach real inboxes

mm_test_ keys exercise the complete send path — authentication, payload validation, approval flow, and delivery webhooks — against a sandbox. You can iterate on your agent without risking accidental sends to real users.

One auth model across REST, MCP, and SDK

The same Bearer token works whether your agent calls the REST API directly, uses the Python SDK, or runs through an MCP-compatible client. No separate credentials or configuration per integration path.

Idempotent sends protect against duplicate delivery

Pass an idempotency_key in the send_email request. If your agent retries after a timeout or network failure, MultiMail returns the original message_id without creating a second send — no duplicate emails reach recipients.


Recommended oversight mode

Recommended
gated_send
gated_send is the right default for a first integration. Your agent can read the inbox and draft messages autonomously, which keeps the workflow moving, but every outbound send goes through a human approval step before delivery. This catches misconfigured recipients, unexpected content, or runaway retry loops before they reach real inboxes. Once you've confirmed the agent sends correctly across a representative sample of production messages, you can migrate specific mailboxes to monitored or autonomous for the paths you trust.

Common questions

What is the difference between a test key and a live key?
Test keys (mm_test_...) route all sends to a sandbox. Emails never leave MultiMail's infrastructure, so you can test the full send and approval flow without reaching real recipients. Live keys (mm_live_...) deliver to actual email addresses. Use test keys during development and switch to live keys when you're ready for production traffic.
How does my agent know whether a send was approved or rejected?
Under gated_send, send_email returns immediately with status: pending and a message_id. You can poll read_email with that ID to check for a status change, or register a webhook endpoint to receive an email.approved or email.rejected event the moment a human makes a decision.
Can I skip the approval step for certain emails?
Yes. You can set the oversight mode for a mailbox to monitored or autonomous if you want the agent to send without waiting for approval. This is appropriate for low-risk, high-volume paths where you've already validated the agent's behavior — for example, transactional confirmations where content is templated and recipients are known.
What happens if the agent tries to send to an invalid address?
MultiMail validates the to address format before queuing the message. If the format is invalid, send_email returns a 400 error immediately. If the address is syntactically valid but the recipient's mail server rejects delivery, MultiMail emits an email.bounced webhook event containing the rejection reason and SMTP status code.
Do I need a custom domain to start sending?
No. MultiMail provides @multimail.dev mailboxes you can use immediately after creating an API key via create_mailbox. For production use, you can verify your own domain (e.g., [email protected]) by adding DNS records in the dashboard. Custom domains improve deliverability and let recipients see your brand in the from address.
How do I list messages waiting for approval?
Call list_pending to retrieve all messages currently queued for human review. The response includes message IDs, recipients, subjects, and queue timestamps. You can pass these IDs to decide_email to approve or reject messages programmatically, or surface them in your own review UI.

Explore more use cases

The only agent email with a verifiable sender

Email infrastructure built for AI agents. Verifiable identity, graduated oversight, and a 50-tool MCP server. Formally verified in Lean 4.