Developers

API reference

Base URL https://api.futuremail.dev. Authenticate with Authorization: Bearer fm_live_… (create keys in Settings → API keys, or with fm keys create). Request and response bodies are JSON; errors look like { "error": { "code", "message" } }, and lists are { "data": [...], "nextCursor" } (pass nextCursor as cursor).

Address fields in requests accept "ada@example.com", "Ada <ada@example.com>" or { "address", "name" }, and recipient fields also take an array of them.

FutureMail is paid-only: without an active plan, sending and creating domains, addresses, agents, keys and webhooks return 402 upgrade_required. Rate limit: 10 requests/second per key (429 with Retry-After).

This page is generated from the OpenAPI 3.1 spec at https://api.futuremail.dev/openapi.json: import it into Postman, Insomnia or a client generator. For guides (webhooks, MCP, the CLI, Resend compatibility), see the developer docs.

Account

Who am I

get/v1/mescope: any key

The current organization, plan and usage, and the API key making the request. Call this first to check a key.

curl https://api.futuremail.dev/v1/me \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Me — OK

Emails

Send an email

post/v1/emailsscope: send · needs an active plan

Send now, or later with scheduledAt. from must be an address on one of your verified domains. To test without sending anything, address it to delivered@test.futuremail.dev (or bounced@, complained@, delayed@): test sends don't count toward your limits.

ParameterInTypeDescription
Idempotency-KeyheaderstringSafe retries: the same key within 24 hours returns the original response instead of sending again (≤256 chars).

Body

FieldTypeDescription
fromaddressAn address you own: "me@mydomain.com", "Me <me@mydomain.com>" or { address, name }. Optional when template has a default sender.
torequiredaddress | address[]One recipient or an array (To + Cc + Bcc count toward your plan's recipient caps).
ccaddress | address[]Copy recipients.
bccaddress | address[]Blind-copy recipients.
replyToaddress | address[]Reply-To address(es).
subjectstringSubject line. · default "", max 998 chars
textstringPlain-text body.
htmlstringHTML body.
attachmentsAttachmentInput[]Up to 20 files, 10 MB per message: base64 content, or a public path URL fetched at send time. contentId makes one inline (<img src="cid:…">). · max 20
headersmapCustom headers, e.g. List-Unsubscribe. Headers FutureMail sets itself are rejected.
inReplyTostringOur message id to thread this email onto (sets In-Reply-To/References).
tagsEmailTag[]Labels echoed in webhooks and filterable with GET /v1/emails?tag=. Max 50. · max 50
scheduledAtISO dateSend later (ISO 8601 with offset, up to 30 days ahead). Cancel or reschedule until then.
templateobjectRender a stored template's published version: { id (or alias), variables }. Fields you pass explicitly win.
curl -X POST https://api.futuremail.dev/v1/emails \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "from": "Acme <hello@acme.com>",
  "to": "ada@example.com",
  "subject": "Welcome to Acme",
  "html": "<p>Hi Ada!</p>",
  "tags": [
    {
      "name": "campaign",
      "value": "welcome"
    }
  ]
}'

Returns 201 Message — The email, queued (or scheduled). Also: 409 idempotency_key_reused / idempotency_request_in_progress. 429 Rate limit or plan recipient cap reached.

Send up to 100 emails

post/v1/emails/batchscope: send · needs an active plan

Validated all-or-nothing: if any email is invalid nothing is sent and the error names it (3: to: …). Results are in input order.

ParameterInTypeDescription
Idempotency-KeyheaderstringSafe retries: the same key within 24 hours returns the original response instead of sending again (≤256 chars).

Body — an array (1–100) of the email object below

FieldTypeDescription
fromaddressAn address you own: "me@mydomain.com", "Me <me@mydomain.com>" or { address, name }. Optional when template has a default sender.
torequiredaddress | address[]One recipient or an array (To + Cc + Bcc count toward your plan's recipient caps).
ccaddress | address[]Copy recipients.
bccaddress | address[]Blind-copy recipients.
replyToaddress | address[]Reply-To address(es).
subjectstringSubject line. · default "", max 998 chars
textstringPlain-text body.
htmlstringHTML body.
attachmentsAttachmentInput[]Up to 20 files, 10 MB per message: base64 content, or a public path URL fetched at send time. contentId makes one inline (<img src="cid:…">). · max 20
headersmapCustom headers, e.g. List-Unsubscribe. Headers FutureMail sets itself are rejected.
inReplyTostringOur message id to thread this email onto (sets In-Reply-To/References).
tagsEmailTag[]Labels echoed in webhooks and filterable with GET /v1/emails?tag=. Max 50. · max 50
scheduledAtISO dateSend later (ISO 8601 with offset, up to 30 days ahead). Cancel or reschedule until then.
templateobjectRender a stored template's published version: { id (or alias), variables }. Fields you pass explicitly win.
curl -X POST https://api.futuremail.dev/v1/emails/batch \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
  {
    "from": "hello@acme.com",
    "to": "ada@example.com",
    "subject": "Hi Ada",
    "text": "Hello!"
  },
  {
    "from": "hello@acme.com",
    "to": "grace@example.com",
    "subject": "Hi Grace",
    "text": "Hello!"
  }
]'

