Skip to content

Server SDKs

The Node.js, Python and PHP server SDKs, including listing notifications and verifying webhooks.

Thin wrappers over the REST API for your server — install from your package manager. They use your secret API key, so never put them (or the key) in code that runs in the browser; on web pages, use the relaybell-sdk.js script tag from the Quickstart.

LanguageInstall
Node.jsnpm install relaybell
Pythonpip install relaybell
PHPcomposer require relaybell/relaybell-php

Note

Prefer no package manager? Zipped builds are also available: Node · PHP · Python.

// Node
const { RelayBell } = require("relaybell");
const rb = new RelayBell({ apiKey: "rb_live_xxxxx" });
await rb.send({ title: "Hello", body: "World", url: "https://example.com" });
await rb.track("purchase", { externalId: "user_123", properties: { value: 42 } });

Each SDK can also make the userHash for identity verification, with no network call: Node userHash(secret, externalId), PHP Relaybell\Client::userHash($secret, $externalId), Python user_hash(secret, external_id) (Python SDK 1.1.0 or newer).

Version 1.2.0 of all three adds reading your sends — Node and PHP listNotifications(params) and getNotification(id), Python list_notifications(**params) and get_notification(id), for GET /api/v1/notifications (from, to, status, q, limit, cursor, tz) and /:id — and webhook verification in one call (last column). The Node SDK also ships TypeScript types, and throws if it is created in a browser, where your API key would be exposed.

SDKOptionsErrorsVerify a webhook (1.2.0)
Nodenew RelayBell({ apiKey, baseUrl?, timeout? }): timeout in ms (default 30000), baseUrl defaults to https://relaybell.com.A non-2xx answer, network failure or timeout throws RelayBellError with .status, .body and .code.verifyWebhook({ payload, headers, secret, tolerance = 300 }): payload is the raw body (string or Buffer), headers an object or Headers. Returns the parsed event; throws RelayBellWebhookError with .code.
PythonRelayBell(api_key, base_url="https://relaybell.com", timeout=30.0) (seconds).Raises relaybell.RelayBellError with .status and .body.verify_webhook(payload: bytes, headers, secret, tolerance=300): returns the event as a dict; raises relaybell.WebhookVerificationError with .code.
PHPnew Relaybell\Client($apiKey, $baseUrl = "https://relaybell.com", ["timeout" => 30, "connectTimeout" => …]) (seconds).Throws Relaybell\RelaybellException (getStatusCode()).Relaybell\Webhook::verify($payload, $headers, $secret, $tolerance = 300): $headers may be getallheaders() or $_SERVER. Returns the event as an array; throws Relaybell\WebhookVerificationException (getReason()).

Each verify function checks X-Relaybell-Webhook-Signature (the timestamp within tolerance seconds, the signature in constant time) and fails with one of these codes: missing_secret (an empty secret: it never verifies without one), missing_signature, malformed_signature, timestamp_out_of_tolerance, signature_mismatch, invalid_payload. The older X-Relaybell-Signature header is only accepted when you pass allowLegacy: true (Python allow_legacy=True, PHP a fifth argument true) and the request has no v1 signature.

Field names are the REST API's camelCase: externalId, externalIds, transactional, segmentId, sendAt. Node and PHP pass fields through as they are, so a snake_case targeting field such as external_id is refused with 400 unknown_field. The Python SDK 1.2.0 also takes snake_case keyword arguments and turns them into camelCase before sending — external_id, external_ids, segment_id, exclude_segment_id, exclude_filters, topic_id, send_at, per_timezone, local_time, require_interaction, smart_time, ab_test (with auto_winner, test_percent, after_hours) — and camelCase keeps working.