Transactional API
Transactional messages are sent to one recipient at a time, triggered by something happening in your application — a password reset, an order confirmation, a shipment update, a one-time code. They bypass the campaign builder.
From a Node backend, use the Server SDK (
@reachbell/node) instead of calling these endpoints directly —rb.transactional.send/rb.transactional.emailwrap them with typed helpers and error handling. The raw REST below is for other languages and for endpoints the SDK doesn't wrap yet.
Every transactional route authenticates with x-api-key (server-to-server only). The rate limit on each ingest endpoint is 120 requests per minute per IP.
Push
POST /transactional/send
Send a single push to one subscriber. You identify the subscriber either by externalId (preferred — your internal user ID, set via identify) or by their raw token.
curl -X POST https://api.reachbell.com/transactional/send \
-H "x-api-key: rb_live_yourkey" \
-H "Content-Type: application/json" \
-d '{
"externalId": "user_42",
"title": "Your order has shipped",
"body": "Tracking: 1Z999AA10123456784",
"url": "https://example.com/orders/A123",
"iconUrl": "https://cdn.example.com/icon-192.png",
"imageUrl": "https://cdn.example.com/order-hero.jpg",
"badge": "https://cdn.example.com/badge.png"
}'Response envelope
Transactional responses always come back with 200 OK and a structured envelope. Treat "not delivered" as a normal outcome, not an exception.
{
"success": true,
"subscriberFound": true,
"delivered": true,
"deactivated": false,
"reason": null
}Common reasons when delivered is false:
reason | What it means |
|---|---|
subscriber_not_found | No subscriber with that externalId or token on this project. |
subscriber_unsubscribed | The user revoked permission. |
frequency_capped | The project's delivery policy (Settings → Delivery policy) blocks another push within the window. |
quiet_hours | Quiet hours are active in the subscriber's timezone. |
provider_error | FCM / APNs / VAPID rejected. Details in the dashboard. |
Error responses are NORMAL, not exceptional. Your code should branch on
deliveredrather than throwing on every miss. Genuine bugs (bad payload, bad auth) still return non-2xx with the standard error envelope.
POST /transactional/email
curl -X POST https://api.reachbell.com/transactional/email \
-H "x-api-key: rb_live_yourkey" \
-H "Content-Type: application/json" \
-d '{
"to": "ada@example.com",
"subject": "Welcome to Acme",
"htmlContent": "<h1>Welcome</h1><p>Glad youre here.</p>",
"textContent": "Welcome. Glad youre here.",
"senderEmail": "hello@acme.com",
"senderName": "Acme Support",
"replyTo": "support@acme.com"
}'You can also send by template:
curl -X POST https://api.reachbell.com/transactional/email \
-H "x-api-key: rb_live_yourkey" \
-H "Content-Type: application/json" \
-d '{
"to": "ada@example.com",
"templateId": "tmpl_welcome",
"variables": { "name": "Ada", "ctaUrl": "https://acme.com/start" }
}'Sender domain enforcement
The senderEmail you pass must be on the project's verified email domain (the project.emailConfig.domain field) or a subdomain. If it isn't, the request returns 422 Unprocessable Entity with reason: "sender_domain_not_verified".
Verify your domain in Settings → Email → Domain setup before going live. Sending from an unverified domain blocks the API call outright — it doesn't silently rewrite the sender.
POST /transactional/whatsapp
Identify the recipient by externalId (preferred) or a direct E.164 to. Business-initiated messages use an approved template; inside the 24-hour customer-service window you may send free-form text instead.
curl -X POST https://api.reachbell.com/transactional/whatsapp \
-H "x-api-key: rb_live_yourkey" \
-H "Content-Type: application/json" \
-d '{
"externalId": "user_42",
"template": { "name": "verification_code", "params": ["482910"] }
}'template.name must be pre-approved by Meta (or your BSP); template.params fill its ordered placeholders. Most backends use the Server SDK (rb.transactional.whatsapp) rather than raw HTTP.
SMS
POST /transactional/sms
Identify by externalId or a direct E.164 to. The sender ID comes from your project's SMS configuration — you don't pass it per request.
curl -X POST https://api.reachbell.com/transactional/sms \
-H "x-api-key: rb_live_yourkey" \
-H "Content-Type: application/json" \
-d '{
"externalId": "user_42",
"text": "Your code is 482910. It expires in 5 minutes.",
"dltTemplateId": "1207xxxxxxxxxxx"
}'dltTemplateId is required where your route enforces DLT (India). The sender ID and provider are configured per project in the dashboard — see SMS. Most backends use the Server SDK (rb.transactional.sms).
Rate limits
Each ingest endpoint (send, email, whatsapp, sms) is independently capped at 120 requests per minute per IP. Exceeding a cap returns 429 Too Many Requests with a Retry-After header.
If you're sending more than 120/min sustained from one IP, batch via the dashboard's campaign sender instead, or contact support to raise the cap.
What's next?
- Wire up your backend with the Subscribers API to identify users before sending.
- Set up error handling for non-delivery reasons.
- Automate multi-touch flows with Automations instead of one-off transactional calls.