Returns 201 a list of Message — The emails, in input order.

List or search emails

get/v1/emailsscope: read

Newest first. q supports Gmail-style search: from: to: subject: has:attachment is:unread in:sent after:2026-09-01 newer_than:7d.

ParameterInTypeDescription
qquerystringSearch query.
mailboxIdquerystringOnly this mailbox.
directionquery"inbound" | "outbound"inbound or outbound.
statusquerystringComma-separated statuses, e.g. bounced,complained.
tagquerystringname:value, or name for any value.
fromquerystringSubstring of the sender address or name.
toquerystringSubstring of a recipient address (To, Cc or Bcc).
sincequeryISO dateOnly emails after this ISO date.
untilqueryISO dateOnly emails before this ISO date.
unreadquerybooleanOnly unread emails.
templateIdquerystringOnly emails sent with this template (id or alias).
limitqueryintegerPage size, 1–100 (default 100).
cursorquerystringThe previous page's nextCursor.
curl https://api.futuremail.dev/v1/emails \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 a list of Message — OK

Wait for an inbound email

get/v1/emails/waitscope: read

Long-poll until the next inbound email matching the filters arrives: ideal for sign-up confirmations and one-time codes (extracted.otp). Returns 204 on timeout.

ParameterInTypeDescription
fromquerystringSubstring of the sender address or name.
toquerystringSubstring of a recipient address.
subjectquerystringSubstring of the subject.
mailboxIdquerystringOnly this mailbox.
sincequeryISO dateAlso accept emails that arrived since this ISO date (default: now).
timeoutqueryintegerSeconds to wait, up to 300 (default 60).
curl https://api.futuremail.dev/v1/emails/wait \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Message — The matching email. Also: 204 Nothing arrived before the timeout.

Get an email

get/v1/emails/{id}scope: read

ParameterInTypeDescription
idrequiredpathstringEmail id (msg_…).
curl https://api.futuremail.dev/v1/emails/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Message — OK

Mark read or reschedule

patch/v1/emails/{id}scope: read

scheduledAt moves a scheduled email (needs the send scope); 409 not_scheduled once it's been sent.

ParameterInTypeDescription
idrequiredpathstringEmail id (msg_…).

Body

FieldTypeDescription
readbooleanMark read or unread.
scheduledAtISO dateReschedule a scheduled email (needs the send scope).
curl -X PATCH https://api.futuremail.dev/v1/emails/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "scheduledAt": "2026-10-09T09:00:00Z"
}'

Returns 200 Message — OK

Cancel a scheduled email

post/v1/emails/{id}/cancelscope: send

ParameterInTypeDescription
idrequiredpathstringEmail id (msg_…).
curl -X POST https://api.futuremail.dev/v1/emails/<id>/cancel \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Message — The email, now canceled. Also: 409 not_scheduled: it was already sent or canceled.

Delivery timeline

get/v1/emails/{id}/eventsscope: read

Oldest first: queued → sent → delivered / bounced / complained / failed, plus opened and clicked when tracking is on.

ParameterInTypeDescription
idrequiredpathstringEmail id (msg_…).
curl https://api.futuremail.dev/v1/emails/<id>/events \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 a list of EmailEvent — OK

Reply

post/v1/emails/{id}/replyscope: send · needs an active plan

Threads correctly (In-Reply-To/References) and quotes the original.

ParameterInTypeDescription
idrequiredpathstringEmail id (msg_…).
Idempotency-KeyheaderstringSafe retries: the same key within 24 hours returns the original response instead of sending again (≤256 chars).

Body

FieldTypeDescription
textstringPlain-text reply (the original is quoted).
htmlstringHTML reply.
replyAllbooleanAlso reply to the original's other recipients. · default false
fromaddressOverride the From address (default: the address the original was sent to).
ccaddress | address[]Extra copy recipients.
attachmentsAttachmentInput[]Same format as when sending. · max 20
curl -X POST https://api.futuremail.dev/v1/emails/<id>/reply \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Thanks, done!"
}'

Returns 201 Message — The reply.

Forward

post/v1/emails/{id}/forwardscope: send · needs an active plan

Attachments are included.

ParameterInTypeDescription
idrequiredpathstringEmail id (msg_…).
Idempotency-KeyheaderstringSafe retries: the same key within 24 hours returns the original response instead of sending again (≤256 chars).

Body

FieldTypeDescription
torequiredaddress | address[]Recipient(s).
textstringNote above the forwarded message.
fromaddressOverride the From address.
curl -X POST https://api.futuremail.dev/v1/emails/<id>/forward \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "to": "team@acme.com",
  "text": "FYI"
}'

Returns 201 Message — The forwarded email.

Raw RFC 822 source

get/v1/emails/{id}/rawscope: read

ParameterInTypeDescription
idrequiredpathstringEmail id (msg_…).
curl https://api.futuremail.dev/v1/emails/<id>/raw \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 message/rfc822 — The raw message.

