Quickstart
Send your first email through the Mailfully API in test mode, then verify a domain to go live.
Send an email with a test API key, check that it was delivered, then go live. Test mode needs no domain, so you can start now.
Open dashboard.mailfully.com/api-keys, switch the environment toggle to Test, and click Create key. Give it a name and keep the default send scope, which is all this guide needs.
The secret is shown once and starts with mf_test_. Copy it and store it where your code can read it:
export MAILFULLY_API_KEY="mf_test_xxxxxxxxxxxx"Authentication explains how test and live keys differ.
A test send runs the real send path but delivers nothing to a real inbox, so from can be any address and you don't need a verified domain. Send to success@example.com to get a delivered outcome.
curl -X POST https://api.mailfully.com/v1/emails \
-H "Authorization: Bearer mf_test_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"from": "orders@mail.acme.com",
"to": "success@example.com",
"subject": "Your order shipped",
"html": "<p>Your order is on its way.</p>"
}'The API responds 202 Accepted with the accepted message and a Location: /v1/emails/{id} header:
{
"object": "email",
"id": "01J9ZCE1Q6XKD8R4W2N7VT3MHB",
"status": "queued",
"type": "transactional",
"to": ["success@example.com"],
"cc": [],
"bcc": [],
"from": "orders@mail.acme.com",
"subject": "Your order shipped",
"scheduled_at": null,
"created_at": "2026-09-11T14:15:00.000Z"
}The local part of the to address picks the simulated outcome: bounce@ bounces, complaint@ reports spam, and anything else delivers. Test mode lists them all.
Fetch the message by id:
curl https://api.mailfully.com/v1/emails/01J9ZCE1Q6XKD8R4W2N7VT3MHB \
-H "Authorization: Bearer mf_test_xxxxxxxxxxxx"{
"object": "email",
"id": "01J9ZCE1Q6XKD8R4W2N7VT3MHB",
"to": ["success@example.com"],
"from": "orders@mail.acme.com",
"created_at": "2026-09-11T14:15:00.000Z",
"type": "transactional",
"subject": "Your order shipped",
"html": "<p>Your order is on its way.</p>",
"text": null,
"cc": [],
"bcc": [],
"reply_to": [],
"last_event": "delivered",
"scheduled_at": null,
"tags": []
}last_event reads delivered within a few seconds. For the full timeline, use GET /v1/emails/{id}/events, and see How sending works for every status.
Live sends need a verified domain. Without one, POST /v1/emails returns 403 unverified_domain_required. While DNS propagates, keep building against test mode.
Add a subdomain you control, either in the dashboard or through the API. Use a live key (mf_live_..., created with the toggle on Live):
curl -X POST https://api.mailfully.com/v1/domains \
-H "Authorization: Bearer mf_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "name": "mail.acme.com" }'Replace mail.acme.com with a subdomain of a domain you own. The 201 response includes six DNS records to publish: three DKIM CNAMEs, one MX and two TXT records (abbreviated here):
{
"object": "domain",
"id": "dom_01J9ZC7XW2K5M8P3Q4R6T9VBHD",
"name": "mail.acme.com",
"status": "pending",
"records": [
{ "type": "CNAME", "name": "6gbrjpgwjskckoa6a5zn6fwqkn67xbtw._domainkey.mail.acme.com", "value": "6gbrjpgwjskckoa6a5zn6fwqkn67xbtw.dkim.mailfully.com" },
{ "type": "CNAME", "name": "yybhvng4tqglsubhkkbwfwbtvvxjb5lb._domainkey.mail.acme.com", "value": "yybhvng4tqglsubhkkbwfwbtvvxjb5lb.dkim.mailfully.com" },
{ "type": "CNAME", "name": "wrx2s7fgbfyvfh2c3wch5bqk3gpjrmtp._domainkey.mail.acme.com", "value": "wrx2s7fgbfyvfh2c3wch5bqk3gpjrmtp.dkim.mailfully.com" },
{ "type": "MX", "name": "send.mail.acme.com", "value": "feedback-smtp.us-east-1.amazonses.com", "priority": 10 },
{ "type": "TXT", "name": "send.mail.acme.com", "value": "v=spf1 include:_spf.mailfully.com ~all" },
{ "type": "TXT", "name": "_dmarc.mail.acme.com", "value": "v=DMARC1; p=none;" }
]
}Add each record at your DNS provider, then trigger a verification check:
curl -X POST https://api.mailfully.com/v1/domains/dom_01J9ZC7XW2K5M8P3Q4R6T9VBHD/verify \
-H "Authorization: Bearer mf_live_xxxxxxxxxxxx"Re-run until status flips from pending to verified. Mailfully keeps checking your DNS for up to 72 hours after the domain is created; each verify call reads its latest result. Verify a domain covers per-record statuses, tracking, and troubleshooting.
Once the domain is verified, send from an address on it with your live key. Replace you@yourcompany.com with an inbox you own, then check it. Until we review a new account, its live sending has a lower daily limit; see New accounts. Verifying your domain moves that review along.
curl -X POST https://api.mailfully.com/v1/emails \
-H "Authorization: Bearer mf_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"from": "orders@mail.acme.com",
"to": "you@yourcompany.com",
"subject": "Your order shipped",
"html": "<p>Your order is on its way.</p>"
}'