API Reference
A REST API for creating ephemeral inboxes and retrieving emails. Designed to be called from test scripts, not browsers.
Introduction
AssertMail gives you real email addresses you can create and destroy via API. The typical pattern in an integration test is:
- Create an inbox — get back an email address.
- Feed that address into your app and trigger the email send.
- Long-poll the inbox until the email arrives (or timeout).
- Assert on subject, body, links, whatever you care about.
Inboxes expire automatically at their TTL, so there's no cleanup to manage between runs. Parallel test workers can each create their own inbox with no coordination needed.
Authentication
All API requests require an API key passed in the Authorization header as a Bearer token. Create and manage your keys in the dashboard.
Authorization: Bearer j8Kp3mN9xR2vL5tQ7wY0aB4cD6eF1gH_zX
Key format
API keys are 43-character base64url strings generated when you create a key in the dashboard.
Keep keys secret
API keys are long-lived. Don't commit them to source control — use environment variables or a secrets manager and inject them into your test environment at runtime.
Quick start
The full flow as a shell script — create an inbox, trigger your app, poll for the result.
# 1. Create an inbox (TTL 5 minutes) $ INBOX=$(curl -sX POST https://assertmail.com/api/inboxes \ -H "Authorization: Bearer $ASSERTMAIL_KEY" \ -H "Content-Type: application/json" \ -d '{"ttlSeconds":300}') $ EMAIL=$(echo $INBOX | jq -r '.emailAddress') $ ID=$(echo $INBOX | jq -r '.id') # 2. Trigger your app's send flow $ curl -sX POST https://yourapp.test/api/register \ -d "{\"email\": \"$EMAIL\"}" # 3. Long-poll until the email arrives (max 25s) $ curl -s "https://assertmail.com/api/inboxes/$ID/emails?wait=25" \ -H "Authorization: Bearer $ASSERTMAIL_KEY" \ | jq '.emails[0].subject'
Base URL
All endpoints are relative to:
https://assertmail.com/api
All requests and responses use JSON. Set Content-Type: application/json on requests with a body.
Create inbox
Creates a new inbox and returns its email address. The inbox and all its emails are automatically deleted when the TTL expires.
Request body
All fields are optional. Send an empty body or omit it entirely to use defaults.
| Field | Type | Description | |
|---|---|---|---|
| ttlSeconds | integer | optional | Lifetime in seconds. Capped at your plan maximum (86400 for 24h). Defaults to the plan maximum. |
| allowedSenders | string[] | optional | If set, only emails matching these rules are delivered. Each entry can be a full address (noreply@yourapp.com) or a domain (@yourapp.com or yourapp.com). |
Response — 201
| Field | Type | Description |
|---|---|---|
| id | string | Inbox ID (ULID). Pass this to subsequent requests. |
| emailAddress | string | The full inbox email address. Send email here. |
| allowedSenders | string[] | Echo of the request field, empty array if not set. |
| ttl | integer | Unix timestamp of when the inbox expires. |
Example
$ curl -sX POST https://assertmail.com/api/inboxes \ -H "Authorization: Bearer $ASSERTMAIL_KEY" \ -H "Content-Type: application/json" \ -d '{"ttlSeconds": 120, "allowedSenders": ["noreply@yourapp.com", "@yourapp.com"]}'
{
"id": "01JXKP7N3MQVW4RBSTDYFZ8EG2",
"emailAddress": "illustration-1234@in.assertmail.com",
"allowedSenders": ["noreply@yourapp.com", "@yourapp.com"],
"ttl": 1751000520
}
Delete inbox
Immediately deletes the inbox and all its emails. Useful for explicit cleanup in teardown, though most test suites can rely on TTL expiry instead.
Response — 204
No body.
Example
$ curl -sX DELETE https://assertmail.com/api/inboxes/01JXKP7N3MQVW4RBSTDYFZ8EG2 \ -H "Authorization: Bearer $ASSERTMAIL_KEY"
List emails
Returns emails in the inbox. Pass ?wait=N to long-poll: the request blocks until an email arrives or N seconds pass, whichever comes first.
Query parameters
| Parameter | Type | Description | |
|---|---|---|---|
| wait | integer | optional | Seconds to long-poll. Range 0–25. Default: 0 (no waiting — returns immediately with whatever's in the inbox). |
| after | string | optional | Email ID cursor. Only returns emails received after this ID. Used for continuous polling — see below. |
Response — 200
| Field | Type | Description |
|---|---|---|
| emails[].id | string | Email ID. Pass as after cursor in the next call. |
| emails[].sender | object | null | Parsed sender from the From header, or null if absent. |
| emails[].sender.address | string | Sender email address. |
| emails[].sender.name | string | null | Display name from the From header, if present. |
| emails[].subject | string | Email subject line. |
| emails[].html | string | null | HTML body, if the email included one. |
| emails[].plaintext | string | null | Plain text body, if present. |
| emails[].truncated | boolean | true if the email exceeded 10KB and was truncated. |
| emails[].attachmentNames | string[] | Names of any attachments. Attachment content is not returned. |
Example — wait for the first email
$ curl -s "https://assertmail.com/api/inboxes/$ID/emails?wait=25" \ -H "Authorization: Bearer $ASSERTMAIL_KEY"
{
"emails": [{
"id": "01JXKP8R2A4BNCVTSFGZ9MW1HK",
"sender": { "address": "noreply@yourapp.com", "name": "Your App" },
"subject": "Confirm your email address",
"html": "<p>Click <a href=\"...\">here</a> to confirm.</p>",
"plaintext": "Click the link to confirm: https://...",
"truncated": false,
"attachmentNames": []
}]
}
Continuous polling
If your test expects multiple emails, use the after cursor to only receive new ones. Take the id of the last email you processed and pass it back on the next call.
# First call — get everything currently in the inbox $ RESULT=$(curl -s "https://assertmail.com/api/inboxes/$ID/emails?wait=25" \ -H "Authorization: Bearer $ASSERTMAIL_KEY") # Save the last email ID as a cursor $ CURSOR=$(echo $RESULT | jq -r '.emails[-1].id // empty') # Subsequent calls — only get emails received after the cursor $ curl -s "https://assertmail.com/api/inboxes/$ID/emails?wait=25&after=$CURSOR" \ -H "Authorization: Bearer $ASSERTMAIL_KEY"
Error codes
Errors return JSON with an error field describing what went wrong.
{
"error": "Inbox not found"
}
| Status | Meaning |
|---|---|
| 400 | Bad request — missing or invalid field in the request body or query string. |
| 401 | Unauthorized — the Authorization header is missing or the key is invalid. |
| 403 | Forbidden — your org has no inbox allocation (no active subscription). |
| 404 | Not found — the inbox doesn't exist or has expired. |
| 500 | Internal server error — something went wrong on our end. Try again; if it persists, let us know. |