Download an attachment

get/v1/attachments/{id}scope: read

ParameterInTypeDescription
idrequiredpathstringAttachment id (from Message.attachments).
downloadquerybooleanSend Content-Disposition: attachment.
curl https://api.futuremail.dev/v1/attachments/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 application/octet-stream — The file, with its content type.

Threads

List conversations

get/v1/threadsscope: read

ParameterInTypeDescription
folderquery"inbox" | "starred" | "sent" | "drafts" | "all" | "spam" | "trash"Folder (default inbox).
mailboxIdquerystringOnly this mailbox.
qquerystringSearch query (same syntax as emails).
limitqueryintegerPage size, 1–100 (default 100).
cursorquerystringThe previous page's nextCursor.
curl https://api.futuremail.dev/v1/threads \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 a list of Thread — OK

Get a thread with its messages

get/v1/threads/{id}scope: read

ParameterInTypeDescription
idrequiredpathstringThread id (thr_…).
curl https://api.futuremail.dev/v1/threads/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 ThreadWithMessages — OK

Archive, star or mark read

patch/v1/threads/{id}scope: read

ParameterInTypeDescription
idrequiredpathstringThread id.

Body

FieldTypeDescription
status"inbox" | "archived" | "trash" | "spam"Move the thread.
starredbooleanStar or unstar.
readbooleanMark every message read or unread.
curl -X PATCH https://api.futuremail.dev/v1/threads/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "archived"
}'

Returns 200 Thread — OK

Update many threads

post/v1/threads/batchscope: read

Body

FieldTypeDescription
status"inbox" | "archived" | "trash" | "spam"
starredboolean
readboolean
idsrequiredstring[]Up to 500 thread ids. · max 500
curl -X POST https://api.futuremail.dev/v1/threads/batch \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "ids": [
    "thr_1",
    "thr_2"
  ],
  "read": true
}'

Returns 200 a list of Thread — OK

Delete a thread permanently

delete/v1/threads/{id}scope: read

ParameterInTypeDescription
idrequiredpathstringThread id.
curl -X DELETE https://api.futuremail.dev/v1/threads/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Deleted — Deleted.

Domains

List domains

get/v1/domainsscope: any key

ParameterInTypeDescription
limitqueryintegerPage size, 1–100 (default 100).
cursorquerystringThe previous page's nextCursor.
curl https://api.futuremail.dev/v1/domains \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 a list of Domain — OK

Add a domain

post/v1/domainsscope: full · needs an active plan

Returns the DNS records to create (DKIM, MX, SPF, DMARC, MAIL FROM). Then call verify.

Body

FieldTypeDescription
namerequiredstringThe domain, e.g. acme.com. The response lists the DNS records to create.
curl -X POST https://api.futuremail.dev/v1/domains \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "acme.com"
}'

Returns 201 Domain — Created.

Get a domain

get/v1/domains/{id}scope: any key

ParameterInTypeDescription
idrequiredpathstringDomain id or name.
curl https://api.futuremail.dev/v1/domains/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Domain — OK

Re-check DNS

post/v1/domains/{id}/verifyscope: full

Looks up every record now. sendingEnabled turns on when DKIM verifies, receivingEnabled when MX points at FutureMail.

ParameterInTypeDescription
idrequiredpathstringDomain id or name.
curl -X POST https://api.futuremail.dev/v1/domains/<id>/verify \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Domain — OK

Domain settings

patch/v1/domains/{id}scope: full

Catch-all, custom MAIL FROM, TLS policy and open/click tracking.

ParameterInTypeDescription
idrequiredpathstringDomain id or name.

Body

FieldTypeDescription
catchAllMailboxIdstring | nullMailbox that receives mail for unknown addresses on this domain (null turns catch-all off).
mailFromSubdomainstringCustom MAIL FROM (bounce) subdomain, default "bounce". Changing it means new MX + SPF records. · max 100 chars
tlsPolicy"opportunistic" | "require""require" only delivers over TLS; "opportunistic" (default) uses TLS when available.
openTrackingbooleanAdd a tracking pixel to outgoing HTML (email.opened).
clickTrackingbooleanRewrite links through the tracker (email.clicked).
trackingSubdomainstring | nullCustom tracking host label, e.g. "links" → links.acme.com (null removes it).
curl -X PATCH https://api.futuremail.dev/v1/domains/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "clickTracking": true,
  "openTracking": true
}'

Returns 200 Domain — OK

Remove a domain

delete/v1/domains/{id}scope: full

ParameterInTypeDescription
idrequiredpathstringDomain id or name.
curl -X DELETE https://api.futuremail.dev/v1/domains/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Deleted — Deleted.

Addresses

List addresses

get/v1/addressesscope: any key

ParameterInTypeDescription
mailboxIdquerystringOnly this mailbox.
limitqueryintegerPage size, 1–100 (default 100).
cursorquerystringThe previous page's nextCursor.
curl https://api.futuremail.dev/v1/addresses \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 a list of Address — OK

Check an address

