Email Testing API
General information
The API is described by an OpenAPI v3 document, and you can try every endpoint in the interactive reference.
Integrations
Built something on the API - an SDK, a test helper, a CI action? Let us know and we'll list it here.
API Documentation
Base URL:
https://api.smtp.dev
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-KEY header 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 /tokens or POST /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: smtplabs_your_api_key_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://api.smtp.dev/accounts" \
-H "X-API-KEY: smtplabs_your_api_key_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
Domain
List Domains
GET /domains
You have to use this when creating an account, to retrieve the domain.
Returns a list of domains.
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/domains?isActive=true&page=1" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response:
{
"member": [
{
"id": "string",
"domain": "string",
"isActive": true,
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
],
"view": {
"id": "string",
"type": "string",
"first": "string",
"last": "string",
"previous": "string",
"next": "string"
},
"search": {
"type": "string",
"template": "string",
"variableRepresentation": "string",
"mapping": [
{
"type": "string",
"variable": "string",
"property": "string",
"required": true
}
]
}
}
Create a Domain
POST /domains
Creates a Domain resource.
Body:
example.comParams:
None
curl Example:
curl -X POST "https://api.smtp.dev/domains" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"domain": "example.com",
"isActive": true
}'
Response:
{
"@context": "string",
"@id": "string",
"@type": "string",
"id": "string",
"domain": "example.com",
"isActive": true,
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
Get a Domain
GET /domains/{id}
Retrieves a Domain resource by its id.
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/domains/{id}" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response:
{
"@context": "string",
"@id": "string",
"@type": "string",
"id": "string",
"domain": "example.com",
"isActive": true,
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
Delete a Domain
DELETE /domains/{id}
Deletes the Domain resource.
Body:
None
Params:
curl Example:
curl -X DELETE "https://api.smtp.dev/domains/{id}" \
-H "X-API-KEY: smtplabs_your_api_key_here"
Response:
None
(Returns status code 204 if successful.)
Update a Domain
PATCH /domains/{id}
Updates the Domain resource.
Body:
Params:
curl Example:
curl -X PATCH "https://api.smtp.dev/domains/{id}" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Content-Type: application/merge-patch+json" \
-H "Accept: application/json" \
-d '{
"isActive": true
}'
Response:
{
"@context": "string",
"@id": "string",
"@type": "string",
"id": "string",
"domain": "example.com",
"isActive": true,
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
Account
List Accounts
GET /accounts
Retrieves the collection of Account resources.
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/accounts?address=user@example.com&isActive=true&page=1" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response
{
"member": [
{
"id": "string",
"address": "user@example.com",
"quota": 0,
"used": 0,
"isActive": true,
"isDeleted": false,
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
],
"view": {
"id": "string",
"type": "string",
"first": "string",
"last": "string",
"previous": "string",
"next": "string"
},
"search": {
"type": "string",
"template": "string",
"variableRepresentation": "string",
"mapping": [
{
"type": "string",
"variable": "string",
"property": "string",
"required": true
}
]
}
}
Create an Account
POST /accounts
Creates an Account resource
Body:
true.Params
None
curl Example:
curl -X POST "https://api.smtp.dev/accounts" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"address": "user@example.com",
"password": "SecurePassword123"
}'
Response
{
"@context": "string",
"@id": "string",
"@type": "string",
"id": "string",
"address": "user@example.com",
"quota": 0,
"used": 0,
"isActive": true,
"isDeleted": false,
"mailboxes": [
{
"id": "string",
"path": "INBOX",
"isSystem": true,
"totalMessages": 0,
"totalUnreadMessages": 0,
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
],
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
When an account is created, 5 default mailboxes are automatically created: INBOX, Sent, Trash, Drafts, and Junk. The address can receive mail immediately.
Get an Account
GET /accounts/{id}
Get an Account resource by its id
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/accounts/{accountId}" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response:
{
"@context": "string",
"@id": "string",
"@type": "string",
"id": "string",
"address": "user@example.com",
"quota": 0,
"used": 0,
"isActive": true,
"isDeleted": false,
"mailboxes": [
{
"id": "string",
"path": "INBOX",
"isSystem": true,
"totalMessages": 0,
"totalUnreadMessages": 0,
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
],
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
Delete an Account
DELETE /accounts/{id}
Deletes the Account resource.
Body:
None
Params:
curl Example:
curl -X DELETE "https://api.smtp.dev/accounts/{accountId}" \
-H "X-API-KEY: smtplabs_your_api_key_here"
Response:
None
(Returns status code 204 if successful.)
Update an Account
PATCH /accounts/{id}
Updates the Account resource.
Body:
Params:
curl Example:
curl -X PATCH "https://api.smtp.dev/accounts/{accountId}" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Content-Type: application/merge-patch+json" \
-H "Accept: application/json" \
-d '{
"password": "NewSecurePassword456",
"isActive": true
}'
Response:
{
"@context": "string",
"@id": "string",
"@type": "string",
"id": "string",
"address": "user@example.com",
"quota": 0,
"used": 0,
"isActive": true,
"isDeleted": false,
"mailboxes": [],
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
Mailboxes
List Mailboxes
GET /accounts/{accountId}/mailboxes
Gets all the Mailbox resources of a given account.
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/accounts/{accountId}/mailboxes?page=1" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response:
{
"member": [
{
"@context": "string",
"@id": "string",
"@type": "string",
"id": "string",
"path": "string",
"isSystem": false,
"totalMessages": 0,
"totalUnreadMessages": 0,
"account": "string",
"createdAt": "2025-04-07T05:53:58.747Z",
"updatedAt": "2025-04-07T05:53:58.748Z"
}
],
"view": {
"@id": "string",
"type": "string",
"first": "string",
"last": "string",
"previous": "string",
"next": "string"
},
"search": {
"@type": "string",
"template": "string",
"variableRepresentation": "string",
"mapping": [
{
"@type": "string",
"variable": "string",
"property": "string",
"required": true
}
]
}
}
Create a Mailbox
POST /accounts/{accountId}/mailboxes
Creates a Mailbox resource for a given account.
Body:
PromotionsParams
curl Example:
curl -X POST "https://api.smtp.dev/accounts/{accountId}/mailboxes" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"path": "Promotions"
}'
Response
{
"@context": "string",
"@id": "string",
"@type": "string",
"id": "string",
"path": "Promotions",
"isSystem": false,
"totalMessages": 0,
"totalUnreadMessages": 0,
"account": "string",
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
Get a Mailbox
GET /accounts/{accountId}/mailboxes/{id}
Retrieves a Mailbox resource by its id.
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response
{
"@context": "string",
"@id": "string",
"@type": "string",
"id": "string",
"path": "string",
"isSystem": false,
"totalMessages": 0,
"totalUnreadMessages": 0,
"account": "string",
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
Delete a Mailbox
DELETE /accounts/{accountId}/mailboxes/{id}
Deletes the Mailbox resource.
Body:
None
Params:
curl Example:
curl -X DELETE "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}" \
-H "X-API-KEY: smtplabs_your_api_key_here"
Response:
None
(Returns status code 204 if successful.)
Update a Mailbox
PATCH /accounts/{accountId}/mailboxes/{id}
Updates the Mailbox resource.
Body:
PrimaryParams:
curl Example:
curl -X PATCH "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Content-Type: application/merge-patch+json" \
-H "Accept: application/json" \
-d '{
"path": "Primary"
}'
Response:
{
"@context": "string",
"@id": "string",
"@type": "string",
"id": "string",
"path": "Primary",
"isSystem": false,
"totalMessages": 0,
"totalUnreadMessages": 0,
"account": "string",
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
Messages
List Messages
GET /accounts/{accountId}/mailboxes/{mailboxId}/messages
Retrieves the collection of Message resources for a given mailbox.
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}/messages?page=1" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response:
{
"member": [
{
"id": "string",
"msgid": "string",
"from": {
"address": "string",
"name": "string"
},
"to": [
{
"address": "string",
"name": "string"
}
],
"cc": [
{
"address": "string",
"name": "string"
}
],
"bcc": [
{
"address": "string",
"name": "string"
}
],
"replyTo": [
{
"address": "string",
"name": "string"
}
],
"date": "2025-04-01T00:00:00.000Z",
"subject": "string",
"intro": "string",
"text": "string",
"html": {},
"isRead": false,
"isFlagged": false,
"isDeleted": false,
"hasAttachments": false,
"size": 0,
"autoDeleteEnabled": false,
"expiresAt": null,
"flags": [],
"verifications": {},
"headers": [],
"attachments": [],
"downloadUrl": "string",
"sourceUrl": "string",
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
],
"view": {
"id": "string",
"type": "string",
"first": "string",
"last": "string",
"previous": "string",
"next": "string"
}
}
Messages are ordered by createdAt descending (newest first). There are up to 30 messages per page. Use the view links (next, previous) to navigate between pages.
Get a Message
GET /accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}
Retrieves a Message resource by its id (this has way more information than a message retrieved with List Messages)
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response:
{
"id": "string",
"msgid": "string",
"from": {
"address": "string",
"name": "string"
},
"to": [
{
"address": "string",
"name": "string"
}
],
"cc": [
{
"address": "string",
"name": "string"
}
],
"bcc": [
{
"address": "string",
"name": "string"
}
],
"replyTo": [
{
"address": "string",
"name": "string"
}
],
"date": "2025-04-01T00:00:00.000Z",
"subject": "string",
"intro": "string",
"text": "string",
"html": {},
"isRead": false,
"isFlagged": false,
"isDeleted": false,
"hasAttachments": false,
"size": 0,
"autoDeleteEnabled": false,
"expiresAt": null,
"flags": [],
"verifications": {},
"threadId": "string",
"headers": [],
"attachments": [
{
"id": "string",
"filename": "string",
"contentType": "string",
"disposition": "string",
"size": 0,
"downloadUrl": "string"
}
],
"downloadUrl": "string",
"sourceUrl": "string",
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
Delete a Message
DELETE /accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}
Deletes the Message resource.
Body:
None
Params:
curl Example:
curl -X DELETE "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}" \
-H "X-API-KEY: smtplabs_your_api_key_here"
Response:
None
(Returns status code 204 if successful.)
Update a Message
PATCH /accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}
Updates the Message resource. For example, mark a message as read or flagged.
Body:
Params:
curl Example:
curl -X PATCH "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Content-Type: application/merge-patch+json" \
-H "Accept: application/json" \
-d '{
"isRead": true,
"isFlagged": true,
"autoDeleteEnabled": true,
"expiresAt": "2025-05-01T00:00:00.000Z"
}'
Response:
The response is the full updated Message object (same schema as Get a Message).
Get a Message Source
GET /accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}/source
Gets a Message's Source resource: the raw message as it arrived over SMTP - headers, MIME boundaries, encoded body parts.
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}/source" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response:
{
"@context": "string",
"@id": "string",
"@type": "string",
"raw": "string"
}
The raw field contains the full RFC 822 message source. You can also use the Download a Message endpoint to download it as a .eml file.
Download a Message Attachment
GET /accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}/attachment/{attachmentId}
Gets a Message's attachment.
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}/attachment/{attachmentId}" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json" \
-o welcome.pdf
Response:
Binary file content (saved to the specified output file).
For JSON metadata about the attachment:
curl -X GET "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
{
"id": "string",
"filename": "string",
"contentType": "string",
"disposition": "string",
"transferEncoding": "string",
"related": true,
"size": 0,
"downloadUrl": "string"
}
Download a Message
GET /accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}/download
Downloads a Message resource.
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}/download" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-o message.eml
Response:
Binary file content (saved to the specified output file).
(Returns status code 200 if successful.)
Move a Message
PUT /accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}/move
Moves a Message resource to a different mailbox.
Body:
Params
curl Example:
curl -X PUT "https://api.smtp.dev/accounts/{accountId}/mailboxes/{mailboxId}/messages/{id}/move" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"mailbox": "{mailboxId}"
}'
Response:
The response is the full Message object in its new mailbox (same schema as Get a Message).
Send a Message
POST /accounts/{accountId}/messages/send
Sends an email message from the specified account.
from address is automatically set to the account's address, regardless of what you provide in the request body.Body:
address (string) and optionally name (string). The address will be overwritten with the account's address.address (string, required) and name (string, optional). At least one recipient is required.address and name.address and name.address and name.name (string) and value (string).Attachment object:
application/pdf, image/png.attachment (default) or inline.text, html, or attachments must be provided.Params:
curl Example:
curl -X POST "https://api.smtp.dev/accounts/{accountId}/messages/send" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"from": {
"address": "user@example.com",
"name": "John Doe"
},
"to": [
{
"address": "recipient@example.com",
"name": "Jane Doe"
}
],
"subject": "Hello from smtp.dev",
"text": "This is a test email.",
"html": "<p>This is a <strong>test</strong> email.</p>"
}'
Response:
None
(Returns status code 204 if successful.)
Handling Attachments
contentType to decide how to open the file, and disposition to tell inline images apart from regular attachments.Tokens
List Tokens
GET /tokens
Retrieves the collection of Token resources.
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/tokens?page=1" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response:
{
"member": [
{
"id": "string",
"name": "My API Token",
"description": "Used for automated testing",
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
]
}
Create a Token
POST /tokens
Creates a new API token.
Body:
Params:
None
curl Example:
curl -X POST "https://api.smtp.dev/tokens" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"name": "Integration Token",
"description": "Used for automated testing"
}'
Response:
{
"id": "string",
"token": "smtplabs_abcdef123456",
"name": "My API Token",
"description": "Used for automated testing",
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
The token field is only returned once when the token is created. It cannot be retrieved again.
Get a Token
GET /tokens/{id}
Retrieves a Token resource by its id.
Body:
None
Params:
curl Example:
curl -X GET "https://api.smtp.dev/tokens/{id}" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response:
{
"id": "string",
"name": "My API Token",
"description": "Used for automated testing",
"createdAt": "2025-04-01T00:00:00.000Z",
"updatedAt": "2025-04-01T00:00:00.000Z"
}
Delete a Token
DELETE /tokens/{id}
Deletes the Token resource.
Body:
None
Params:
curl Example:
curl -X DELETE "https://api.smtp.dev/tokens/{id}" \
-H "X-API-KEY: smtplabs_your_api_key_here"
Response:
None
(Returns status code 204 if successful.)
Real-time Updates
Mercure for Server-Sent Events (SSE)
Instead of traditional webhooks, we use Mercure to provide real-time updates via Server-Sent Events (SSE).
SSE is a standard technology that allows servers to push updates to web clients over HTTP, maintaining a single connection. This is more efficient than polling the API for updates.
Getting a Mercure Token
Before subscribing to real-time updates, you need to obtain a Mercure token:
GET /mercure/token
curl Example:
curl -X GET "https://api.smtp.dev/mercure/token" \
-H "X-API-KEY: smtplabs_your_api_key_here" \
-H "Accept: application/json"
Response:
{
"token": "eyJhbGciOiJIUzI1NiJ9..."
}
Listening for New Messages
To listen for real-time updates about new messages:
Base URL:
https://mercure.smtp.dev/.well-known/mercure
Topic:
/accounts/{id}{+path}
Example implementation (JavaScript):
The standard EventSource API does not support custom headers. Use a library like @microsoft/fetch-event-source instead:
import { fetchEventSource } from '@microsoft/fetch-event-source'
const tokenResponse = await fetch('https://api.smtp.dev/mercure/token', {
headers: { 'X-API-KEY': 'smtplabs_your_api_key_here' }
})
const { token } = await tokenResponse.json()
const mercureUrl = new URL('https://mercure.smtp.dev/.well-known/mercure')
// e.g. /accounts/6789abcdef012345{+path}
mercureUrl.searchParams.append('topic', `/accounts/${accountId}{+path}`)
await fetchEventSource(mercureUrl.toString(), {
headers: {
Authorization: `Bearer ${token}`
},
onmessage(event) {
const data = JSON.parse(event.data)
if (data['@type'] === 'Message') {
// new message received
}
}
})
You'll receive updates with Account, Mailbox, or Message types depending on what changed.
Questions and suggestions
If you have any questions or suggestions, please contact us.
Tech stack
Our stack includes API-Platform, Mercure, Nuxt.js, Haraka, Caddy, MongoDB, Node.js, RockyLinux