ReachBellDocs

API conventions

The rules that hold across every ReachBell REST endpoint: where the API lives, how you authenticate, what rate limits apply, how lists paginate, and the shape of an error. Read this once and the per-resource pages (Subscribers, Transactional, Events, and so on) will read the same way.

Base URL

All requests go to:

https://api.reachbell.com

Endpoints are mounted at the root — there is no /v1 or /api version prefix. A path is exactly what each reference page lists, e.g. POST /transactional/send, POST /events, GET /subscribers/.... Requests must be HTTPS.

Authentication

Server-to-server calls authenticate with a project API key in the x-api-key header:

curl https://api.reachbell.com/projects/context \
  -H "x-api-key: rb_live_yourkey"

The key is project-scoped, so most endpoints don't need any organization header when called this way. Keys look like rb_live_… and must stay server-side — get one from Settings → API keys in the dashboard (see API keys & your first server send).

Dashboard/browser sessions use a JWT (Authorization: Bearer … plus x-org-id) instead. Both modes, key format, and rotation are covered in full on the Authentication page. For backend integrations, use an API key.

A missing or unrecognized key returns 401:

{
  "statusCode": 401,
  "message": "Invalid API key",
  "requestId": "req_01HX4..."
}

Request tracing

Every response carries an x-request-id response header. If you send your own x-request-id on the request, the API echoes it back; otherwise it generates one. Error bodies also include the same value as requestId (see Error envelope below).

Log this value. When something doesn't behave, quoting the request ID to support is the single fastest way to trace exactly what happened.

Rate limits

The API is rate-limited per IP address, on a 60-second rolling window, across two buckets:

BucketLimitApplies to
Default600 requests / minutedashboard and general traffic
Ingest120 requests / minutethe high-volume write surface — /subscribers, /events, /transactional/*, and the messaging send routes

Exceed a bucket and you get a 429 Too Many Requests. The framework returns the standard rate-limit response headers alongside it — Retry-After (seconds to wait), plus X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. Honour Retry-After and back off; a little jitter on retries avoids a thundering herd.

Build retry-with-backoff into any client that sends in bursts. If your legitimate traffic outgrows these caps, contact support — limits can be raised per project.

The default caps can be tuned per deployment via the RATE_LIMIT_RPM and RATE_LIMIT_INGEST_RPM environment variables; the numbers above are the defaults on the hosted API.

Idempotency

ReachBell does not currently support client-supplied idempotency keys. There is no Idempotency-Key header — sending one has no effect, and a retried request is processed as a new request.

In practice this matters less than it sounds, because the operations you'll retry are forgiving:

  • Transactional sends return 200 with a delivered flag rather than failing when there's no active subscription, so a retry doesn't error — though a retried successful send can deliver the notification twice. For anything you must not double-send, dedupe on your side before calling.
  • identify and tag are naturally idempotent: re-running them converges to the same subscriber state.
  • Webhooks are deduplicated server-side on the provider's message ID, so the same provider event won't be processed twice.

If first-class idempotency keys would help your integration, let support know — it's on the roadmap, and the section above will document the header when it ships.

Pagination

List endpoints are page-based. The common query parameters are:

ParamDefaultNotes
page11-indexed
limit50capped at 100

A paginated list response wraps its rows alongside the page metadata, for example:

{
  "items": [ /* … */ ],
  "total": 1280,
  "page": 1,
  "limit": 50,
  "pages": 26
}

Iterate by requesting page=1, page=2, … until page reaches pages (or a page comes back with fewer than limit rows).

Pagination isn't perfectly uniform across the API yet — a few list endpoints name the rows after the resource (e.g. subscribers rather than items) or accept offset instead of page. Each reference page documents its exact parameters and envelope; treat the table above as the default to expect.

Error envelope

Errors come back as JSON with a consistent shape: an HTTP statusCode, a human-readable message, and the requestId for that call.

{
  "statusCode": 400,
  "message": "Validation failed",
  "error": "Bad Request",
  "requestId": "req_01HX4..."
}
  • statusCode — mirrors the HTTP status.
  • message — a string, or an array of strings for validation errors (one entry per failing field).
  • error — a short status label (e.g. Bad Request, Unauthorized), present on most client errors.
  • requestId — the same value as the x-request-id response header.

Server errors never leak internals — a 500 is always exactly:

{
  "statusCode": 500,
  "message": "Internal server error",
  "requestId": "req_01HX4..."
}

For the meaning of each status and how to handle it, see Error codes.

Successful responses are returned directly — there is no envelope wrapping success payloads in a data field. Each endpoint's reference page shows its exact response shape.

What's next?