Mailfully logo

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.

1
Create a test API key

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.

2
Send a test email

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.

3
Check the result

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.

4
Go live

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>"
  }'

Next steps

Webhooks

Get delivery and bounce events pushed to your endpoint instead of polling.

Templates

Store reusable email content and send with variables.

Test mode

Force bounces and complaints, and read back which recipient was affected.

Choosing a sending feature

Pick between a single send, a batch, a scheduled send, a template, and a marketing send.