API Reference
Integrate mail.cx into your application to create temporary inboxes, receive emails, and handle real-time mailbox events via WebSocket.
Base URL
https://api.mail.cx/apiAll API requests should be made to this base URL. HTTPS is required.
Authentication
All API requests require an API token sent via the x-api-token header. You can create tokens in your Dashboard under the Tokens page.
curl https://api.mail.cx/api/mailboxes \
-H "x-api-token: tm_live_your_token_here"Quick start
1. Create a mailbox:
curl -X POST https://api.mail.cx/api/mailboxes \
-H "x-api-token: tm_live_your_token_here" \
-H "Content-Type: application/json" \
-d '{"local_part":"myinbox","domain":"qabq.com"}'2. List emails in the mailbox:
curl https://api.mail.cx/api/mailboxes/{mailbox_id}/emails \
-H "x-api-token: tm_live_your_token_here"3. Read a specific email (full content with body and attachments):
curl https://api.mail.cx/api/emails/{email_id} \
-H "x-api-token: tm_live_your_token_here"Rate limits
Rate limits are applied in two layers: per-IP globally (100 requests/second for all users), and per-user (across all tokens). Free and Pro users also have monthly Ops quotas based on received emails.
| Limit | Free | Pro |
|---|---|---|
| Per-user global | 85/min | 20/s |
| Monthly Ops quota | 1,500/mo | 200,000/mo |
Authenticated requests include X-Ops-Limit and X-Ops-Remaining headers showing your monthly Ops usage. Rate limit 429 responses include a Retry-After header; Ops quota 429 responses do not (resets monthly).
What counts as an Op?
Ops is a monthly quota based on received emails:
- Every email received by a registered user consumes 1 Op
- API calls do not consume Ops
- Webhook deliveries do not consume Ops
- Anonymous users are not subject to Ops limits (IP-based rate limits apply instead)
Endpoints
Mailboxes
/api/mailboxesCreate a new temporary mailbox
API token required/api/mailboxes/:id/emailsList emails in a mailbox
API token required/api/mailboxes/:idDelete a mailbox and all its emails
API token requiredBoth fields are optional. If omitted, a random 6-character address on the default domain is generated.
| Parameter | Rules |
|---|---|
| local_part | 3–20 chars, lowercase a–z 0–9 . _ - only. Cannot start or end with . _ or -. Some names are reserved (admin, postmaster, abuse, etc.). |
| domain | Defaults to system domain. Authenticated users may specify a verified custom domain. |
Emails
/api/emails/:idGet full email content (body, attachments, headers)
API token required/api/emails/:id/rawDownload the original .eml file
API token required/api/emails/:id/attachments/:indexDownload an attachment by index (0-based)
API token required/api/emails/:idDelete a single email
API token requiredTokens
/api/tokensCreate a new API token
API token required/api/tokensList your API tokens
API token required/api/tokens/:idRevoke an API token
API token requiredDomains (Pro)
/api/domainsAdd a custom domain
Pro plan required/api/domainsList your custom domains
Pro plan required/api/domains/:idRemove a custom domain
Pro plan requiredDomain Emails (Pro)
/api/domain-emails?address=user@yourdomain.comList emails for a specific address under your domain (no mailbox needed)
Pro plan required/api/domain-emails?domain=yourdomain.comList all emails across your entire domain (no mailbox needed)
Pro plan requiredBoth endpoints support pagination via offset and limit query parameters. Default: 20 items per page, max 50. Response includes total count for building pagination.
GET /api/domain-emails?domain=yourdomain.com&offset=0&limit=20
// Response
{
"emails": [...],
"total": 142,
"offset": 0,
"limit": 20
}Webhooks (Pro)
/api/webhooksCreate a webhook endpoint
Pro plan required/api/webhooksList your webhooks
Pro plan required/api/webhooks/:idDelete a webhook
Pro plan required/api/webhooks/:id/rotateRotate webhook signing secret
Pro plan required/api/webhooks/:id/deliveriesView recent delivery attempts (last 20)
Pro plan requiredWebhook guide
Webhooks send an HTTP POST to your URL whenever an email arrives. Use them to integrate mail.cx with your application in real time.
Payload format
Each delivery sends a JSON payload with signature headers. The payload includes email metadata — not the full email body. Use the email_id to fetch the complete email via GET /api/emails/:id.
POST https://your-server.com/webhook
Content-Type: application/json
X-Webhook-ID: evt_abc123
X-Webhook-Timestamp: 1709721600
X-Webhook-Signature: sha256=a1b2c3...
{
"id": "evt_abc123",
"type": "email.received",
"created_at": "2024-03-06T12:00:00Z",
"data": {
"email_id": "uuid",
"mailbox_id": "uuid",
"address": "inbox@qabq.com",
"from": "sender@example.com",
"sender": "sender@example.com",
"subject": "Your verification code",
"preview_text": "Your code is 123456...",
"size": 2048,
"created_at": "2024-03-06T12:00:00Z"
}
}Verifying signatures
Every webhook request includes HMAC-SHA256 signature headers. Always verify the signature before processing the payload to ensure it came from mail.cx.
import hmac, hashlib
def verify_webhook(payload: bytes, timestamp: str, signature: str, secret: str) -> bool:
message = f"{timestamp}.{payload.decode()}"
expected = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)Expected responses
Your endpoint should respond quickly. The system interprets your HTTP status code as follows:
| Your response | What happens |
|---|---|
| 2xx | Delivery marked as successful. Failure count resets to 0. |
| 4xx | Treated as a failure. The delivery will be retried with exponential backoff. |
| 5xx | Treated as a failure. The delivery will be retried with exponential backoff. |
| Timeout (>5s) | Connection aborted. Treated as a failure and retried. |
Retry schedule
Failed deliveries are retried up to 5 times with exponential backoff: 15s, 1min, 5min, 10min, 20min. After 5 consecutive failures, the webhook status changes to "failing". Rotating the secret resets the failure count and restores the webhook to active.
Limits & requirements
| Max webhooks per user | 1 |
| Delivery rate limit | No per-user limit |
| Delivery timeout | 5s |
| Max retry attempts | 5 |
| URL protocol | HTTPS |
Use GET /api/webhooks/:id/deliveries to view the last 20 delivery attempts, including HTTP status codes, errors, and response times — useful for debugging integration issues.
WebSocket
Connect to the WebSocket endpoint for real-time email notifications. Authenticate by sending an auth message as the first frame. The server sends ping frames every 30 seconds.
// Connect
const ws = new WebSocket("wss://api.mail.cx/api/ws");
// Authenticate (send as first message)
ws.send(JSON.stringify({
type: "auth",
token: "tm_live_your_token_here"
}));
// Receive new emails
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// { type: "new_email", data: { id, from, subject, mailbox_id } }
};Errors
All errors return a JSON object with an error field:
{
"error": "rate_limit_exceeded"
}| Status | Description |
|---|---|
| 400 | Bad request — invalid parameters |
| 401 | Unauthorized — missing or invalid token |
| 403 | Forbidden — insufficient permissions or account disabled |
| 404 | Not found — resource does not exist |
| 410 | Gone — email expired and was deleted |
| 429 | Too many requests — rate limit exceeded |
Security
- All API traffic is encrypted via TLS (HTTPS required)
- Tokens are stored as SHA-256 hashes — we never see your plaintext token
- Webhook payloads are signed with HMAC-SHA256 for verification