get/v1/addresses/availabilityscope: any key

Whether you can create this address (its domain must be one of yours).

ParameterInTypeDescription
addressrequiredquerystringFull address, e.g. hello@acme.com.
curl https://api.futuremail.dev/v1/addresses/availability \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 AddressAvailability — OK

Create an address

post/v1/addressesscope: full · needs an active plan

Addresses are unlimited on your plan; each delivers into one mailbox.

Body

FieldTypeDescription
addressrequiredstringFull address on one of your domains, e.g. hello@acme.com.
mailboxIdstringMailbox to deliver to (default: your personal mailbox).
displayNamestring | nullName shown in From.
curl -X POST https://api.futuremail.dev/v1/addresses \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "address": "support@acme.com",
  "displayName": "Acme Support"
}'

Returns 201 Address — Created.

Update an address

patch/v1/addresses/{id}scope: full

ParameterInTypeDescription
idrequiredpathstringAddress id or the address itself.

Body

FieldTypeDescription
mailboxIdstringMove the address to another mailbox.
displayNamestring | nullName shown in From.
curl -X PATCH https://api.futuremail.dev/v1/addresses/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "displayName": "Acme Help"
}'

Returns 200 Address — OK

Delete an address

delete/v1/addresses/{id}scope: full

ParameterInTypeDescription
idrequiredpathstringAddress id or the address itself.
curl -X DELETE https://api.futuremail.dev/v1/addresses/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Deleted — Deleted.

Mailboxes & agents

List mailboxes

get/v1/mailboxesscope: any key

ParameterInTypeDescription
limitqueryintegerPage size, 1–100 (default 100).
cursorquerystringThe previous page's nextCursor.
curl https://api.futuremail.dev/v1/mailboxes \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 a list of Mailbox — OK

Create a mailbox

post/v1/mailboxesscope: full · needs an active plan

Body

FieldTypeDescription
namerequiredstringMailbox name. · max 60 chars
kind"personal" | "agent"personal or agent. · default "personal"
descriptionstring | nullWhat it's for.
colorstringHex accent color for the web app.
curl -X POST https://api.futuremail.dev/v1/mailboxes \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Support"
}'

Returns 201 Mailbox — Created.

Get a mailbox

get/v1/mailboxes/{id}scope: any key

ParameterInTypeDescription
idrequiredpathstringMailbox id (mbx_…).
curl https://api.futuremail.dev/v1/mailboxes/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Mailbox — OK

Update a mailbox

patch/v1/mailboxes/{id}scope: full

ParameterInTypeDescription
idrequiredpathstringMailbox id.

Body

FieldTypeDescription
namestringmax 60 chars
descriptionstring | null
colorstring
signaturestring | nullAppended to replies written in the web app.
primaryAddressstringDefault From address (must belong to this mailbox).
curl -X PATCH https://api.futuremail.dev/v1/mailboxes/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "primaryAddress": "support@acme.com"
}'

Returns 200 Mailbox — OK

Delete a mailbox

delete/v1/mailboxes/{id}scope: full

ParameterInTypeDescription
idrequiredpathstringMailbox id.
curl -X DELETE https://api.futuremail.dev/v1/mailboxes/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Deleted — Deleted.

Provision an agent inbox

post/v1/agentsscope: full · needs an active plan

Creates an agent mailbox, an address and an API key pinned to that mailbox in one call. The key is returned once.

Body

FieldTypeDescription
namerequiredstringHuman name, e.g. "Signup bot". · max 60 chars
addressstringThe agent's address on one of your domains.
descriptionstring | nullWhat the agent does.
scopes("send" | "read")[]Scopes of the agent's key (always pinned to its mailbox, so never full). · default ["send","read"]
curl -X POST https://api.futuremail.dev/v1/agents \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Signup bot",
  "address": "signup-bot@acme.com"
}'

Returns 201 AgentProvisioned — Created.

Templates

List templates

get/v1/templatesscope: any key

ParameterInTypeDescription
limitqueryintegerPage size, 1–100 (default 100).
cursorquerystringThe previous page's nextCursor.
curl https://api.futuremail.dev/v1/templates \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 a list of Template — OK

Create a template

post/v1/templatesscope: full

Edits change the draft; sends use the last published version.

Body

FieldTypeDescription
namerequiredstringInternal name. · max 100 chars
aliasstring | nullStable handle to send with, e.g. "welcome".
subjectstringSubject; may use {{variables}}. · default "", max 998 chars
htmlstring | nullHTML body; {{var}} is HTML-escaped, {{{var}}} is raw.
textstring | nullPlain-text body.
fromstring | nullDefault sender for sends that don't pass one.
replyTostring[]Default Reply-To addresses. · max 10
variablesobject[]Declared variables: { key, type, fallback }. A missing variable without a fallback is a 422. · max 50
publishbooleanPublish right away so it can be used for sending. · default false
curl -X POST https://api.futuremail.dev/v1/templates \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Welcome",
  "alias": "welcome",
  "subject": "Welcome, {{name}}!",
  "html": "<p>Hi {{name}}</p>",
  "variables": [
    {
      "key": "name",
      "fallback": "there"
    }
  ],
  "publish": true
}'

