Mapping a Shopify points program onto the UCP loyalty extension
What maps cleanly, what does not, and four questions we could not answer from the released spec. Written for UCP contributors and implementers; useful whether or not you ever evaluate StabileRewards.
1. Version and sources
Everything here is mapped against the released UCP v2026-08-25 loyalty extension, merged in PR #340.
- Spec: ucp.dev/2026-08-25/specification/common/extensions/loyalty
- Schema: ucp.dev/2026-08-25/schemas/common/loyalty.json
- Release: v2026-08-25 release notes
- Identity linking (for the authorized example): identity-linking spec
Every loyalty object in the fixtures validates against that schema with a full JSON Schema 2020-12 validator; the warning and info messages validate against the released message schemas.
2. Scenario declaration
All data is synthetic. Harbor & Pine Tea (harborpine.example, a reserved TLD) is invented. No real merchant, shopper, balance, code, or order appears anywhere in this reference.
| Program | 5 points per $1 · $35 minimum to earn · 150-point first-order bonus · 500 points → $5 off, 900 → $12 off · 180-day expiry |
|---|---|
| Loyalty key | example.harborpine.rewards |
| Cart A | Loose-leaf tea sampler $48.00 + ceramic mug $24.00 = $72.00 |
| Cart B | Single-origin tea tin $28.00 (under the minimum) |
| Member | Synthetic returning member, three prior orders, masked ****4821 |
| Math source | The same functions StabileRewards serves to shopping agents today (buildProgramVisibility, buildRewardOptions). Changing that math without regenerating the fixtures fails our test suite. |
3. Field mapping
“Released UCP” means the field exists in the released schema and the fixture value validates. “Stabile-specific” means there is no released representation.
Loading mapping…
4. Public and authorized data
The anonymous projection answers with provisional: true and no display_id. The authorized projection resolves membership from an authenticated identity and returns provisional: false with a masked id. Available points and reward codes appear in neither.
Public Cart A, anonymous
…
Buyer-authorized Cart A, member
…
The authorized example is a controlled synthetic scenario. It is not evidence of completed production identity linking.
5. Observations
Questions and edge cases we hit while mapping. We would value maintainer and implementer views on each.
6. Reward lifecycle Not standardized
The released extension explicitly leaves redeemable balances to a payment instrument or a future redemption capability. This is how StabileRewards handles it today on Shopify, published as implementation evidence. Point movements are shown as deltas; codes are redacted.
Loading lifecycle…
7. Known gaps
- Balance. Available points are Stabile operational state, not loyalty-extension output.
- Redemption. Turning points into a discount code has no released UCP operation.
- Discount stacking. Follows each Shopify discount's combination settings; not modeled here.
- Settlement. Points settle when the order is created; see the lifecycle gaps below.
8. Our /.well-known/ucp
The loyalty extension decorates a business's catalog, cart, and checkout responses, and a business that advertises dev.ucp.common.loyalty must also serve those parent capabilities. StabileRewards is a loyalty provider to Shopify merchants, not a store, so our profile does not advertise the loyalty extension or any shopping capability. It declares one vendor-namespaced service, com.stabilerewards.loyalty, pointing at our merchant-scoped REST quote API and our MCP server, with no payment handlers.
UCP has no participant type for a loyalty provider yet. That is the subject of issue #384, and our reason for publishing this reference.
9. Downloads and changelog
1.0.0 — 2026-09-23
- First public reference against UCP v2026-08-25.
- Synthetic merchant, $72.00 and $28.00 carts, one synthetic member.
- Three fixtures, field mapping, four observations.
Fixtures are regenerated from StabileRewards' program code, checked against the released schema and a privacy denylist, and versioned. A schema change in a future UCP release gets a new fixture version, not a silent edit.
Questions or corrections: info@stabilerewards.com. Merchants can request a pilot.
StabileRewards