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