Developer Documentation

Point Reservations

How points are held and settled on both sides of the ledger — earned points the shopper must claim, and spent points that stay reversible until the order is invoiced.

Spend Side
Reversible Hold
Invoice-Settled

Reward Reservations (Spending Points)

This guide covers what happens after a shopper redeems a reward with their points. Redeeming no longer settles instantly: it creates a reservation — the points are deducted from the shopper's balance right away, but the redemption stays open until Legion sees the matching invoice from the commerce platform. If no invoice ever arrives, the reservation lapses and the points are automatically refunded.

This is the spend-side counterpart of claimable points (earn-side reservations, covered in the Earning tab above). A shopper first claims earned points into their spendable balance, then spends them on a reward — which creates the reservation described here.


How it works

1Your site Legion (user-x) Commerce / ERP (Sage) 2───────── ─────────────── ───────────────────── 31. Shopper clicks "Claim" ─► POST /api/widget/claim-rewards/redeem 4 ├─ deducts points (reversible hold) 5 └─ returns sku + reservationCode + expiresAt 62. Add SKU to cart, stamp reservationCode on the order ──► order placed 7 (customerPONo = code) 83. Order invoiced in Sage ◄────────────────────────────── invoice created 94. Legion invoice sync ◄─ matches invoice to reservation 10 └─ finalizes: redemption → fulfilled 11 …or, if no invoice arrives before expiresAt: 12 Legion expiry sweep └─ reservation → expired, points refunded

Your integration only changes at step 2: alongside adding the returned sku to the cart, stamp the returned reservationCode on the order so it reaches your ERP as the customer PO number (customerPONo in Sage). Steps 3–4 are handled entirely by Legion's hourly invoice sync.

Reservation lifecycle

StatusMeaning
pendingReserved. Points already deducted from the balance, but the hold is reversible.
fulfilledFinalized. The invoice sync matched a Sage invoice to the reservation; the deduction is final.
expiredNo invoice arrived before expiresAt. The points were refunded automatically.
cancelledReversed (e.g. order cancelled or refunded). The points were refunded.

Every transition is auditable: the finalizing invoice number, date, and customer are stamped into the redemption record, and the refund on expiry/cancellation appears in the shopper's points history as a refund transaction.


1. Redeeming creates the reservation

POST /api/widget/claim-rewards/redeem works exactly as before — same request, same authentication — but the response now carries two extra fields:

1POST {BASE_URL}/api/widget/claim-rewards/redeem 2Authorization: Bearer {TOKEN} 3Content-Type: application/json 4 5{ "rewardId": "b1f3…" }
1{ 2 "redemptionId": "9c2a…", 3 "transactionId": "4d7e…", 4 "balance": 2500, 5 "currency": "points", 6 "sku": "STORE-CREDIT-100", 7 "reservationCode": "RSV-Y3BZVH3R6HFZ", 8 "expiresAt": "2026-08-21T16:14:03.000Z" 9}
  • reservationCode — a short, unguessable code (RSV- + 12 characters) that uniquely identifies this reservation. It fits within Sage's 30-character customerPONo limit by design.
  • expiresAt — when the unfinalized reservation lapses and the points are refunded. The window is a per-deployment setting (30 days by default).

The shopper's balance returned here already reflects the deduction — display it as-is. No UI changes are required beyond (optionally) surfacing the reservation code on the order confirmation.

2. Stamp the reservation code on the order

When you add the reward sku to the cart and the shopper checks out, set the order's customer PO number to the reservationCode. How you do this depends on your platform; the requirement is simply that the value arrives in Sage as the invoice's customerPONo.

1const { sku, reservationCode } = await redeemReward(token, reward.id); 2 3// WooCommerce (legion-sso plugin) — pass it through to the order meta: 4await window.legionAuth.addToCartBySku(sku, { 5 quantity: 1, 6 rewardId: reward.id, 7 purchaseOrder: reservationCode, // becomes customerPONo in Sage 8});

Why it matters. The reservation code is the primary matching key. If it is missing, Legion falls back to matching by shopper + SKU, which works for one open reservation per SKU but cannot disambiguate a shopper with two pending reservations for the same reward. Always stamp the code when your platform allows it.

3. Legion finalizes (or expires) the reservation

You do not call anything for this step. Legion's invoice sync runs hourly:

  1. It pulls new invoices from the ERP (Sage) since the last run.
  2. For each invoice it looks for a pending reservation whose reservationCode equals the invoice's customerPONo (primary match), or — failing that — a pending reservation for the same shopper and an invoiced SKU (fallback match).
  3. Matched reservations become fulfilled; the invoice number, date, and customer are recorded on the redemption for audit.
  4. Reservations past expiresAt become expired and the points are refunded in the same sweep.

Fulfilled and expired reservations are visible to shop staff in the admin console (Points → Redemptions → History), including the reservation code and finalizing invoice number.


Monitoring from your side (optional)

  • Points history (GET /api/widget/points-history) shows the initial deduction at redeem time and, if the reservation later expires or is cancelled, a refund row re-crediting the points.
  • Balance (GET /api/widget/claim-rewards/balance) always reflects holds: a pending reservation's points are not spendable.
  • Order webhook — if your platform already reports order events to POST /api/webhooks/claim-rewards/redeem, order.cancelled and order.refunded release the hold immediately instead of waiting for expiry.

Integration checklist

  • Redeem response's reservationCode and expiresAt captured.
  • Reservation code stamped on the commerce order so it reaches Sage as customerPONo (≤ 30 chars — the code always fits).
  • Balance shown from the redeem response / balance endpoint (already reflects the hold).
  • No point deduction on your side at checkout — Legion owns the ledger.
  • (Optional) Order cancellations/refunds reported to the redeem webhook for immediate release of the hold.
  • Shoppers informed that unfinalized reservations auto-refund after the expiry window.

Related Documentation

SSO Authentication — every endpoint here requires an SSO bearer token. Read the SSO guide →

Claim Rewards UI — catalog, balance, and redeem endpoints for building a custom rewards experience. Back to the integration guide →