Returns 201 Template — Created.

Get a template

get/v1/templates/{id}scope: any key

ParameterInTypeDescription
idrequiredpathstringTemplate id or alias.
curl https://api.futuremail.dev/v1/templates/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Template — OK

Edit the draft

patch/v1/templates/{id}scope: full

ParameterInTypeDescription
idrequiredpathstringTemplate id or alias.

Body

FieldTypeDescription
namestringmax 100 chars
aliasstring | null
subjectstringmax 998 chars
htmlstring | null
textstring | null
fromstring | null
replyTostring[]max 10
variablesobject[]max 50
curl -X PATCH https://api.futuremail.dev/v1/templates/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "subject": "Welcome aboard, {{name}}!"
}'

Returns 200 Template — OK

Publish the draft

post/v1/templates/{id}/publishscope: full

ParameterInTypeDescription
idrequiredpathstringTemplate id or alias.
curl -X POST https://api.futuremail.dev/v1/templates/<id>/publish \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Template — OK

Render with variables

post/v1/templates/{id}/renderscope: any key

ParameterInTypeDescription
idrequiredpathstringTemplate id or alias.

Body

FieldTypeDescription
variablesmapValues for the template's variables. · default {}
draftbooleanRender the draft instead of the published version. · default false
curl -X POST https://api.futuremail.dev/v1/templates/<id>/render \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "variables": {
    "name": "Ada"
  }
}'

Returns 200 RenderedTemplate — OK

Delete a template

delete/v1/templates/{id}scope: full

ParameterInTypeDescription
idrequiredpathstringTemplate id or alias.
curl -X DELETE https://api.futuremail.dev/v1/templates/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Deleted — Deleted.

Suppressions

List suppressed addresses

get/v1/suppressionsscope: read

Addresses your org won't send to: permanent bounces and complaints are added automatically.

ParameterInTypeDescription
reasonquery"bounce" | "complaint" | "manual" | "unsubscribe"Only this reason.
qquerystringSubstring of the address.
limitqueryintegerPage size, 1–100 (default 100).
cursorquerystringThe previous page's nextCursor.
curl https://api.futuremail.dev/v1/suppressions \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 a list of Suppression — OK

Suppress addresses

post/v1/suppressionsscope: full

Body

FieldTypeDescription
emailstringOne address. · max 320 chars
emailsstring[]Many addresses (up to 1,000 in total). · max 1000
reason"manual" | "unsubscribe"manual (default) or unsubscribe. · default "manual"
detailsstring | nullNote shown next to the entry.
curl -X POST https://api.futuremail.dev/v1/suppressions \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "emails": [
    "old@example.com"
  ],
  "reason": "unsubscribe"
}'

Returns 201 AddSuppressionsResult — Added (200 when every address was already suppressed).

Check addresses

post/v1/suppressions/checkscope: read

Body

FieldTypeDescription
emailsrequiredstring[]Addresses to check (up to 1,000). · max 1000
curl -X POST https://api.futuremail.dev/v1/suppressions/check \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "emails": [
    "ada@example.com",
    "old@example.com"
  ]
}'

Returns 200 CheckSuppressionsResult — OK

Get a suppression

get/v1/suppressions/{email}scope: read

ParameterInTypeDescription
emailrequiredpathstringThe suppressed address.
curl https://api.futuremail.dev/v1/suppressions/<email> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Suppression — OK

Remove a suppression

delete/v1/suppressions/{email}scope: full

ParameterInTypeDescription
emailrequiredpathstringThe suppressed address.
curl -X DELETE https://api.futuremail.dev/v1/suppressions/<email> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Deleted — Deleted.

API keys

List API keys

get/v1/api-keysscope: full

ParameterInTypeDescription
limitqueryintegerPage size, 1–100 (default 100).
cursorquerystringThe previous page's nextCursor.
curl https://api.futuremail.dev/v1/api-keys \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 a list of ApiKey — OK

Create an API key

post/v1/api-keysscope: full · needs an active plan

The secret is returned once. Restrict keys to a mailbox, a sending domain and/or an expiry.

Body

FieldTypeDescription
namerequiredstringLabel for the key. · max 60 chars
scopes("full" | "send" | "read")[]full, send and/or read. · default ["full"]
mailboxIdstring | nullRestrict the key to one mailbox.
domainIdstring | nullRestrict sending to addresses on one domain.
expiresAtISO date | nullWhen the key stops working (omit for never).
curl -X POST https://api.futuremail.dev/v1/api-keys \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "CI",
  "scopes": [
    "send"
  ],
  "expiresAt": "2027-01-01T00:00:00Z"
}'

Returns 201 ApiKeyWithSecret — Created.

Update an API key

patch/v1/api-keys/{id}scope: full

Expiry can't be changed: rotate the key instead.

ParameterInTypeDescription
idrequiredpathstringKey id (key_…).

Body

