ReachBellDocs

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.email wrap 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:

reasonWhat it means
subscriber_not_foundNo subscriber with that externalId or token on this project.
subscriber_unsubscribedThe user revoked permission.
frequency_cappedThe project's delivery policy (Settings → Delivery policy) blocks another push within the window.
quiet_hoursQuiet hours are active in the subscriber's timezone.
provider_errorFCM / APNs / VAPID rejected. Details in the dashboard.

Error responses are NORMAL, not exceptional. Your code should branch on delivered rather than throwing on every miss. Genuine bugs (bad payload, bad auth) still return non-2xx with the standard error envelope.

Email

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.

WhatsApp

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.