MailFlatDocs
Documentation/API References/Agent API & MCP

Agent API & MCP Server

API endpoints and MCP integrations to easily connect MailFlat to AI agents (GPT, Claude, Cursor).

Endpoints

Every route below authenticates with an account key: X-API-Key: mf_live_… (create one in Agents → API keys).
Method & pathDoesReturns
POST /api/v1/inboxesOpen a new agent inbox (always plain text){ address, retention_hours, … }
GET /api/v1/inboxesList every agent inbox this key can see{ inboxes: [ … ] }
GET /api/v1/inboxes/{addr}/messagesAll messages in the inbox{ emails: [ … ] }
GET /api/v1/inboxes/{addr}/latestNewest message only, the OTP polling call{ email: { … } }
POST /api/v1/inboxes/{addr}/sendSend a DKIM-signed mail from this inbox{ ok: true }
DELETE /api/v1/inboxes/{addr}Delete the inbox and its messages{ ok: true }
DELETE /api/v1/inboxes/{addr}/messages/{id}Delete one message{ ok: true }
GET /api/v1/inboxes/{addr}/messages/{id}/attachments/{aid}Download one attachmentthe file itself — or the envelope, on an encrypted inbox
Message fields worth knowingattachments · headers · links · spam
FieldTypeMeaning
attachmentsarrayMetadata only — id, filename, content_type, size_bytes, is_encrypted, truncated. The bytes come from the attachment endpoint above; putting them in every listing would make a single 5 MB file weigh down every call.
headersobjectEvery header as it arrived. Repeating ones (notably Received) come back as a list, because the chain is the delivery path. null on an encrypted inbox: headers carry the Subject, so they travel inside the envelope instead.
linksarrayEvery http(s) URL in the message, in order, de-duplicated. Magic links without a regex.
spamobject | nullSpamAssassin result: score, the required threshold it was judged against, is_spam, and every rules[] entry that fired with its own weight. null means the message was never scanned — not that it is clean; 0.0 is a real result. Stays readable on encrypted inboxes: rule names carry no message content. Nothing is filtered or blocked on our side.
Attachment limits10 files · 5 MB each · 10 MB total
LimitValueWhat happens past it
Files per message10Later files are not recorded.
One file5 MBKept as metadata with truncated: true — the entry stays so an oversized file is distinguishable from one that never arrived.
All files in a message10 MBSame: the entry stays, the bytes do not.
Downloading a truncated attachment
Returns 400 with Attachment was too large to store rather than an empty file, so a test fails loudly instead of asserting on zero bytes.
POST /api/v1/inboxes body fieldsevery field is optional
FieldTypeMeaning
prefixstringPart before the @. Omit it and one is generated for you.
subdomainstringNamespace after the @ on mailflat.net. Random when omitted; paid plans only.
domainstringOne of your verified BYOD domains, e.g. acme.comprefix@acme.com.
labelstringA human name for the inbox, shown in the dashboard.
retention_hoursintHow long messages are kept. Capped to your plan max rather than rejected.
POST /api/v1/inboxes/{addr}/send body fieldsto is required
FieldTypeMeaning
toemail, requiredRecipient address.
subjectstringDefaults to empty.
bodystringPlain-text body.
htmlstringOptional HTML body. Use it for templated mail; plain body alone is fine.
Errors you can actually hit400 · 401 · 403 · 429
StatusWhenDetail message
400A plan or business rule blocked itFree plan allows at most 3 agent inboxes…
401Missing key, or an inbox key used on a v1 pathA valid API key is required
403The key does not own that inboxInbox not found for this key
429More than 500 requests/hour from one IPToo many requests
Encrypted inboxes answer differently
If you point these routes at an end-to-end encrypted inbox, the response carries encrypted: true and a note explaining that body and otp_code are unavailable, because the server genuinely cannot read them. Inboxes created through this API are never encrypted, so this only happens when you target an inbox from the human pool.

The full loop

Create → use → poll → clean up. The same four steps in every language.
# 1) create a short-lived inbox (2h retention)
curl -X POST https://mailflat.net/api/v1/inboxes \
-H "X-API-Key: mf_live_…" -H "Content-Type: application/json" \
-d '{"prefix":"agent-7f3","retention_hours":2}'
# → { "address": "agent-7f3@a7f2c.mailflat.net" }
# 2) poll the latest mail (OTP auto-extracted)
curl https://mailflat.net/api/v1/inboxes/agent-7f3@a7f2c.mailflat.net/latest \
-H "X-API-Key: mf_live_…"
# → { "email": { "subject": "Your code", "otp_code": "482913" } }
# 3) send a reply from this inbox (DKIM-signed)
curl -X POST https://mailflat.net/api/v1/inboxes/agent-7f3@a7f2c.mailflat.net/send \
-H "X-API-Key: mf_live_…" -H "Content-Type: application/json" \
-d '{"to":"user@gmail.com","subject":"Re: hi","body":"Hello back!"}'
# 4) clean up when done
curl -X DELETE https://mailflat.net/api/v1/inboxes/agent-7f3@a7f2c.mailflat.net \
-H "X-API-Key: mf_live_…"
Line by linewhat each step of this curl example does
-H "X-API-Key: …"
Account key, the only auth this API accepts.
retention_hours: 2
Short window so abandoned test inboxes clean themselves up.
/latest
Newest message only. Poll it every 2–3s until otp_code is non-null.
DELETE …
Optional. Retention would purge the messages anyway, but this frees the inbox slot immediately.
The SDKs wrap exactly this loop: wait_for_otp / waitForOtp is the polling block above.

MCP server, give an AI agent inbox tools

MailFlat ships a native Model Context Protocol server. Run it inside Claude Desktop, Cursor or your own agent framework and the model gets six inbox tools automatically.
# no install step, uvx fetches and runs it
MAILFLAT_API_KEY=mf_live_… uvx mailflat-mcp
Seven tools exposed
ToolWhat it does
create_inbox(prefix?, label?, retention_hours?)Open an inbox; retention_hours capped by your plan
list_inboxes()All inboxes this key can see
read_messages(address)Read every message in an inbox
wait_for_otp(address, timeout=30)Poll until an OTP arrives, then return it
send_email(address, to, subject?, body?, html?)Send a DKIM-signed mail from the inbox
delete_inbox(address)Delete the inbox and its messages
delete_message(address, message_id)Delete one message; the inbox itself stays
Not using MCP?
Agents → Tool spec → Copy spec gives you six of them — everything exceptdelete_message — as a raw function schema for GPT, Claude or LangChain, with the same names and parameters.
GPTClaudeLangChain