Browser SDK reference
RelayBell.init() options (projectId, apiBase, swPath, swScope, externalId/userHash, tags, consent, autoCapture), window.relaybellInit and every SDK method.
RelayBell.init({
projectId: "YOUR_PROJECT_ID", // required
apiBase: "https://relaybell.com", // optional: defaults to where relaybell-sdk.js was loaded from
swPath: "/sw.js", // service-worker path on your domain
externalId: "user_123", // link this browser to your own user id
userHash: "9f2c…", // your server's signature of externalId (see Identity verification)
tags: { plan: "pro" }, // custom attributes for segmentation
requireConsent: false, // true = wait for grantConsent() (CMP integration)
policyVersion: "2026-01", // recorded with consent for your audit trail
autoCapture: false // true = record pageviews + data-rb-event clicks (see below)
});| Option | Description |
|---|---|
projectId * | Your website's Project ID (dashboard: Install page, or Settings → Developers). Without it init() throws relaybell: projectId is required. |
apiBase | The relaybell address. Left out (or null), the origin relaybell-sdk.js was loaded from; "" means the page's own origin. A trailing / is removed. |
swPath | Where your copy of sw.js is, on your own domain. Default "/sw.js". A full URL on another host (e.g. rewritten by a CDN plugin) is cut back to its path on the page's domain, since browsers only accept a same-origin service worker. |
swScope | Optional scope for the service-worker registration, e.g. "/folder/" when sw.js isn't at the root. It is also how the SDK finds the registration again on later pages, so set it whenever sw.js lives in a folder. Left out, the browser's default (the folder sw.js is in). |
externalId, userHash | Your own user id for this visitor, and your server's signature of it (see Identity verification). Sent with the subscription and with every track() event. |
tags | Custom attributes, { key: value }; values are stored as text. Used by segments (tag:KEY) and merge tags ({KEY}). Keys starting with rb_ are reserved and ignored. |
requireConsent, policyVersion | With requireConsent: true the SDK registers no service worker, shows no opt-in prompt and sends no auto-captured events until you call grantConsent() (remembered in the browser's localStorage; revokeConsent() forgets it and unsubscribes). On-site messages still show. policyVersion is stored with the opt-in. |
autoCapture | true records page_view (including single-page-app route changes) and [data-rb-event] clicks; or pass { pageviews, clicks, scroll } to choose (scroll sends scroll_depth at 25/50/75/100%; it is off unless you turn it on). Only for subscribed or identified visitors, and nothing at all while requireConsent is waiting for consent. See Events. |
init() returns a promise. Where push can't work it resolves { supported: false, needsInstall, reason } (needsInstall is true on an iPhone/iPad outside a Home Screen web app); while waiting for consent, { supported: true, waitingForConsent: true }.
Call init() once per page. A second call starts again from its own options. In React or Next.js, load the script with next/script (strategy="afterInteractive") and call RelayBell.init() in its onLoad inside a client component (or set window.relaybellInit, below), and put sw.js in Next's public/ folder so it is served at /sw.js. Step by step: Next.js, React, Vue and Nuxt.
Script loaded late? If your own code may run before relaybell-sdk.js has loaded (a delayed or consent-gated script), set the options on the page instead — the SDK starts itself with them once it loads (on the page's load event, or straight away if that has passed), unless init() already ran:
<script>window.relaybellInit = { projectId: "YOUR_PROJECT_ID", apiBase: "https://relaybell.com" };</script>
<script src="https://relaybell.com/relaybell-sdk.js" defer></script>Methods
| Method | What it does |
|---|---|
init(opts) | Registers the SW, shows the opt-in prompt, subscribes (see options). |
subscribeNow() | Triggers the browser permission prompt (call from a click). Resolves { supported, subscribed, permission?, reason? } — subscribed is true only once relaybell has stored the subscription; supported is false where the browser can't do push (e.g. iOS Safari in a normal tab). Safe to call before init() has finished: it waits up to ~15 s, then resolves with reason: "not_ready". |
isSubscribed() | Resolves true if this browser has a push subscription for your site. Never prompts — use it to decide whether to show a “Notify me” button or to track() interest. |
identify(externalId, userHash) | Links this browser to your own user id after the page has loaded (for example once the visitor signs in): an existing subscription is saved again with the id, and later track() events carry it. A browser with no id yet joins that user (a second device joins the same profile). A browser already linked to a different user moves to the new one only with a valid userHash (an account switch on a shared browser); without one it stays with the first user, so on a site without identity verification call unsubscribe() when someone signs out of a shared browser. Calling it again with the same values does nothing; identify(null) stops sending an id from this page. userHash is optional unless you require identity verification. Safe to call before init(). |
unsubscribe() | Removes this browser's push subscription and tells relaybell, which records the opt-out (webhook subscriber.unsubscribed). Resolves { unsubscribed: true }. |
isSupported() | { supported, browser, os, needsInstall, reason } — feature detection, no network call. |
setTags({...}) | Merges the tags into the ones given to init() and, if this browser is subscribed, saves them (a key is overwritten, never removed). Resolves the merged tags. |
setTopics(["news"]) | Saves the topics this subscriber wants, by slug. It replaces the whole list ([] leaves every topic); unknown slugs are skipped. Works only for a browser with a push subscription: resolves { ok: true, applied } (how many topics were saved), or { error: "subscriber_not_found" }. |
showPreferences() | Opens a preference-center modal of your topics, ticked with the subscriber's current choices; saving calls setTopics(). |
showInbox() | Opens a modal of your 15 most recent broadcasts (never transactional, automation, journey or one-person sends). |
track(event, props) | Sends a behavioral event (fires automations, journeys and attribution). From the browser, the person is the subscriber who owns this browser's push subscription: an event from a browser that isn't subscribed is stored but triggers nothing for anyone. Its externalId is kept only when verified, or when verification isn't required and no userHash is sent. |
trackConversion(value, props) | Shorthand for track("purchase", { value, ...props }). |
grantConsent() / revokeConsent() | For cookie-consent (CMP) gating when requireConsent is set: grantConsent() starts the SDK as init() would have; revokeConsent() forgets the consent and calls unsubscribe(). |
// Behavioral tracking → powers cart abandonment, product alerts, attribution
RelayBell.track("cart.updated", { productId: "1234", product: "Blue Shoes", price: 49, url: location.href });
RelayBell.trackConversion(79.99, { orderId: "A123" });