API keys & your first server send
This is the backend on-ramp. By the end you'll have a project, a server API key, and a transactional push delivered to a real subscriber — sent two ways, with the Server SDK and with a raw cURL.
If you only need browser web push (a prompt on your site, subscribing users, segments), you don't need any of this — that's the client SDK path, and sending happens in the dashboard. Reach for a server API key when you want to send transactional messages or track events from your own backend.
Client key vs. server key
ReachBell projects have two ways in, and the distinction matters:
| Client SDK key | Server API key | |
|---|---|---|
| Used by | @reachbell/sdk in the browser | @reachbell/node and raw HTTP from your backend |
| Where it lives | shipped in browser code | environment variable on your server, never shipped |
| Format | rb_live_… | rb_live_… |
| What it can do | read project context, register/identify/tag the current device | send transactional messages, track events, manage subscribers |
Both are project-scoped keys with the rb_live_ prefix. The difference is where you use them and what you let them touch: the client key is fine in front-end code because the SDK only exposes a narrow, device-scoped surface with it; the server key drives the full Server API and must stay on your backend. Treat the server key like a password.
Never put your server key in browser code. Anything that reaches a browser is public. The client SDK is built to operate safely with the public key; your backend integration is not — keep the server key in an environment variable.
1. Create a project
A project maps to one website or app. Its VAPID keys, API key, and subscriber list are isolated from every other project in your organization.
In the dashboard, create your first project (you're prompted to on first sign-in, or use + New project). Pick a name, paste your site URL, and hit Create. The key is generated with the project — there's nothing extra to provision.
2. Get your API key
Open the project, then go to Settings → API keys. Your key is shown there, looking like:
rb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Copy it into your server environment — don't paste it into source control:
export REACHBELL_API_KEY="rb_live_yourkey"Keep
REACHBELL_API_KEYin a secret manager or a.envfile that's gitignored. If a key ever leaks, rotate it from the same Settings → API keys screen.
3. Send your first transactional push
You'll send to one subscriber, addressed by the externalId you set from the browser with ReachBell.identify() (or by a raw token). The two snippets below are equivalent — the SDK is a thin wrapper over the same endpoint.
With the Node SDK
npm install @reachbell/nodeimport { ReachBell } from "@reachbell/node";
const rb = new ReachBell({ apiKey: process.env.REACHBELL_API_KEY! });
const result = await rb.transactional.send({
externalId: "user_42",
title: "Order shipped 📦",
body: "Your order arrives tomorrow.",
url: "https://shop.example/orders/4821",
});
// A 200 with delivered:false just means the user has no active subscription —
// it's a normal outcome, not an error.
if (!result.delivered) console.warn("no active subscription for user_42");@reachbell/node requires Node 18+ (it uses the built-in fetch). See the Server SDK reference for email, SMS, WhatsApp, event tracking, and subscriber management.
With raw cURL
The SDK sends your input verbatim to POST /transactional/send with your key in the x-api-key header — so the equivalent HTTP call is:
curl -X POST https://api.reachbell.com/transactional/send \
-H "x-api-key: $REACHBELL_API_KEY" \
-H "content-type: application/json" \
-d '{
"externalId": "user_42",
"title": "Order shipped 📦",
"body": "Your order arrives tomorrow.",
"url": "https://shop.example/orders/4821"
}'To send from a saved template instead of an inline title/body, pass a templateId (and variables for its placeholders). See the Transactional API for the full field list.
What you'll get back
A successful call returns 200 with a result describing what happened — including whether a matching subscriber was found and whether the push was delivered. A 200 with delivered: false is the expected response when the user simply has no active subscription; it is not an error, so branch on the result rather than relying on the status code alone.
Every response also carries an x-request-id header, and error bodies include a matching requestId — log it; it's the fastest way for support to trace a call. See API conventions for the auth header, rate limits, and error envelope, and error codes for what each failure means.
What's next?
- Read the Server SDK reference for the full set of backend helpers.
- Learn the cross-cutting rules in API conventions — auth, rate limits, pagination, errors.
- Going lower-level? Authentication covers both API keys and dashboard JWTs.
- Trigger sends off your own events with the Events API and the Automation guide.