FieldTypeDescription
namestringmax 60 chars
scopes("full" | "send" | "read")[]
mailboxIdstring | nullnull removes the mailbox restriction.
domainIdstring | nullnull removes the domain restriction.
curl -X PATCH https://api.futuremail.dev/v1/api-keys/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "CI (deploys)"
}'

Returns 200 ApiKey — OK

Revoke an API key

delete/v1/api-keys/{id}scope: full

ParameterInTypeDescription
idrequiredpathstringKey id.
curl -X DELETE https://api.futuremail.dev/v1/api-keys/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Deleted — Deleted.

Webhooks

List webhooks

get/v1/webhooksscope: full

ParameterInTypeDescription
limitqueryintegerPage size, 1–100 (default 100).
cursorquerystringThe previous page's nextCursor.
curl https://api.futuremail.dev/v1/webhooks \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 a list of Webhook — OK

Create a webhook

post/v1/webhooksscope: full · needs an active plan

Events are POSTed as { id, type, createdAt, data } with a FutureMail-Signature: t=<unix>,v1=<hex> header, where v1 = HMAC-SHA256(secret, <t>.<raw body>). Non-2xx responses are retried with backoff (8 attempts).

Body

FieldTypeDescription
urlrequiredstringHTTPS endpoint that receives POSTed events.
events("email.received" | "email.scheduled" | "email.sent" | "email.delivery_delayed" | "email.delivered" | "email.bounced" | "email.complained" | "email.failed" | "email.suppressed" | "email.opened" | "email.clicked" | "domain.verified")[]Event types to deliver (default: email.received). · default ["email.received"]
curl -X POST https://api.futuremail.dev/v1/webhooks \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://acme.com/hooks/mail",
  "events": [
    "email.received",
    "email.bounced"
  ]
}'

Returns 201 Webhook — Created (includes the signing secret, shown once).

Update a webhook

patch/v1/webhooks/{id}scope: full

ParameterInTypeDescription
idrequiredpathstringWebhook id.

Body

FieldTypeDescription
urlstring
events("email.received" | "email.scheduled" | "email.sent" | "email.delivery_delayed" | "email.delivered" | "email.bounced" | "email.complained" | "email.failed" | "email.suppressed" | "email.opened" | "email.clicked" | "domain.verified")[]
enabledbooleanPause (false) or resume a webhook. Pausing stops pending retries.
curl -X PATCH https://api.futuremail.dev/v1/webhooks/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": false
}'

Returns 200 Webhook — OK

Delete a webhook

delete/v1/webhooks/{id}scope: full

ParameterInTypeDescription
idrequiredpathstringWebhook id.
curl -X DELETE https://api.futuremail.dev/v1/webhooks/<id> \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 Deleted — Deleted.

Send a test event

post/v1/webhooks/{id}/testscope: full

ParameterInTypeDescription
idrequiredpathstringWebhook id.
curl -X POST https://api.futuremail.dev/v1/webhooks/<id>/test \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 WebhookDelivery — OK

Delivery attempts

get/v1/webhooks/{id}/deliveriesscope: full

Newest first, with status codes, payloads and the next retry. Kept 30 days.

ParameterInTypeDescription
idrequiredpathstringWebhook id.
limitqueryintegerPage size, 1–100 (default 100).
cursorquerystringThe previous page's nextCursor.
curl https://api.futuremail.dev/v1/webhooks/<id>/deliveries \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 a list of WebhookDelivery — OK

Replay a delivery

post/v1/webhooks/{id}/deliveries/{deliveryId}/replayscope: full

Re-sends the exact payload (same event id) now.

ParameterInTypeDescription
idrequiredpathstringWebhook id.
deliveryIdrequiredpathstringDelivery id.
curl -X POST https://api.futuremail.dev/v1/webhooks/<id>/deliveries/<deliveryId>/replay \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 WebhookDelivery — OK

Realtime event stream

get/v1/eventsscope: read

Server-Sent Events for the current org: every webhook event plus thread.updated, as event: <type> / data: <json>. A ready event is sent first. Mailbox-pinned keys only see their mailbox.

curl https://api.futuremail.dev/v1/events \
  -H "Authorization: Bearer $FUTUREMAIL_API_KEY"

Returns 200 text/event-stream — An event stream.

Objects

AddSuppressionsResult

FieldTypeDescription
addedrequiredSuppression[]
alreadySuppressedrequiredstring[]

Address

FieldTypeDescription
idrequiredstring
addressrequiredstring
localPartrequiredstring
domainIdrequiredstring
domainrequiredstring
mailboxIdrequiredstring
displayNamerequiredstring | null
createdAtrequiredISO date

AddressAvailability

FieldTypeDescription
availablerequiredboolean
reasonstring

AgentProvisioned

FieldTypeDescription
mailboxrequiredMailbox
addressrequiredAddress
apiKeyrequiredApiKeyWithSecret

ApiKey

