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.
| Parameter | In | Type | Description |
|---|---|---|---|
Idempotency-Key | header | string | Safe retries: the same key within 24 hours returns the original response instead of sending again (≤256 chars). |
Body
| Field | Type | Description |
|---|---|---|
from | address | An address you own: "me@mydomain.com", "Me <me@mydomain.com>" or { address, name }. Optional when template has a default sender. |
torequired | address | address[] | One recipient or an array (To + Cc + Bcc count toward your plan's recipient caps). |
cc | address | address[] | Copy recipients. |
bcc | address | address[] | Blind-copy recipients. |
replyTo | address | address[] | Reply-To address(es). |
subject | string | Subject line. · default "", max 998 chars |
text | string | Plain-text body. |
html | string | HTML body. |
attachments | AttachmentInput[] | 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 |
headers | map | Custom headers, e.g. List-Unsubscribe. Headers FutureMail sets itself are rejected. |
inReplyTo | string | Our message id to thread this email onto (sets In-Reply-To/References). |
tags | EmailTag[] | Labels echoed in webhooks and filterable with GET /v1/emails?tag=. Max 50. · max 50 |
scheduledAt | ISO date | Send later (ISO 8601 with offset, up to 30 days ahead). Cancel or reschedule until then. |
template | object | Render 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.
| Parameter | In | Type | Description |
|---|---|---|---|
Idempotency-Key | header | string | Safe 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
| Field | Type | Description |
|---|---|---|
from | address | An address you own: "me@mydomain.com", "Me <me@mydomain.com>" or { address, name }. Optional when template has a default sender. |
torequired | address | address[] | One recipient or an array (To + Cc + Bcc count toward your plan's recipient caps). |
cc | address | address[] | Copy recipients. |
bcc | address | address[] | Blind-copy recipients. |
replyTo | address | address[] | Reply-To address(es). |
subject | string | Subject line. · default "", max 998 chars |
text | string | Plain-text body. |
html | string | HTML body. |
attachments | AttachmentInput[] | 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 |
headers | map | Custom headers, e.g. List-Unsubscribe. Headers FutureMail sets itself are rejected. |
inReplyTo | string | Our message id to thread this email onto (sets In-Reply-To/References). |
tags | EmailTag[] | Labels echoed in webhooks and filterable with GET /v1/emails?tag=. Max 50. · max 50 |
scheduledAt | ISO date | Send later (ISO 8601 with offset, up to 30 days ahead). Cancel or reschedule until then. |
template | object | Render 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.
| Parameter | In | Type | Description |
|---|---|---|---|
q | query | string | Search query. |
mailboxId | query | string | Only this mailbox. |
direction | query | "inbound" | "outbound" | inbound or outbound. |
status | query | string | Comma-separated statuses, e.g. bounced,complained. |
tag | query | string | name:value, or name for any value. |
from | query | string | Substring of the sender address or name. |
to | query | string | Substring of a recipient address (To, Cc or Bcc). |
since | query | ISO date | Only emails after this ISO date. |
until | query | ISO date | Only emails before this ISO date. |
unread | query | boolean | Only unread emails. |
templateId | query | string | Only emails sent with this template (id or alias). |
limit | query | integer | Page size, 1–100 (default 100). |
cursor | query | string | The 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.
| Parameter | In | Type | Description |
|---|---|---|---|
from | query | string | Substring of the sender address or name. |
to | query | string | Substring of a recipient address. |
subject | query | string | Substring of the subject. |
mailboxId | query | string | Only this mailbox. |
since | query | ISO date | Also accept emails that arrived since this ISO date (default: now). |
timeout | query | integer | Seconds 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Email 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.
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Email id (msg_…). |
Body
| Field | Type | Description |
|---|---|---|
read | boolean | Mark read or unread. |
scheduledAt | ISO date | Reschedule 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Email 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.
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Email 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.
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Email id (msg_…). |
Idempotency-Key | header | string | Safe retries: the same key within 24 hours returns the original response instead of sending again (≤256 chars). |
Body
| Field | Type | Description |
|---|---|---|
text | string | Plain-text reply (the original is quoted). |
html | string | HTML reply. |
replyAll | boolean | Also reply to the original's other recipients. · default false |
from | address | Override the From address (default: the address the original was sent to). |
cc | address | address[] | Extra copy recipients. |
attachments | AttachmentInput[] | 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.
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Email id (msg_…). |
Idempotency-Key | header | string | Safe retries: the same key within 24 hours returns the original response instead of sending again (≤256 chars). |
Body
| Field | Type | Description |
|---|---|---|
torequired | address | address[] | Recipient(s). |
text | string | Note above the forwarded message. |
from | address | Override 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Email 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Attachment id (from Message.attachments). |
download | query | boolean | Send 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
| Parameter | In | Type | Description |
|---|---|---|---|
folder | query | "inbox" | "starred" | "sent" | "drafts" | "all" | "spam" | "trash" | Folder (default inbox). |
mailboxId | query | string | Only this mailbox. |
q | query | string | Search query (same syntax as emails). |
limit | query | integer | Page size, 1–100 (default 100). |
cursor | query | string | The 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Thread 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Thread id. |
Body
| Field | Type | Description |
|---|---|---|
status | "inbox" | "archived" | "trash" | "spam" | Move the thread. |
starred | boolean | Star or unstar. |
read | boolean | Mark 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
| Field | Type | Description |
|---|---|---|
status | "inbox" | "archived" | "trash" | "spam" | |
starred | boolean | |
read | boolean | |
idsrequired | string[] | 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Thread 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
| Parameter | In | Type | Description |
|---|---|---|---|
limit | query | integer | Page size, 1–100 (default 100). |
cursor | query | string | The 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
| Field | Type | Description |
|---|---|---|
namerequired | string | The 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Domain 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.
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Domain 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.
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Domain id or name. |
Body
| Field | Type | Description |
|---|---|---|
catchAllMailboxId | string | null | Mailbox that receives mail for unknown addresses on this domain (null turns catch-all off). |
mailFromSubdomain | string | Custom 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. |
openTracking | boolean | Add a tracking pixel to outgoing HTML (email.opened). |
clickTracking | boolean | Rewrite links through the tracker (email.clicked). |
trackingSubdomain | string | null | Custom 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Domain 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
| Parameter | In | Type | Description |
|---|---|---|---|
mailboxId | query | string | Only this mailbox. |
limit | query | integer | Page size, 1–100 (default 100). |
cursor | query | string | The 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).
| Parameter | In | Type | Description |
|---|---|---|---|
addressrequired | query | string | Full 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
| Field | Type | Description |
|---|---|---|
addressrequired | string | Full address on one of your domains, e.g. hello@acme.com. |
mailboxId | string | Mailbox to deliver to (default: your personal mailbox). |
displayName | string | null | Name 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Address id or the address itself. |
Body
| Field | Type | Description |
|---|---|---|
mailboxId | string | Move the address to another mailbox. |
displayName | string | null | Name 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Address 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
| Parameter | In | Type | Description |
|---|---|---|---|
limit | query | integer | Page size, 1–100 (default 100). |
cursor | query | string | The 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
| Field | Type | Description |
|---|---|---|
namerequired | string | Mailbox name. · max 60 chars |
kind | "personal" | "agent" | personal or agent. · default "personal" |
description | string | null | What it's for. |
color | string | Hex 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Mailbox 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Mailbox id. |
Body
| Field | Type | Description |
|---|---|---|
name | string | max 60 chars |
description | string | null | |
color | string | |
signature | string | null | Appended to replies written in the web app. |
primaryAddress | string | Default 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Mailbox 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
| Field | Type | Description |
|---|---|---|
namerequired | string | Human name, e.g. "Signup bot". · max 60 chars |
address | string | The agent's address on one of your domains. |
description | string | null | What 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
| Parameter | In | Type | Description |
|---|---|---|---|
limit | query | integer | Page size, 1–100 (default 100). |
cursor | query | string | The 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
| Field | Type | Description |
|---|---|---|
namerequired | string | Internal name. · max 100 chars |
alias | string | null | Stable handle to send with, e.g. "welcome". |
subject | string | Subject; may use {{variables}}. · default "", max 998 chars |
html | string | null | HTML body; {{var}} is HTML-escaped, {{{var}}} is raw. |
text | string | null | Plain-text body. |
from | string | null | Default sender for sends that don't pass one. |
replyTo | string[] | Default Reply-To addresses. · max 10 |
variables | object[] | Declared variables: { key, type, fallback }. A missing variable without a fallback is a 422. · max 50 |
publish | boolean | Publish 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Template 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Template id or alias. |
Body
| Field | Type | Description |
|---|---|---|
name | string | max 100 chars |
alias | string | null | |
subject | string | max 998 chars |
html | string | null | |
text | string | null | |
from | string | null | |
replyTo | string[] | max 10 |
variables | object[] | 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Template 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Template id or alias. |
Body
| Field | Type | Description |
|---|---|---|
variables | map | Values for the template's variables. · default {} |
draft | boolean | Render 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Template 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.
| Parameter | In | Type | Description |
|---|---|---|---|
reason | query | "bounce" | "complaint" | "manual" | "unsubscribe" | Only this reason. |
q | query | string | Substring of the address. |
limit | query | integer | Page size, 1–100 (default 100). |
cursor | query | string | The 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
| Field | Type | Description |
|---|---|---|
email | string | One address. · max 320 chars |
emails | string[] | Many addresses (up to 1,000 in total). · max 1000 |
reason | "manual" | "unsubscribe" | manual (default) or unsubscribe. · default "manual" |
details | string | null | Note 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
| Field | Type | Description |
|---|---|---|
emailsrequired | string[] | 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
| Parameter | In | Type | Description |
|---|---|---|---|
emailrequired | path | string | The 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
| Parameter | In | Type | Description |
|---|---|---|---|
emailrequired | path | string | The 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
| Parameter | In | Type | Description |
|---|---|---|---|
limit | query | integer | Page size, 1–100 (default 100). |
cursor | query | string | The 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
| Field | Type | Description |
|---|---|---|
namerequired | string | Label for the key. · max 60 chars |
scopes | ("full" | "send" | "read")[] | full, send and/or read. · default ["full"] |
mailboxId | string | null | Restrict the key to one mailbox. |
domainId | string | null | Restrict sending to addresses on one domain. |
expiresAt | ISO date | null | When 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.
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Key id (key_…). |
Body
| Field | Type | Description |
|---|---|---|
name | string | max 60 chars |
scopes | ("full" | "send" | "read")[] | |
mailboxId | string | null | null removes the mailbox restriction. |
domainId | string | null | null 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Key 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
| Parameter | In | Type | Description |
|---|---|---|---|
limit | query | integer | Page size, 1–100 (default 100). |
cursor | query | string | The 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
| Field | Type | Description |
|---|---|---|
urlrequired | string | HTTPS 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Webhook id. |
Body
| Field | Type | Description |
|---|---|---|
url | string | |
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")[] | |
enabled | boolean | Pause (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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Webhook 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
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Webhook 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.
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Webhook id. |
limit | query | integer | Page size, 1–100 (default 100). |
cursor | query | string | The 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.
| Parameter | In | Type | Description |
|---|---|---|---|
idrequired | path | string | Webhook id. |
deliveryIdrequired | path | string | Delivery 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
| Field | Type | Description |
|---|---|---|
addedrequired | Suppression[] | |
alreadySuppressedrequired | string[] |
Address
| Field | Type | Description |
|---|---|---|
idrequired | string | |
addressrequired | string | |
localPartrequired | string | |
domainIdrequired | string | |
domainrequired | string | |
mailboxIdrequired | string | |
displayNamerequired | string | null | |
createdAtrequired | ISO date |
AddressAvailability
| Field | Type | Description |
|---|---|---|
availablerequired | boolean | |
reason | string |
AgentProvisioned
| Field | Type | Description |
|---|---|---|
mailboxrequired | Mailbox | |
addressrequired | Address | |
apiKeyrequired | ApiKeyWithSecret |
ApiKey
| Field | Type | Description |
|---|---|---|
idrequired | string | |
namerequired | string | |
prefixrequired | string | First characters of the key, e.g. "fm_live_3kd9". |
scopesrequired | ("full" | "send" | "read")[] | |
mailboxIdrequired | string | null | Only this mailbox (agent keys). |
domainIdrequired | string | null | Only send from addresses on this domain. |
expiresAtrequired | ISO date | null | |
createdByUserIdrequired | string | null | |
lastUsedAtrequired | ISO date | null | |
createdAtrequired | ISO date |
ApiKeyWithSecret
| Field | Type | Description |
|---|---|---|
idrequired | string | |
namerequired | string | |
prefixrequired | string | First characters of the key, e.g. "fm_live_3kd9". |
scopesrequired | ("full" | "send" | "read")[] | |
mailboxIdrequired | string | null | Only this mailbox (agent keys). |
domainIdrequired | string | null | Only send from addresses on this domain. |
expiresAtrequired | ISO date | null | |
createdByUserIdrequired | string | null | |
lastUsedAtrequired | ISO date | null | |
createdAtrequired | ISO date | |
keyrequired | string | The full secret. Only returned once, at creation. |
Attachment
| Field | Type | Description |
|---|---|---|
idrequired | string | |
filenamerequired | string | |
contentTyperequired | string | |
sizerequired | integer | -9007199254740991–9007199254740991 |
contentIdrequired | string | null | |
inlinerequired | boolean | |
urlrequired | string | API path to download the file. |
blockedrequired | boolean | The message failed the virus scan: the file can't be downloaded. |
AttachmentInput
| Field | Type | Description |
|---|---|---|
filenamerequired | string | |
content | string | |
path | string | |
contentType | string | |
contentId | string | max 255 chars |
Billing
| Field | Type | Description |
|---|---|---|
enabledrequired | boolean | False 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. |
graceEndsAtrequired | ISO date | null | |
complimentaryrequired | boolean | |
statusrequired | "active" | "trialing" | "past_due" | "canceled" | "unpaid" | "incomplete" | "incomplete_expired" | "paused" | null | |
currentPeriodEndrequired | ISO date | null | |
cancelAtPeriodEndrequired | boolean | |
cancelAtrequired | ISO date | null | |
checkoutUrlrequired | string | null | |
portalUrlrequired | string | null | |
limitsrequired | object | |
plansrequired | object[] |
CheckSuppressionsResult
| Field | Type | Description |
|---|---|---|
suppressedrequired | Suppression[] | |
notSuppressedrequired | string[] |
Deleted
| Field | Type | Description |
|---|---|---|
deletedrequired | true |
DnsRecord
| Field | Type | Description |
|---|---|---|
purposerequired | "verification" | "dkim" | "mx" | "spf" | "mail_from_mx" | "mail_from_spf" | "dmarc" | "tracking" | |
typerequired | "MX" | "TXT" | "CNAME" | |
namerequired | string | Host relative to the domain ("@" for the apex). |
fqdnrequired | string | |
valuerequired | string | |
priority | integer | null | |
statusrequired | "pending" | "verified" | "failed" | |
descriptionrequired | string | |
note | string | null |
Domain
| Field | Type | Description |
|---|---|---|
idrequired | string | |
namerequired | string | |
statusrequired | "pending" | "verified" | "failed" | |
sendingEnabledrequired | boolean | DKIM verified: you can send as this domain. |
receivingEnabledrequired | boolean | MX points at FutureMail: mail to this domain is received. |
catchAllMailboxIdrequired | string | null | |
recordsrequired | DnsRecord[] | |
warningsrequired | DomainWarning[] | |
addressCountrequired | integer | -9007199254740991–9007199254740991 |
createdAtrequired | ISO date | |
verifiedAtrequired | ISO date | null | |
lastCheckedAtrequired | ISO date | null | |
mailFromSubdomainrequired | string | |
tlsPolicyrequired | "opportunistic" | "require" | |
openTrackingrequired | boolean | |
clickTrackingrequired | boolean | |
trackingSubdomainrequired | string | null | |
trackingHostrequired | string |
DomainWarning
| Field | Type | Description |
|---|---|---|
coderequired | "mx_conflict" | "spf_missing" | "spf_incomplete" | "spf_multiple" | "dmarc_missing" | "leftover_dkim" | |
severityrequired | "warning" | "info" | |
titlerequired | string | |
detailrequired | string | |
records | object[] | |
fix | object |
EmailAddress
| Field | Type | Description |
|---|---|---|
addressrequired | string | |
name | string | null |
EmailEvent
| Field | Type | Description |
|---|---|---|
idrequired | string | |
messageIdrequired | string | |
typerequired | "queued" | "scheduled" | "canceled" | "sent" | "delivery_delayed" | "delivered" | "bounced" | "complained" | "failed" | "suppressed" | "opened" | "clicked" | |
datarequired | map | null | |
createdAtrequired | ISO date |
EmailTag
| Field | Type | Description |
|---|---|---|
namerequired | string | |
valuerequired | string |
Mailbox
| Field | Type | Description |
|---|---|---|
idrequired | string | |
namerequired | string | |
kindrequired | "personal" | "agent" | |
descriptionrequired | string | null | |
colorrequired | string | |
primaryAddressrequired | string | null | Default From address when sending from this mailbox. |
addressesrequired | string[] | |
unreadCountrequired | integer | -9007199254740991–9007199254740991 |
signaturerequired | string | null | |
createdAtrequired | ISO date |
Me
| Field | Type | Description |
|---|---|---|
limitsrequired | object | |
billingrequired | Billing | |
userrequired | object | |
orgrequired | object | |
rolerequired | "owner" | "admin" | "developer" | "viewer" | null | Null for API keys (their scopes apply instead). |
orgsrequired | object[] | |
onboardingrequired | object | |
staffrequired | boolean | |
featuresrequired | object | |
providerrequired | "local" | "ses" | |
apiKey | object | null | The key making this request (null for browser sessions). |
smtp | object |
Message
| Field | Type | Description |
|---|---|---|
idrequired | string | |
threadIdrequired | string | |
mailboxIdrequired | string | |
directionrequired | "inbound" | "outbound" | |
statusrequired | "received" | "queued" | "scheduled" | "canceled" | "sent" | "delivery_delayed" | "delivered" | "bounced" | "complained" | "failed" | |
messageIdrequired | string | RFC 5322 Message-ID header, without angle brackets. |
inReplyTorequired | string | null | |
referencesrequired | string[] | |
fromrequired | EmailAddress | |
torequired | EmailAddress[] | |
ccrequired | EmailAddress[] | |
bccrequired | EmailAddress[] | |
replyTorequired | EmailAddress[] | |
subjectrequired | string | |
textrequired | string | null | |
htmlrequired | string | null | |
snippetrequired | string | |
attachmentsrequired | Attachment[] | |
extractedrequired | object | One-time code and links found in the email (great for agents). |
authenticationrequired | object | null | Inbound only: SPF/DKIM/DMARC, spam and virus verdicts. |
readrequired | boolean | |
tagsrequired | EmailTag[] | |
scheduledAtrequired | ISO date | null | |
daterequired | ISO date | |
createdAtrequired | ISO date |
RenderedTemplate
| Field | Type | Description |
|---|---|---|
subjectrequired | string | |
htmlrequired | string | null | |
textrequired | string | null |
Suppression
| Field | Type | Description |
|---|---|---|
idrequired | string | |
emailrequired | string | |
reasonrequired | "bounce" | "complaint" | "manual" | "unsubscribe" | |
sourceMessageIdrequired | string | null | |
sourceThreadIdrequired | string | null | |
detailsrequired | string | null | |
createdByUserIdrequired | string | null | |
createdAtrequired | ISO date |
Template
| Field | Type | Description |
|---|---|---|
idrequired | string | |
namerequired | string | |
aliasrequired | string | null | Stable handle to send with instead of the id, e.g. "welcome". |
statusrequired | "draft" | "published" | |
publishedAtrequired | ISO date | null | |
hasUnpublishedChangesrequired | boolean | |
createdAtrequired | ISO date | |
updatedAtrequired | ISO date | |
subjectrequired | string | |
htmlrequired | string | null | |
textrequired | string | null | |
fromrequired | string | null | |
replyTorequired | string[] | |
variablesrequired | TemplateVariable[] |
TemplateVariable
| Field | Type | Description |
|---|---|---|
keyrequired | string | Referenced as {{key}} (HTML-escaped) or {{{key}}} (raw). |
typerequired | "string" | "number" | |
fallbackrequired | string | number | null |
Thread
| Field | Type | Description |
|---|---|---|
idrequired | string | |
mailboxIdrequired | string | |
subjectrequired | string | |
snippetrequired | string | |
participantsrequired | EmailAddress[] | |
messageCountrequired | integer | -9007199254740991–9007199254740991 |
unreadCountrequired | integer | -9007199254740991–9007199254740991 |
hasAttachmentsrequired | boolean | |
starredrequired | boolean | |
statusrequired | "inbox" | "archived" | "trash" | "spam" | |
lastMessageAtrequired | ISO date | |
lastDirectionrequired | "inbound" | "outbound" |
ThreadWithMessages
| Field | Type | Description |
|---|---|---|
idrequired | string | |
mailboxIdrequired | string | |
subjectrequired | string | |
snippetrequired | string | |
participantsrequired | EmailAddress[] | |
messageCountrequired | integer | -9007199254740991–9007199254740991 |
unreadCountrequired | integer | -9007199254740991–9007199254740991 |
hasAttachmentsrequired | boolean | |
starredrequired | boolean | |
statusrequired | "inbox" | "archived" | "trash" | "spam" | |
lastMessageAtrequired | ISO date | |
lastDirectionrequired | "inbound" | "outbound" | |
messagesrequired | Message[] |
Webhook
| Field | Type | Description |
|---|---|---|
idrequired | string | |
urlrequired | string | |
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")[] | |
enabledrequired | boolean | |
secret | string | Only on create: verifies the FutureMail-Signature header. |
lastDeliveryAtrequired | ISO date | null | |
lastDeliveryStatusrequired | integer | null | |
createdAtrequired | ISO date |
WebhookDelivery
| Field | Type | Description |
|---|---|---|
idrequired | string | |
webhookIdrequired | string | |
eventIdrequired | string | Stable 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" | |
attemptrequired | integer | -9007199254740991–9007199254740991 |
statusCoderequired | integer | null | |
successrequired | boolean | |
durationMsrequired | integer | null | |
nextRetryAtrequired | ISO date | null | |
createdAtrequired | ISO date | |
payload | The JSON body that was POSTed: { id, type, createdAt, data }. |