REST API overview
/api/v1 is the HTTP surface the SDK wraps. Every SDK method maps 1:1 to one
endpoint here, so this section is the reference for anything not calling from
TypeScript.
Base URL
https://<your-mailroom-deployment>/api/v1
Mailroom is deployed per installation — there is no shared api.mailroom.com. The
base URL is whatever host serves your instance.
Authentication
Authorization: Bearer pk_live_…
One key = one project. Write endpoints additionally require a scope; see
Authentication for the scope list and failure codes.
Only /health and /unsubscribe accept requests without a key.
Response envelope
Success bodies are flat — the payload is spread next to ok: true, not nested
under data:
{ "ok": true, "contacts": [{ "id": "…", "email": "ada@example.com" }] }
Errors:
{ "ok": false, "error": "Missing scope: contacts:write", "code": "forbidden" }
error is human-readable and may change. code is the stable identifier — branch
on it, not on the message. Some errors carry no code.
Status codes
| Status | When |
|---|---|
| 200 | Success |
| 201 | Created (campaigns, segments, sequences, suppressions, webhooks) |
| 400 | Malformed request |
| 401 | Missing, unknown, or revoked API key (unauthorized) |
| 403 | Valid key without the required scope (forbidden), suspended project (project_suspended), unverified owner email (email_unverified) |
| 404 | No such resource in this project (not_found) |
| 422 | Validation failed (invalid_request) or the operation is not possible in the current state |
| 429 | Rate limited — see Retry-After |
| 500 | Unhandled server error |
Content type
Send Content-Type: application/json with a JSON body on writes. Query parameters
carry filters on reads. Responses are always JSON except /unsubscribe, which
returns HTML for humans.
Idempotency
Mutating requests accept an Idempotency-Key header. On POST /send it is
forwarded to the delivery provider and recorded on the send log, so a retry with the
same key cannot produce a second email. Derive it from your own domain object
(receipt:ord_123), not a random value per attempt.
curl -X POST https://mailroom.example.com/api/v1/send \
-H "Authorization: Bearer pk_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: receipt:ord_123" \
-d '{"to":"ada@example.com","template":"receipt","data":{"orderId":"ord_123"}}'
Rate limits
A token bucket per project: 60 request burst, refilling 10/second by default.
Over the limit returns 429 with Retry-After in seconds. The bucket lives per
server instance, so treat the numbers as a floor and back off when told rather than
pacing to an exact rate.
Pagination
GET /contacts and GET /sends are cursor-paginated. Each response includes
nextCursor; pass it back as ?cursor= to continue, and stop when it is null.
Keep every other filter identical between pages. Cursors are opaque.
Retries
Retry 429, 5xx, and network failures with exponential backoff and jitter; do not
retry 4xx. Always send an Idempotency-Key on retried mutations. The SDK does all
of this for you.
Endpoint index
| Group | Endpoints |
|---|---|
| Contacts | POST /contacts · GET /contacts · GET /contacts/:id · POST /contacts/:id/unsubscribe |
| Send | POST /send |
| Send log | GET /sends |
| Templates | GET /templates · PUT /templates/:key · GET /templates/:key/schema · GET and PUT /templates/:key/content |
| Campaigns | POST /campaigns · GET /campaigns · GET /campaigns/:id · POST /campaigns/:id/cancel |
| Sequences | POST /sequences · GET /sequences · GET, PUT, DELETE /sequences/:key · POST /sequences/:key/simulate · POST /sequences/:key/enroll · POST /sequences/:key/enroll/bulk |
| Enrollments | DELETE /enrollments/:id |
| Events | POST /events |
| Segments | GET /segments · POST /segments · GET, PATCH, DELETE /segments/:id |
| Suppressions | GET /suppressions · POST /suppressions · DELETE /suppressions/:email |
| Webhooks | GET /webhooks · POST /webhooks · PATCH, DELETE /webhooks/:id · GET /webhooks/:id/deliveries · POST /webhooks/:id/rotate · POST /webhooks/:id/ping |
| Unsubscribe | GET /unsubscribe · POST /unsubscribe |
| Health | GET /health · POST /health |