FieldTypeDescription
idrequiredstring
namerequiredstring
prefixrequiredstringFirst characters of the key, e.g. "fm_live_3kd9".
scopesrequired("full" | "send" | "read")[]
mailboxIdrequiredstring | nullOnly this mailbox (agent keys).
domainIdrequiredstring | nullOnly send from addresses on this domain.
expiresAtrequiredISO date | null
createdByUserIdrequiredstring | null
lastUsedAtrequiredISO date | null
createdAtrequiredISO date

ApiKeyWithSecret

FieldTypeDescription
idrequiredstring
namerequiredstring
prefixrequiredstringFirst characters of the key, e.g. "fm_live_3kd9".
scopesrequired("full" | "send" | "read")[]
mailboxIdrequiredstring | nullOnly this mailbox (agent keys).
domainIdrequiredstring | nullOnly send from addresses on this domain.
expiresAtrequiredISO date | null
createdByUserIdrequiredstring | null
lastUsedAtrequiredISO date | null
createdAtrequiredISO date
keyrequiredstringThe full secret. Only returned once, at creation.

Attachment

FieldTypeDescription
idrequiredstring
filenamerequiredstring
contentTyperequiredstring
sizerequiredinteger-9007199254740991–9007199254740991
contentIdrequiredstring | null
inlinerequiredboolean
urlrequiredstringAPI path to download the file.
blockedrequiredbooleanThe message failed the virus scan: the file can't be downloaded.

AttachmentInput

FieldTypeDescription
filenamerequiredstring
contentstring
pathstring
contentTypestring
contentIdstringmax 255 chars

Billing

FieldTypeDescription
enabledrequiredbooleanFalse on servers without billing (local development).
planrequired"none" | "pro" | "scale"
accessrequired"active" | "grace" | "inactive"active: everything works. grace: the plan ended; sending and creating are blocked, inbound mail is accepted until graceEndsAt. inactive: no plan; inbound mail is rejected.
graceEndsAtrequiredISO date | null
complimentaryrequiredboolean
statusrequired"active" | "trialing" | "past_due" | "canceled" | "unpaid" | "incomplete" | "incomplete_expired" | "paused" | null
currentPeriodEndrequiredISO date | null
cancelAtPeriodEndrequiredboolean
cancelAtrequiredISO date | null
checkoutUrlrequiredstring | null
portalUrlrequiredstring | null
limitsrequiredobject
plansrequiredobject[]

CheckSuppressionsResult

FieldTypeDescription
suppressedrequiredSuppression[]
notSuppressedrequiredstring[]

Deleted

FieldTypeDescription
deletedrequiredtrue

DnsRecord

FieldTypeDescription
purposerequired"verification" | "dkim" | "mx" | "spf" | "mail_from_mx" | "mail_from_spf" | "dmarc" | "tracking"
typerequired"MX" | "TXT" | "CNAME"
namerequiredstringHost relative to the domain ("@" for the apex).
fqdnrequiredstring
valuerequiredstring
priorityinteger | null
statusrequired"pending" | "verified" | "failed"
descriptionrequiredstring
notestring | null

Domain

FieldTypeDescription
idrequiredstring
namerequiredstring
statusrequired"pending" | "verified" | "failed"
sendingEnabledrequiredbooleanDKIM verified: you can send as this domain.
receivingEnabledrequiredbooleanMX points at FutureMail: mail to this domain is received.
catchAllMailboxIdrequiredstring | null
recordsrequiredDnsRecord[]
warningsrequiredDomainWarning[]
addressCountrequiredinteger-9007199254740991–9007199254740991
createdAtrequiredISO date
verifiedAtrequiredISO date | null
lastCheckedAtrequiredISO date | null
mailFromSubdomainrequiredstring
tlsPolicyrequired"opportunistic" | "require"
openTrackingrequiredboolean
clickTrackingrequiredboolean
trackingSubdomainrequiredstring | null
trackingHostrequiredstring

DomainWarning

FieldTypeDescription
coderequired"mx_conflict" | "spf_missing" | "spf_incomplete" | "spf_multiple" | "dmarc_missing" | "leftover_dkim"
severityrequired"warning" | "info"
titlerequiredstring
detailrequiredstring
recordsobject[]
fixobject

EmailAddress

FieldTypeDescription
addressrequiredstring
namestring | null

EmailEvent

FieldTypeDescription
idrequiredstring
messageIdrequiredstring
typerequired"queued" | "scheduled" | "canceled" | "sent" | "delivery_delayed" | "delivered" | "bounced" | "complained" | "failed" | "suppressed" | "opened" | "clicked"
datarequiredmap | null
createdAtrequiredISO date

EmailTag

FieldTypeDescription
namerequiredstring
valuerequiredstring

Mailbox

FieldTypeDescription
idrequiredstring
namerequiredstring
kindrequired"personal" | "agent"
descriptionrequiredstring | null
colorrequiredstring
primaryAddressrequiredstring | nullDefault From address when sending from this mailbox.
addressesrequiredstring[]
unreadCountrequiredinteger-9007199254740991–9007199254740991
signaturerequiredstring | null
createdAtrequiredISO date

Me

