Web & Session API
The endpoints the web interface itself uses. Authenticate with a session token, or with a single inbox's own key.
Two ways to authenticate
| Header | Scope | Where it comes from |
|---|---|---|
| Authorization: Bearer <token> | The whole account | Returned by login/verify, what the browser uses |
| X-API-Key: mf_… | One inbox only | The 🔑 button in that inbox's header |
Account keys do not work here
mf_live_… keys belong to the /api/v1 agent API. On these paths use a session token or the per-inbox key. Mixing them is what produces a confusing 401 on an otherwise valid key.
Endpoints
Authregister · verify · login
| Method & path | Body | Returns |
|---|---|---|
| POST /api/auth/register | { email, password, username?, full_name? } | Sends a 6-digit code to the address |
| POST /api/auth/verify | { email, otp } | { token, user } |
| POST /api/auth/login | { identifier, password } | A second-factor code, then { token, user } on verify |
identifier accepts either the username or the email address.
Inboxescreate · list · burn · delete
| Method & path | Body / query | Notes |
|---|---|---|
| POST /api/inboxes | { prefix, name?, group_name?, retention_hours?, subdomain?, domain_id? } | prefix is required here (1–50 chars). Encrypted automatically when the account has E2E on. |
| GET /api/inboxes | ?agent=1 | ?agent=0 | 1 = agent pool only, 0 = human pool only, omitted = both |
| POST /api/inboxes/{addr}/burn | — | Deletes every message but keeps the address alive |
| DELETE /api/inboxes/{addr} | — | Releases the address itself, permanently |
Mailread · latest · tag filter · mark read · delete · send
| Method & path | Body / query | Notes |
|---|---|---|
| GET /api/inboxes/{addr}/emails | ?tag=promo | Each item carries id, from, subject, body, otp_code, is_read, direction, plus links, headers, attachments (metadata only — the bytes come from the attachment endpoint below) and spam (SpamAssassin score, threshold and the rules that fired; null means not scanned, which is not the same as clean) |
| GET /api/inboxes/{addr}/emails/latest | — | Newest message, or { email: null } when the inbox is empty |
| POST /api/inboxes/{addr}/emails/{id}/read | — | Marks one message read |
| DELETE /api/inboxes/{addr}/emails/{id} | — | Deletes one message |
| GET /api/inboxes/{addr}/emails/{id}/attachments/{aid} | — | The attached file itself. On an encrypted inbox it returns the envelope instead — the server cannot open it. |
| POST /api/inboxes/{addr}/send | { to, subject?, body?, html? } | Sends from this address, DKIM-signed. Counts against your monthly allowance. |
Working examples
# every message in an inbox
curl https://mailflat.net/api/inboxes/you@a7f2c.mailflat.net/emails \
-H "Authorization: Bearer <token>"
# → { "emails": [ { id, from, subject, body, otp_code, is_read }, … ] }
# newest message only, the OTP polling call
curl https://mailflat.net/api/inboxes/you@a7f2c.mailflat.net/emails/latest \
-H "Authorization: Bearer <token>"
# filter by tag (plus-addressing: you+promo@…)
curl "https://mailflat.net/api/inboxes/you@a7f2c.mailflat.net/emails?tag=promo" \
-H "Authorization: Bearer <token>"
# the same call with that inbox's own key
curl https://mailflat.net/api/inboxes/you@a7f2c.mailflat.net/emails/latest \
-H "X-API-Key: mf_9d2f8a…"
# mark read · delete · send
curl -X POST .../emails/123/read -H "Authorization: Bearer <token>"
curl -X DELETE .../emails/123 -H "Authorization: Bearer <token>"
curl -X POST .../send -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"to":"a@gmail.com","subject":"Hi","body":"Hello"}'
Line by linewhat each step of this curl example does
- /emails
- Everything in the inbox, newest first.
- /emails/latest
- One message, cheaper to poll than the full list.
- ?tag=promo
- Only mail sent to you+promo@…, so you can see who shared your address.
- X-API-Key: mf_…
- Same routes, scoped to this one inbox. Safe to give to a third party.
OTP loop: call /emails/latest every 2–3s until otp_code is non-null, then stop. That is exactly what wait_for_otp does under the hood.
Status codes, rate limits & quota
Errors return { "detail": "…" } with the status below.
| Status | Meaning | Example detail |
|---|---|---|
| 200 | OK | request succeeded |
| 400 | Business rule rejected it | Monthly email quota reached |
| 401 | Missing / invalid credentials | A valid session token is required |
| 403 | Authenticated but not allowed | Inbox not found for this key |
| 404 | Resource not found | Not found |
| 422 | Malformed body, schema validation failed | field required |
| 429 | Rate limit exceeded | Too many requests, please slow down |
Rate limit: 500 requests/hour per IP. Space OTP polling to ~1 call every 2–3s.
Monthly quota: each plan has an allowance covering received and sent mail (see /api/plans); once spent, both directions are rejected with 400 Monthly email quota reached until the next month.
Retention: messages auto-purge after the inbox's window; the address itself is permanent.
Monthly quota: each plan has an allowance covering received and sent mail (see /api/plans); once spent, both directions are rejected with 400 Monthly email quota reached until the next month.
Retention: messages auto-purge after the inbox's window; the address itself is permanent.