Skip to content

Balance widget

Show a signed-in shopper their points on any web page with one script tag and a server-minted session token.

Updated 2026-09-28

The balance widget shows a signed-in shopper their points, tier and anything about to expire, on any web page, with one <div> and one script tag. It has no dependencies, sets no cookies and does no tracking. An API key never reaches the browser: your server mints a short-lived member session token and renders it into the page.

How it works

  1. The shopper signs in to your site as usual.
  2. Your server, which knows who they are, mints a member session token with POST /v1/members/{id}/session-tokens. The token is read-only, names one member and expires after 15 minutes.
  3. Your page puts that token on the widget's <div> as data-token.
  4. The script calls GET /v1/public/member with the token (the one endpoint that allows cross-origin calls) and draws the card. The response carries points, tier and referral code, never contact details.

Mint a token on your server

Needs a key with the read scope. Name the member by your own customer id (ref:…), their phone (phone:…) or our member id:

Server: mint a token
curl -X POST https://stickytier.com/v1/members/ref:CUST-1001/session-tokens \
  -H "Authorization: Bearer $KEY"
# → 201 { "data": { "token": "smt_…", "expires_at": "…", "member_id": "M-…" } }

A 403 means the member's account is suspended, deleted or erased — no token is issued, so leave the widget out. A 404 means the customer is not a member yet: leave data-token out (the widget then asks them to sign in) or show your own "Join" prompt and enrol them from the server with POST /v1/members.

Mint the token when you render the page. Never cache one across shoppers, and never put an API key (sticky_live_… or sticky_test_…) in a page — the widget and GET /v1/public/member only accept session tokens.

Embed it

Page
<div data-stickytier-widget data-token="smt_…"
     data-portal-url="https://rewards.yourstore.com" data-accent="#0f766e"></div>
<script src="https://stickytier.com/widget/stickytier-widget.js" async></script>

Every element with the data-stickytier-widget attribute becomes a widget, so one script serves several on a page. The script reads these attributes:

AttributeRequiredWhat it does
data-stickytier-widgetyesMarks the element the widget draws into.
data-tokenyes, for a signed-in shopperThe member session token from your server. Without one the widget shows "Sign in to see your rewards."
data-portal-urlnoAn https:// address for a "View rewards →" link, such as your hosted rewards page. Other schemes are ignored.
data-accentnoAny CSS colour value for the balance and the link. Defaults to indigo.
data-localenoA BCP 47 locale for number formatting, such as en-GB. Defaults to the programme's own locale.

The script talks to the site it was loaded from, so load it from https://stickytier.com as shown.

Refreshing and expiry

A token lasts 15 minutes. The widget only fetches when the page loads and when you call refresh() — it does not poll — so an expired token shows up at the next redraw, where the widget asks the shopper to refresh the page. On long-lived pages (a single-page app, say), fetch a fresh token from your server, set it on the element and call:

After swapping in a new token
document.querySelector("[data-stickytier-widget]").setAttribute("data-token", freshToken);
window.StickyTierWidget.refresh();

refresh() redraws every widget on the page.

Events

The widget's element fires two DOM events that bubble, so you can react to them anywhere up the tree:

  • stickytier:loaded — event.detail is the member data from GET /v1/public/member (program_name, currency_name, locale, tier, balances and so on).
  • stickytier:error — event.detail.reason is expired for any 401 — the token is invalid or has run out, or the programme is no longer active — otherwise a short message. The widget has already shown the shopper a neutral message.
Hide the card for shoppers with nothing to show
document.addEventListener("stickytier:loaded", (e) => {
  if (e.detail.balances.spendable_cents === 0) e.target.hidden = true;
});

Your own design instead

The widget is a convenience. For a fully custom card, or in a mobile app, call GET /v1/public/member yourself with Authorization: Bearer smt_… — see A mobile app. For a "My rewards" button that opens your hosted rewards page already signed in, use POST /v1/members/{id}/portal-links from your server (see SDKs, widget & plugins).