JavaScript API

The window.Benson API for single-page apps, headless storefronts, custom carts and consent. Generated from the TypeScript types the loader ships.

Reference
Updated 26 September 20262 min

The Benson script exposes window.Benson. Every method is safe to call before the script has loaded: use the queue form and Benson replays the calls once it's ready.

// Safe before the script loads: queued and replayed in order.
(window.Benson = window.Benson || []).push(["setCart", {
  currency: "GBP",
  totalMinor: 4200,
  items: [{ id: "merino-jacket-m", qty: 1, priceMinor: 4200 }],
  codes: [],
}]);

Methods#

This list is generated at build time from the BensonPublicApi type the loader ships, so it always matches the script.

version: string
The loaded script's version.
push(cmd: [string, ...unknown[]]): void
Queue form usable before load: (window.Benson ||= []).push(['setCart', {...}]).
ready(cb: () => void): void
Runs your callback once Benson has loaded (straight away if it already has).
setCart(cart: BensonCart | null): void
codes default to [].
page(path?: string): void
SPA navigation → page_view + module re-scan.
on(event: BensonEventName, cb: (e: unknown) => void): () => void
Subscribes to an in-page event. Returns a function that unsubscribes.
getSessionId(): string | null
Null unless persistent ids are allowed; for headless order attribution.
debug(on?: boolean): void
Persists in sessionStorage.bn_debug.
registerApplyHandler(fn: BensonApplyHandler): voidoptional
Custom stores apply widget codes through this (queued until the widget loads).
widget?: BensonWidgetApioptional
Present once the widget module has loaded (calls before that are queued).

Cart#

Benson.setCart(cart) tells Benson what's in the basket on stores where it can't read the cart itself (headless, Hydrogen, custom checkouts). Shopify theme stores don't need it. Amounts are integers in minor units (pence, cents).

currency: string
totalMinor: number
items: BensonCartItem[]
codes: string[]
subtotalMinor?: numberoptional
Derived from items when absent.
itemCount?: numberoptional
Derived from items when absent.

Each item in items:

id: string
qty: number
priceMinor: number
Benson.setCart({
  currency: "GBP",
  totalMinor: 9600,
  items: [
    { id: "sku-123", qty: 2, priceMinor: 3600 },
    { id: "sku-456", qty: 1, priceMinor: 2400 },
  ],
  codes: ["AUTUMN15"],
});

// Empty or unknown cart:
Benson.setCart(null);

Single-page apps#

Call Benson.page() after each client-side navigation, so Benson records the page view and re-scans the new page:

router.afterEach((to) => Benson.page(to.fullPath));

Events#

Benson.on() subscribes to in-page events and returns an unsubscribe function. Events fire whether or not the session is sampled.

const off = Benson.on("ext_detected", (event) => {
  if (event.positive && event.actorClass === "extension") {
    console.log("Coupon extension:", event.actorId);
  }
});

The same events reach Google Tag Manager and GA4 through the dataLayer bridge as benson_ext_detected, benson_ext_blocked, benson_widget_shown and benson_code_applied.

On sites with manual consent, call Benson.consent() when the shopper decides. See Consent and storage.

myCmp.onChange((choices) => Benson.consent(choices.analytics === true));

Discount widget#

Once the widget has loaded, Benson.widget controls it for the current page view:

open(): void
close(): void
hide(): void
Hide the launcher and panel for this page view (merchant UI conflicts).

Custom checkouts can apply widget codes through their own function:

Benson.registerApplyHandler(async (code) => {
  const res = await fetch("/api/cart/discount", { method: "POST", body: JSON.stringify({ code }) });
  return res.ok ? { ok: true } : { ok: false, message: "That code can't be used on this basket." };
});

Headless order attribution#

Benson.getSessionId() returns the current session id when persistent ids are allowed (otherwise null). Add it to the order as a _benson_sid attribute so Benson can join the order to the session.

Debugging#

Add ?benson_debug=1 to any page URL, or call Benson.debug(true), to log what Benson is doing to the console. The switch is kept in session storage until you close the tab.

A ?benson_test= link from your dashboard runs a test for one page view: a simulated coupon pop-up that Benson detects and hides, so you can see it work. Benson ignores the parameter unless it confirms the token belongs to your store, then removes it from the address bar. That page view stores nothing, bypasses sampling and strict consent, writes no cart attributes, and never reaches your reports. See Test Benson on your store.

Stuck? Email [email protected].

Find out what's leaking. 14 days of Own, no card.