Skip to main content

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​

StatusWhen
200Success
201Created (campaigns, segments, sequences, suppressions, webhooks)
400Malformed request
401Missing, unknown, or revoked API key (unauthorized)
403Valid key without the required scope (forbidden), suspended project (project_suspended), unverified owner email (email_unverified)
404No such resource in this project (not_found)
422Validation failed (invalid_request) or the operation is not possible in the current state
429Rate limited — see Retry-After
500Unhandled 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​

GroupEndpoints
ContactsPOST /contacts · GET /contacts · GET /contacts/:id · POST /contacts/:id/unsubscribe
SendPOST /send
Send logGET /sends
TemplatesGET /templates · PUT /templates/:key · GET /templates/:key/schema · GET and PUT /templates/:key/content
CampaignsPOST /campaigns · GET /campaigns · GET /campaigns/:id · POST /campaigns/:id/cancel
SequencesPOST /sequences · GET /sequences · GET, PUT, DELETE /sequences/:key · POST /sequences/:key/simulate · POST /sequences/:key/enroll · POST /sequences/:key/enroll/bulk
EnrollmentsDELETE /enrollments/:id
EventsPOST /events
SegmentsGET /segments · POST /segments · GET, PATCH, DELETE /segments/:id
SuppressionsGET /suppressions · POST /suppressions · DELETE /suppressions/:email
WebhooksGET /webhooks · POST /webhooks · PATCH, DELETE /webhooks/:id · GET /webhooks/:id/deliveries · POST /webhooks/:id/rotate · POST /webhooks/:id/ping
UnsubscribeGET /unsubscribe · POST /unsubscribe
HealthGET /health · POST /health