๐Ÿ“จ Outbox

API reference#

The Outbox API is REST over JSON. It mirrors the Resend API: the same paths, request bodies, response shapes, and error envelope. Any Resend SDK works against it by changing only the base URL.

Base URL#

Whatever host you deployed to:

https://outbox.example.com

Locally that is http://localhost:3000. Unlike Resend there is no separate api. hostname โ€” the API is served at the root and the dashboard lives under /app, so a base URL swap is all an SDK needs.

Authentication#

Send your API key as a bearer token:

Authorization: Bearer ob_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are created in the dashboard or through the API and shown exactly once. Only a SHA-256 hash is stored, so a lost key cannot be recovered โ€” create a new one and delete the old.

Keys carry a permission:

  • full_access โ€” every endpoint.
  • sending_access โ€” POST /emails and POST /emails/batch only. Every other endpoint returns 401 restricted_api_key.

A key may also be restricted to one domain, in which case it can only send from that domain.

User-Agent#

Every request must include a User-Agent. Requests without one are rejected:

json
{
  "statusCode": 403,
  "name": "invalid_user_agent",
  "message": "The User-Agent header is required. Set it to identify your application, e.g. `my-app/1.0`."
}

Every official SDK and most HTTP clients set this automatically. Bare curl does too. Explicitly clearing it โ€” curl -H "User-Agent:" โ€” is what trips this.

Response codes#

StatusMeaning
200Success
201Created
400Malformed request or bad parameter
401Missing key, or a restricted key on a forbidden endpoint
403Invalid key, unverified sending domain, or missing User-Agent
404Resource not found
409Conflict โ€” a duplicate, or a concurrent idempotent request
422Validation failed or a required field is missing
429Rate limit exceeded
5xxServer error

Errors#

Every error uses the same envelope:

json
{
  "statusCode": 422,
  "name": "missing_required_field",
  "message": "Missing `subject` field."
}

name is the stable machine-readable slug โ€” switch on it rather than parsing message. See Errors for the full list.

Rate limits#

The default is 10 requests per second per team, shared across all of that team's API keys, matching Resend's default. Change it with RATE_LIMIT_PER_SECOND.

Exceeding it returns 429 with the standard headers:

retry-after: 1
ratelimit-limit: 10
ratelimit-remaining: 0
ratelimit-reset: 1

Back off and retry. A client that ignores 429s will drop requests.

Pagination#

List endpoints return a consistent envelope and use cursor pagination. See Pagination.

json
{
  "object": "list",
  "has_more": false,
  "data": []
}

Idempotency#

POST /emails and POST /emails/batch accept an Idempotency-Key header. Repeating a request with the same key replays the original response instead of sending again โ€” see Emails.

Endpoints#

GroupPaths
Emails/emails, /emails/batch, /emails/:id, /emails/receiving
Domains/domains
API keys/api-keys
Contacts/contacts
Contact properties/contact-properties
Segments/segments, /audiences
Topics/topics
Broadcasts/broadcasts
Templates/templates
Suppressions/suppressions
Webhooks/webhooks
Automations/automations, /events/send
Metrics/emails/metrics, /segments/metrics
Logs/logs