API
REST API overview
Authentication, conventions, errors, pagination and realtime endpoints.
Base URL: https://YOUR-INSTANCE/api/v1. The full, generated reference is on the endpoint reference page; the OpenAPI 3.1 document is served at /api/openapi.json by every instance.
Authentication
curl https://app.squadmail.dev/api/v1/me -H "Authorization: Bearer sqm_YOUR_KEY" Keys are scoped to one workspace. Dashboard sessions also work for same-origin requests.
Conventions
- JSON in, JSON out. Field names are
snake_case, timestamps ISO-8601 UTC. - Inbox path parameters accept the id or the address:
/inboxes/swift-otter-4821@example.com. - Lists return
dataandnext_cursor. Passcursorto fetch the next page. DELETEreturnsdeleted: truewith the id.
Errors
{
"error": {
"code": "validation_error",
"message": "Invalid request",
"issues": [{ "path": "ttl_minutes", "message": "Too big" }]
}
} | Status | Codes |
|---|---|
| 400 | validation_error, invalid_json, invalid_domain, unknown_label |
| 401 | unauthorized |
| 403 | forbidden, insufficient_scope, feature_disabled |
| 404 | not_found |
| 409 | address_taken, label_exists, no_domain |
| 429 | rate_limited, limit_reached |
Waiting for mail
GET /inboxes/{id}/wait?timeout=60&subject=verify&from=acme blocks until a matching email arrives (max 120 s) and returns it, or 204 on timeout. It is backed by a Durable Object, so there is no polling on your side or ours.
Streaming
GET /inboxes/{id}/stream is a Server-Sent Events stream:
event: email.received
data: {"type":"email.received","inboxId":"inb_…","emailId":"eml_…","from":"no-reply@acme.io","subject":"Your code","code":"482913","at":1791386064000} Extracted data
Every email has an extracted object computed on ingest — no AI involved:
{
"code": "482913",
"link": "https://acme.io/verify?token=abc",
"codes": ["482913"],
"links": [
{ "url": "https://acme.io/verify?token=abc", "text": "Confirm email", "kind": "verify" }
]
} Link kinds: verify, login, reset, unsubscribe, other.