Email Testing API
Email Testing API
REST API for domains, accounts, mailboxes, messages, and tokens. Authenticate with an X-API-KEY header; stream new messages over SSE.
General information
The API is described by an OpenAPI v3 document, and you can try every endpoint in the interactive reference.
Usage of our service for illegal activity is strictly prohibited.
Error handling
Successful requests return 200, 201, or 204. Errors return a 4xx code:
- 400 Bad Request: The payload is missing or malformed.
- 401 Unauthorized: The
X-API-KEYheader is missing or the key is invalid. - 404 Not Found: The resource doesn't exist — check the id and the path.
- 405 Method Not Allowed: Wrong method for the path, e.g.
PUT /tokensorPOST /domains/{id}. - 422 Unprocessable Entity: The payload didn't validate — a username too short, a domain that isn't yours.
- 429 Too Many Requests: Rate limit exceeded. Wait for the limit window to reset before retrying.
Authentication
All API requests require authentication using an API key.
To authenticate, add the following header to each request:
X-API-KEY: smtpla...here
How to get it? If you don't have an API key yet, create a new one on the API Keys page.
Example request with authentication:
curl -X GET "https://stmp.ink/api/accounts" \
-H "X-API-KEY: smtpla...here" \
-H "Accept: application/json"
Rate Limiting
The API has the following rate limits:
- 4096 requests per minute per authenticated user (sliding window)
- When rate limited, the API will return a
429 Too Many Requestsstatus code - Responses include rate limit headers:
RateLimit-Limit,RateLimit-Remaining, andRateLimit-Reset
If you receive a 429 response, you should:
- Check the
RateLimit-Resetheader to know when you can retry - Implement exponential backoff in your requests
- Add a delay between batches of requests
- Consider optimizing your code to make fewer API calls if possible
Health and schema
GET /api/health
Public health check.
curl https://stmp.ink/api/health
{"status": "ok"}
GET /api/openapi.json
Public OpenAPI 3 document listing the REST paths exposed by this clone.
Domains
GET /api/domains
List domains for the authenticated user. Query: page, limit.
curl -H "X-API-KEY: $SMTPDEV_API_KEY" \
"https://stmp.ink/api/domains?page=1&limit=25"
POST /api/domains
Body: domain required, mx_verified optional.
curl -X POST https://stmp.ink/api/domains \
-H "X-API-KEY: $SMTPDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"dev.example.test","mx_verified":true}'
{
"id": 1,
"domain": "dev.example.test",
"mx_verified": true,
"verification_status": "verified",
"created_at": "2026-08-06T12:00:00+00:00"
}
Accounts and mailboxes
GET /api/accounts
List accounts. Query: page, limit.
POST /api/accounts
Body: domain_id, address, optional password, is_active, is_catch_all, storage_limit.
curl -X POST https://stmp.ink/api/accounts \
-H "X-API-KEY: $SMTPDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain_id":1,"address":"qa@dev.example.test","password":"account-password"}'
{
"id": 1,
"domain_id": 1,
"address": "qa@dev.example.test",
"used_bytes": 0,
"used": 0,
"is_active": true,
"is_catch_all": false,
"storage_limit": 104857600,
"created_at": "2026-08-06T12:00:00+00:00"
}
GET /api/accounts/<account_id>
Fetch one account owned by the authenticated user.
GET /api/accounts/<account_id>/mailboxes
List mailboxes. Query: page, limit.
POST /api/accounts/<account_id>/mailboxes
Body: path required.
curl -X POST https://stmp.ink/api/accounts/1/mailboxes \
-H "X-API-KEY: $SMTPDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{"path":"Promotions"}'
Messages
GET /api/accounts/<account_id>/mailboxes/<mailbox_id>/messages
List mailbox messages newest first. Query: page, limit, q. Search matches subject, sender, recipients, and text body.
curl -H "X-API-KEY: $SMTPDEV_API_KEY" \
"https://stmp.ink/api/accounts/1/mailboxes/1/messages?q=reset"
{
"items": [{
"id": 1,
"msgid": "<abc@example.test>",
"account_id": 1,
"mailbox_id": 1,
"subject": "Reset code",
"intro": "Your code is 481516.",
"thread_id": null,
"from_address": "app@example.test",
"to_addresses": ["qa@dev.example.test"],
"received_at": "2026-08-06T12:00:00+00:00",
"expires_at": null,
"size_bytes": 512
}],
"pagination": {"page": 1, "limit": 25, "per_page": 25, "total": 1}
}
GET /api/messages/<message_id>
Fetch full parsed content: envelope, text, HTML, headers, attachments, SPF/DKIM/DMARC fields, and audit results.
GET /api/messages/<message_id>/raw
Return raw RFC 5322 source with message/rfc822 content type.
GET /api/messages/<message_id>/attachments/<attachment_id>
Download an attachment as raw binary using the stored content type and filename.
DELETE /api/messages/<message_id>
Delete a message and publish a deletion event.
POST /api/accounts/<account_id>/messages/send
Sandboxed send to local accounts. Body: to required, optional cc, bcc, replyTo, subject, text, html. Returns 204.
curl -X POST https://stmp.ink/api/accounts/1/messages/send \
-H "X-API-KEY: $SMTPDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to":["qa+api@dev.example.test"],"subject":"Hello","text":"Sandbox only"}'
Tokens and events
GET /api/tokens
List token metadata. Token values are not returned after creation.
POST /api/tokens
Create a named API token. Body: optional name. The response includes the token value once.
DELETE /api/tokens/<token_id>
Revoke a token and return 204.
GET /api/events
SSE stream. Auth by X-API-KEY or session cookie. Query once=1 returns the latest event and closes.
curl -N -H "X-API-KEY: $SMTPDEV_API_KEY" \
"https://stmp.ink/api/events?once=1"
event: new-message
data: {"type":"message.received","id":1,"account_id":1,"mailbox_id":1,"subject":"Reset code"}