Points History — Build Your Own UI

Use these REST endpoints when you want the same data as the Points History widget without embedding the iframe or web component. The ledger payload matches the fields the widget renders.

Interactive API reference (Swagger / Redoc)

Open the live OpenAPI UI and jump to each operation:

EndpointRedoc
GET /api/widget/points-history/api/docs#operation/getPointsHistory
GET /api/widget/claim-rewards/balance/api/docs#operation/getPointsBalance
Full Widget tag/api/docs#tag/Widget
Machine-readable spec/api/docs/spec

Prerequisites

  1. SSO client (clientId + clientSecret) with your origins registered — see SSO.
  2. Pioneer widgets enabled on the deployment: PIONEER_WIDGETS=1. When the flag is off, these routes return 404.
  3. Your Legion base URL (e.g. https://app.yourshop.com), referred to below as {BASE_URL}.

Authentication

Send an SSO (or hub) bearer token:

Authorization: Bearer {TOKEN}

Mint the token server-to-server via POST /api/auth/sso (never expose clientSecret in the browser), or use window.legionAuth.getToken() when the storefront runs the Legion SSO WordPress plugin. Middleware verifies the JWT and stamps x-user-id for the handlers — clients only need the Authorization header.

CORS is granted for origins registered on your SSO client.


Endpoints you need

1. Current balance (optional header)

GET {BASE_URL}/api/widget/claim-rewards/balance Authorization: Bearer {TOKEN}
1{ 2 "balance": 1500, 3 "currency": "points", 4 "asOf": "2026-06-15T22:14:03.000Z" 5}

Use this for a balance badge above your history table. Swagger: getPointsBalance.

2. Paginated ledger (required)

GET {BASE_URL}/api/widget/points-history?limit=10&offset=0 Authorization: Bearer {TOKEN}
QueryDefaultNotes
limit10Max 100
offset0Skip N rows for pagination
1{ 2 "data": [ 3 { 4 "id": "b1c2d3e4-…", 5 "amount": 150, 6 "balance": 1500, 7 "transaction_type": "earn", 8 "description": "Order #1042", 9 "reference_type": "order", 10 "created_at": "2026-06-15T22:14:03.000Z" 11 } 12 ], 13 "pagination": { "limit": 10, "offset": 0, "total": 87 } 14}

Rows are newest-first and scoped to the authenticated shopper on the current shop. Swagger: getPointsHistory.

Field → UI mapping

FieldTypical UI use
created_atDate / time column
description or transaction_typeLabel (prefer description when present)
amountSigned points delta (+150 / -50)
balanceRunning balance after the row
transaction_typeBadge / filter (earn, claim, redeem, …)
reference_typeOptional secondary label (order, point_claim, …)
pagination.totalPage count / “Showing X–Y of Z”

Common transaction_type values: earn, claim, redeem, adjustment, refund, reversal. When the type is claim, reference_type is usually point_claim.


Minimal browser example

1async function loadPointsHistory( 2 baseUrl, 3 token, 4 { limit = 10, offset = 0 } = {} 5) { 6 const url = new URL("/api/widget/points-history", baseUrl); 7 url.searchParams.set("limit", String(limit)); 8 url.searchParams.set("offset", String(offset)); 9 10 const res = await fetch(url, { 11 headers: { Authorization: `Bearer ${token}` }, 12 }); 13 14 if (!res.ok) { 15 throw new Error(`points-history failed: ${res.status}`); 16 } 17 18 return res.json(); // { data, pagination } 19} 20 21// Example: first page 22const { data, pagination } = await loadPointsHistory( 23 "https://app.yourshop.com", 24 token, 25 { limit: 10, offset: 0 } 26);

Fetch the next page with offset = pagination.offset + pagination.limit while offset < pagination.total.


Reserved / claimable points

On shops that hold earnings as reservations until the shopper claims them:

  • List pending grants: GET /api/user/points/claimable
  • Claim: POST /api/user/points/claim
  • After a claim, refresh history — a single claim row appears in the ledger

Full walkthrough: Point Reservations guide.


Embed the widget instead

If you prefer the packaged UI (table, theming, pagination):

Documentation | Legion Hand Technologies