ReachBellDocs

Authentication

Every ReachBell API request is authenticated. There are two modes, and which one you use depends on who's making the call.

  • JWT — for browser sessions in the dashboard. Short-lived, user-scoped, organization-aware.
  • API key — for server-to-server traffic from your backend, marketing automation, or the SDK. Long-lived, project-scoped.

Pick the mode that matches the caller. Most production integrations use API keys.

Choosing a mode

CallerModeHeader
Dashboard user (browser)JWTAuthorization: Bearer <token> + x-org-id
Your backend or workerAPI keyx-api-key: rb_live_...
SDK on a customer siteAPI keyx-api-key: rb_live_... (handled for you)
Sandbox / staging testsAPI key (test)x-api-key: rb_test_...

Pick API keys for anything that runs without a human in the loop. JWTs expire and need refresh handling — fine for the dashboard, painful everywhere else.

JWT authentication

JWTs are issued by POST /auth/login. The dashboard uses them for every request a signed-in user makes.

Logging in

curl -X POST https://api.reachbell.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email":    "you@example.com",
    "password": "your-password"
  }'

A successful response looks like this:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id":            "usr_01HX0...",
    "email":         "you@example.com",
    "name":          "Ada Lovelace",
    "organizations": [
      { "id": "org_01HX1...", "name": "Acme", "role": "OWNER" }
    ]
  }
}

Store the access_token and send it on every subsequent request as the Authorization header.

Sending the token

curl https://api.reachbell.com/projects \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "x-org-id: org_01HX1..."

The x-org-id header tells the API which organization context to use. Most routes that read or write organization-scoped resources require it. If you omit it on a route that needs it, you'll get a 403.

Token lifetime

Tokens expire after seven days by default. The exact window is controlled by the JWT_EXPIRES_IN env var on the API. When the token expires, calls return 401 Unauthorized — the dashboard redirects to the login screen; your own integrations should re-call /auth/login.

JWTs are bearer tokens. Treat them like passwords — never log them, never check them into source control, never expose them to a customer's browser unless that customer owns them.

API key authentication

API keys live on a single project. They authenticate every server-side call and the SDK on customer sites.

Key format

rb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
rb_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • rb_live_ keys hit your real subscribers and bill against your plan.
  • rb_test_ keys hit the sandbox — pushes are accepted but never actually delivered. Use these in CI.

Sending a key

curl https://api.reachbell.com/subscribers/project/proj_01HX2... \
  -H "x-api-key: rb_live_yourkey"

The key implicitly identifies the project, so most routes don't need an x-org-id header when called this way.

Rotating keys

API keys never expire on their own. You rotate them from the dashboard:

  1. Go to Settings → API keys.
  2. Click Generate new key — both keys are live simultaneously so you can roll your integration over with no downtime.
  3. Update your environment.
  4. Click Revoke on the old key.

Rotate any key you suspect has leaked. Rotate every key at least once a year regardless. Old keys live forever until revoked, and forever is a long time.

Rate limits

ReachBell rate-limits per IP address per route group.

Route groupLimit
Default600 requests / minute
Ingest (/subscribers, /transactional/*)120 requests / minute

When you exceed a limit you get a 429 Too Many Requests response with a Retry-After header (seconds). Back off and retry.

If your traffic is bursty and legitimate, contact support — we'll raise the cap on a per-project basis.

OpenAPI playground

Every endpoint described in these docs is also live in the interactive OpenAPI playground:

https://api.reachbell.com/docs

You can paste a JWT or API key into the "Authorize" panel and run requests against the live API straight from the browser.

Common authentication errors

A few responses you'll see while wiring things up for the first time:

{
  "statusCode": 401,
  "message":    "Invalid or expired token",
  "requestId":  "req_01HX4..."
}

Your JWT is expired or malformed. Re-call POST /auth/login.

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

The key doesn't exist or has been revoked. Double-check the value, and verify against the dashboard's Settings → API keys page.

{
  "statusCode": 403,
  "message":    "Missing x-org-id header",
  "requestId":  "req_01HX4..."
}

You authenticated with a JWT but didn't send the x-org-id header. Add it.

{
  "statusCode": 403,
  "message":    "Resource belongs to a different project",
  "requestId":  "req_01HX4..."
}

Your API key is scoped to one project; the resource you asked for lives on another. Use the right key.

Every error response includes a requestId. Always log it — it's the single most useful thing to quote to support when something doesn't work.

What's next?