Sending email
One endpoint sends your mail: POST /v1/emails. Bearer-key auth, JSON in, a message id out, webhooks for everything after.
Send with a single authenticated request to POST https://api.mail.atrix.dev/v1/emails. Authentication is an am_live_ bearer key with the email.send scope — see Authentication.
Send an email
curl -X POST https://api.mail.atrix.dev/v1/emails \ -H "Authorization: Bearer $ATRIX_MAIL_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "Yourco Billing <billing@yourco.com>", "to": ["ava@client.io"], "subject": "Your March invoice", "html": "<p>Invoice attached. Thanks!</p>", "text": "Invoice attached. Thanks!", "reply_to": "support@yourco.com" }'
A successful request returns 201 Created with the message id:
{ "id": "6f6d2b1a-3c9e-4a51-8f2b-2d7c1a0e9b44", "status": "queued" }
queued means we accepted the message; delivery is asynchronous. Track it with webhooks or fetch it back with GET /v1/emails/:id (needs the emails.read scope).
Request fields
| Field | Type | Notes |
|---|---|---|
| from | string | Required. Address on a verified domain; Name <addr> form allowed. |
| to | string | string[] | Required. 1–50 addresses. |
| subject | string | Required. Up to 998 characters. |
| html | string | At least one of html / text is required. |
| text | string | Plain-text body; strongly recommended alongside html. |
| cc | string | string[] | Optional. |
| bcc | string | string[] | Optional. |
| reply_to | string | Optional. |
| headers | object | Optional map of custom header name → value. |
| attachments | object[] | Optional. See below. Up to 20 files. |
| idempotency_key | string | Optional safe-retry key; see Idempotency. |
The combined recipient count across to, cc, and bcc is capped at 50 per request. Each address may be a bare addr or a Name <addr> pair.
From-address rules
The domain part of from must belong to your organization, be active (DNS verified), and have sending enabled. Otherwise the request is rejected with 403 domain_not_verified. There are no shared or sandbox sending domains — you always send from a domain you own.
Attachments
Attach files inline as base64. Each entry is { filename, content, contentType? } — the decoded total across all attachments must stay under 10 MB, with at most 20 files.
{ "from": "billing@yourco.com", "to": "ava@client.io", "subject": "Your March invoice", "text": "Invoice attached.", "attachments": [ { "filename": "invoice-1042.pdf", "content": "JVBERi0xLjQKJ...", // base64 "contentType": "application/pdf" // optional; inferred if omitted } ] }
Idempotency
Network timeouts happen mid-request. To retry safely, include an idempotency_key field — any unique string per logical message. If a send with that key was already accepted for your organization, the original message id is returned and no second email is sent.
curl -X POST https://api.mail.atrix.dev/v1/emails \ -H "Authorization: Bearer $ATRIX_MAIL_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "billing@yourco.com", "to": ["ava@client.io"], "subject": "Your March invoice", "text": "Attached.", "idempotency_key": "invoice-1042" }'
idempotency_key is a field in the JSON body, not an HTTP header. Reuse the exact same key on every retry of the same message.Errors
Errors are JSON with a stable machine-readable code and a human message. Validation errors add a fields map keyed by the offending field:
{ "error": { "code": "validation_error", "message": "Validation failed", "fields": { "html": "Either html or text body is required" } } }
| Status | Code | Meaning |
|---|---|---|
| 422 | validation_error | Body failed schema validation; fields lists what. |
| 401 | unauthorized | Missing, malformed, or revoked API key. |
| 403 | forbidden | Key lacks the email.send scope. |
| 403 | domain_not_verified | The from domain isn't verified/send-enabled on your account. |
| 403 | org_suspended | Your organization is suspended from sending. |
| 422 | suppressed_recipient | A recipient is on your suppression list; details lists them. |
| 429 | rate_limited | Over your plan's send budget. Honour the Retry-After header. |
| 404 | not_found | Unknown message id (on GET /v1/emails/:id). |
| 500 | internal_error | Our fault. Safe to retry with the same idempotency_key. |
429 and 5xx as retryable with exponential backoff; treat every other 4xx as a bug in the request, not something to retry. See Rate limits & suppression.