FieldTypeDescription
limitsrequiredobject
billingrequiredBilling
userrequiredobject
orgrequiredobject
rolerequired"owner" | "admin" | "developer" | "viewer" | nullNull for API keys (their scopes apply instead).
orgsrequiredobject[]
onboardingrequiredobject
staffrequiredboolean
featuresrequiredobject
providerrequired"local" | "ses"
apiKeyobject | nullThe key making this request (null for browser sessions).
smtpobject

Message

FieldTypeDescription
idrequiredstring
threadIdrequiredstring
mailboxIdrequiredstring
directionrequired"inbound" | "outbound"
statusrequired"received" | "queued" | "scheduled" | "canceled" | "sent" | "delivery_delayed" | "delivered" | "bounced" | "complained" | "failed"
messageIdrequiredstringRFC 5322 Message-ID header, without angle brackets.
inReplyTorequiredstring | null
referencesrequiredstring[]
fromrequiredEmailAddress
torequiredEmailAddress[]
ccrequiredEmailAddress[]
bccrequiredEmailAddress[]
replyTorequiredEmailAddress[]
subjectrequiredstring
textrequiredstring | null
htmlrequiredstring | null
snippetrequiredstring
attachmentsrequiredAttachment[]
extractedrequiredobjectOne-time code and links found in the email (great for agents).
authenticationrequiredobject | nullInbound only: SPF/DKIM/DMARC, spam and virus verdicts.
readrequiredboolean
tagsrequiredEmailTag[]
scheduledAtrequiredISO date | null
daterequiredISO date
createdAtrequiredISO date

RenderedTemplate

FieldTypeDescription
subjectrequiredstring
htmlrequiredstring | null
textrequiredstring | null

Suppression

FieldTypeDescription
idrequiredstring
emailrequiredstring
reasonrequired"bounce" | "complaint" | "manual" | "unsubscribe"
sourceMessageIdrequiredstring | null
sourceThreadIdrequiredstring | null
detailsrequiredstring | null
createdByUserIdrequiredstring | null
createdAtrequiredISO date

Template

FieldTypeDescription
idrequiredstring
namerequiredstring
aliasrequiredstring | nullStable handle to send with instead of the id, e.g. "welcome".
statusrequired"draft" | "published"
publishedAtrequiredISO date | null
hasUnpublishedChangesrequiredboolean
createdAtrequiredISO date
updatedAtrequiredISO date
subjectrequiredstring
htmlrequiredstring | null
textrequiredstring | null
fromrequiredstring | null
replyTorequiredstring[]
variablesrequiredTemplateVariable[]

TemplateVariable

FieldTypeDescription
keyrequiredstringReferenced as {{key}} (HTML-escaped) or {{{key}}} (raw).
typerequired"string" | "number"
fallbackrequiredstring | number | null

Thread

FieldTypeDescription
idrequiredstring
mailboxIdrequiredstring
subjectrequiredstring
snippetrequiredstring
participantsrequiredEmailAddress[]
messageCountrequiredinteger-9007199254740991–9007199254740991
unreadCountrequiredinteger-9007199254740991–9007199254740991
hasAttachmentsrequiredboolean
starredrequiredboolean
statusrequired"inbox" | "archived" | "trash" | "spam"
lastMessageAtrequiredISO date
lastDirectionrequired"inbound" | "outbound"

ThreadWithMessages

FieldTypeDescription
idrequiredstring
mailboxIdrequiredstring
subjectrequiredstring
snippetrequiredstring
participantsrequiredEmailAddress[]
messageCountrequiredinteger-9007199254740991–9007199254740991
unreadCountrequiredinteger-9007199254740991–9007199254740991
hasAttachmentsrequiredboolean
starredrequiredboolean
statusrequired"inbox" | "archived" | "trash" | "spam"
lastMessageAtrequiredISO date
lastDirectionrequired"inbound" | "outbound"
messagesrequiredMessage[]

Webhook

FieldTypeDescription
idrequiredstring
urlrequiredstring
eventsrequired("email.received" | "email.scheduled" | "email.sent" | "email.delivery_delayed" | "email.delivered" | "email.bounced" | "email.complained" | "email.failed" | "email.suppressed" | "email.opened" | "email.clicked" | "domain.verified")[]
enabledrequiredboolean
secretstringOnly on create: verifies the FutureMail-Signature header.
lastDeliveryAtrequiredISO date | null
lastDeliveryStatusrequiredinteger | null
createdAtrequiredISO date

WebhookDelivery

FieldTypeDescription
idrequiredstring
webhookIdrequiredstring
eventIdrequiredstringStable across retries and replays: dedupe on it.
eventrequired"email.received" | "email.scheduled" | "email.sent" | "email.delivery_delayed" | "email.delivered" | "email.bounced" | "email.complained" | "email.failed" | "email.suppressed" | "email.opened" | "email.clicked" | "domain.verified"
attemptrequiredinteger-9007199254740991–9007199254740991
statusCoderequiredinteger | null
successrequiredboolean
durationMsrequiredinteger | null
nextRetryAtrequiredISO date | null
createdAtrequiredISO date
payloadThe JSON body that was POSTed: { id, type, createdAt, data }.