REST API Reference
The SES Mailbox API lets you add and update contacts, trigger transactional emails, and manage suppressions programmatically. All endpoints require authentication with an API key.
Authentication
Pass your API key as a Bearer token in the Authorization header on every request:
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx
Base URL
https://api.sesmailbox.com/v1
Response format
All responses are JSON. Successful responses return a 200 or 201 status. Errors return a 4xx or 5xx with an error field explaining the problem.
# Success
{ "id": "msg_01abc...", "status": "queued" }
# Error
{ "error": "Invalid API key", "code": "auth_invalid" }Send a transactional email
Send a single email to one or more recipients. Useful for transactional messages (welcome emails, password resets, notifications) triggered by your application.
POST /v1/send
{
"to": ["ada@example.com"],
"from": "noreply@yourcompany.com",
"from_name": "Acme Corp",
"subject": "Welcome to Acme, {{first_name}}!",
"html": "<h1>Hi {{first_name}},</h1><p>Your account is ready.</p>",
"text": "Hi {{first_name}}, your account is ready.",
"variables": {
"first_name": "Ada"
}
}Request fields
| Field | Type | Required | Description |
|---|---|---|---|
to | string[] | Yes | Recipient email addresses (max 50) |
from | string | Yes | Sender address — must be a verified domain |
from_name | string | No | Sender display name |
subject | string | Yes | Email subject. Supports {{variable}} syntax |
html | string | Yes* | HTML body. *Required if text not provided |
text | string | No | Plain-text body (auto-generated from HTML if omitted) |
variables | object | No | Key-value pairs for {{variable}} substitution |
reply_to | string | No | Reply-to address |
headers | object | No | Custom email headers |
Example response
{
"id": "msg_01j9k2m...",
"status": "queued",
"recipient_count": 1,
"created_at": "2026-05-23T10:41:00Z"
}Contacts API
Add or update a contact
POST /v1/contacts
{
"email": "grace@example.com",
"first_name": "Grace",
"last_name": "Hopper",
"fields": {
"plan": "pro",
"company": "Navy Labs"
}
}If a contact with the same email already exists, this updates their fields. If they don't exist, it creates them. Subscription status is preserved on update unless you pass "subscribed": false explicitly.
Get a contact
GET /v1/contacts/{email}
# Example
GET /v1/contacts/grace%40example.comDelete a contact
DELETE /v1/contacts/{email}Deleting a contact removes them from your contacts list but does not add them to your suppression list. To prevent future sends, use the unsubscribe endpoint instead.
Unsubscribe a contact
POST /v1/contacts/{email}/unsubscribeMarks the contact as unsubscribed and adds them to the suppression list. They will not receive any future campaigns.
Suppressions API
Add to suppression list
POST /v1/suppressions
{
"email": "do-not-email@example.com",
"reason": "unsubscribed"
}Valid reasons: unsubscribed, bounced, complained, other.
Check suppression status
GET /v1/suppressions/{email}
# Response if suppressed
{ "email": "do-not-email@example.com", "reason": "unsubscribed", "created_at": "..." }
# Response if not suppressed
{ "error": "Not found", "code": "not_found" } // 404Remove from suppression list
DELETE /v1/suppressions/{email}Error codes
| HTTP status | Code | Meaning |
|---|---|---|
| 401 | auth_missing | No Authorization header |
| 401 | auth_invalid | API key is invalid or revoked |
| 403 | plan_required | This endpoint requires Pro plan |
| 404 | not_found | Resource does not exist |
| 422 | validation_error | Request body has missing or invalid fields — details in errors array |
| 429 | rate_limited | Too many requests — retry after the seconds specified in Retry-After header |
| 500 | server_error | Internal error — contact support if this persists |
Rate limits
API requests are rate-limited per API key:
- Send endpoint: 100 requests per minute
- All other endpoints: 300 requests per minute
When rate-limited, the response includes a Retry-After header with the number of seconds to wait before retrying.