Send via REST API
Send transactional and marketing emails programmatically using the Axene Mailer REST API.
The REST API is the most flexible way to send email through Axene Mailer. All requests go to https://mail.axene.io/v1/ and require an API key.
Authentication
Include your API key in the Authorization header on every request. API keys are prefixed with axm_k_:
Generate API keys at Settings -> API Keys -> Create Key.
POST /v1/emails/
Send a single email. Note the trailing slash on the path.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
from_ | object | Yes | Sender address. Must be a registered sender on a verified domain. |
from_.email | string | Yes | Sender email address. |
from_.name | string | No | Sender display name. |
to | array | Yes | Array of recipient {email, name} objects. At least one required. |
to[].email | string | Yes | Recipient email address. |
to[].name | string | No | Recipient display name. |
subject | string | Yes | Email subject line. |
html | string | No | HTML body. At least one of html or text is required. |
text | string | No | Plain text body. At least one of html or text is required. |
cc | array | No | Array of {email, name} objects for CC recipients. |
bcc | array | No | Array of {email, name} objects for BCC recipients. |
reply_to | object | No | {email, name} object for the Reply-To header. |
headers | object | No | Key-value pairs of custom email headers. |
tags | array | No | Array of strings for categorization and filtering. |
send_at | string | No | ISO 8601 datetime to schedule delivery. Omit to send immediately. See Scheduling. |
attachments | array | No | Array of attachment objects (see below). |
Attachments
Each attachment is an object with the file body base64-encoded:
| Field | Type | Required | Description |
|---|---|---|---|
filename | string | Yes | File name. Must not contain path separators (/, \) or NUL. |
content_base64 | string | Yes | File body as base64. No data: URL prefix. |
content_type | string | No | MIME type. Defaults to application/octet-stream. |
Each file is capped at 24 MiB, and the combined size of all attachments is also capped at 24 MiB. Oversized attachments return 413.
Example: cURL
Example: Node.js (fetch)
Example: Python (requests)
Response
A successful request returns 202 Accepted:
| Field | Description |
|---|---|
id | The email's UUID. Use this to query status, events, or retry. |
status | queued for an immediate send, scheduled if send_at was provided. |
message_id | RFC 5322 Message-ID header value. |
rejection_reason | Reason the email was rejected, or null if accepted. |
A 202 means the email was accepted for delivery, not that it was delivered. Use webhooks or the events endpoint to track delivery status.
Sending to a suppressed recipient
If any to, cc, or bcc address is on your suppression list, the send is rejected immediately, in this same 202 response, instead of being silently dropped later:
Addresses land on the suppression list automatically (hard bounces, unsubscribes) as well as manually, and sometimes that happens by mistake. Rather than requiring a dashboard trip to remove the suppression, a single deliberate retry within 24 hours is treated as an override:
- First attempt -> rejected, with the reason above. Nothing is sent.
- Retry within 24 hours, using
POST /v1/emails/:id/retryon the rejected email (or by callingPOST /v1/emails/again with the same recipient) -> delivered once. - Every attempt after that -> rejected again. The override is spent; the address stays suppressed until it's explicitly removed from the suppression list.
If you don't retry within the 24-hour window, the next attempt is treated as a fresh first attempt (rejected again, starting a new 24-hour window) rather than leaving an indefinitely pending override.
The override is a safety net for accidental suppressions, not a way to force mail to a genuinely bad address. Each suppressed address gets exactly one override; use it deliberately.
POST /v1/emails/validate
Dry-run validation. Checks whether an email would send without actually sending it. It validates the sender address, domain verification, plan send limits, recipients, content, and account restrictions. The request body is identical to POST /v1/emails/.
Response:
If a check fails, issues contains one or more {field, error} objects and both valid and can_send are false. A suppressed recipient that's still eligible for a retry override (see above) does not raise an issue here, since that send would actually go through; a recipient whose override is already spent, or that hasn't been retried yet, does.
validate never consumes a suppressed address's retry override or counts as an attempt against it. It only reports what would currently happen.
GET /v1/emails/:id
Retrieve a single email by its UUID, including its bodies, headers, and event history.
GET /v1/emails/:id/events
List every tracked event for an email (queued, sent, delivered, opened, clicked, bounced, cancelled, and so on). Each event has an event_type, optional metadata, and a created_at timestamp.
POST /v1/emails/:id/retry
Retry a previously failed email. Only emails with status bounced, rejected, or failed can be retried; any other status returns 409 Conflict. The retry creates a new email row linked back to the original; the original is preserved. The response shape matches POST /v1/emails/.
GET /v1/emails/search
Search emails by recipient, sender, subject, status, domain, or tag. The free-text q parameter accepts inline tokens that translate to structured filters:
| Token | Effect |
|---|---|
to:gmail.com | Substring match on the recipient list. |
from:[email protected] | Substring match on the sender address. |
status:bounced | Exact status match. |
domain:axene.io | Substring match on the sending domain. |
tag:welcome | Exact tag match. |
Any remaining words become a free-text search across sender, subject, and recipients. Status and tag can also be passed as the separate query parameters status and tag. Results are paginated with page (default 0) and limit (default 20, max 100).
GET /v1/emails/
List emails with an optional status filter and page / limit pagination.
Error handling
| Status | Meaning |
|---|---|
400 Bad Request | Invalid attachment encoding, bad send_at value, or batch size over the plan limit. |
401 Unauthorized | Missing or invalid API key. |
403 Forbidden | Plan feature gate or send limit reached. |
404 Not Found | The referenced email does not exist. |
409 Conflict | The email cannot be retried in its current status. |
413 Payload Too Large | Attachment exceeds the per-file or combined size cap. |
422 Unprocessable Entity | Validation error in the request body. |
