Loyalty Extension¶
Overview¶
The loyalty extension is designed to facilitate high-fidelity loyalty experiences: ensuring existing loyalty members can seamlessly access their benefits during agentic Catalog, Cart, and Checkout experiences. By enabling buyers to see their specific tier, eligible rewards, and immediately applicable benefits before finalizing a purchase, it addresses a foundational expectation for program members and removes friction from the checkout funnel.
Specifically the following core use cases of benefit recognition for known members are addressed:
- Price-Impacting Benefits: Real-time application of member-only discounts and free shipping offers with clear attribution of benefit sources.
- Non-Price Benefits: Transparent display of rewards earned or rewards applicable to future purchases.
- Status Recognition: Verification and display of the buyers’ specific loyalty tier within a program.
Key Concepts¶
Loyalty has four main components:
Memberships: Distinct enrollment pathways or program categories that a user can join
- Independent programs offered by the same brand (e.g., a "Rewards Club" vs. a "Co-branded Credit Card") are modeled as separate, independently verifiable memberships. They are programmatically represented as separate sibling top-level keys in the loyalty extension map, namespaced by reverse-domain naming.
Tiers: Specific achievement ranks or status milestones within a membership that unlock escalating value as a member progresses through activity or spend
- A member typically holds a single active tier per membership. For programs with parallel status dimensions (e.g., holding both "Gold" and "Lifetime Platinum"), multiple tiers can be active concurrently.
Benefits: Ongoing perks and privileges granted to a customer based on their current tier or membership status
- Contains both delayed (e.g. “Members have access to dedicated customer service”) and immediate-value (e.g. “Members get 5% off”) benefits.
Rewards: Quantifiable loyalty value that may be earned from the current transaction. Note: Redeemable balances and stored value are modeled by the negotiated payment instrument or a future redemption capability, not by this loyalty extension.
- One membership can offer multiple types of accumulable/collectable rewards, each having its own usage and redemption rules.
{
"loyalty": {
"com.example.loyalty": {
"id": "membership_1",
"name": "Example Rewards",
"tiers": [
{
"id": "gold",
"name": "Gold",
"benefits": [
{ "id": "BEN_001", "description": "Early access to sales" }
]
}
],
"rewards": [
{
"currency": { "name": "LoyaltyStars", "code": "LST" }
}
],
"provisional": false
}
}
}
Discovery¶
Businesses can follow the standard advertising mechanism to advertise loyalty support in the Business profile. Currently the loyalty extension can decorate catalog search, catalog lookup, cart, and checkout capabilities. Businesses MAY advertise loyalty support for any subset of these capabilities. Platforms SHOULD check which resources are extended.
{
"ucp": {
"version": "draft",
"capabilities": {
"dev.ucp.shopping.catalog.search": [
{
"version": "draft",
"spec": "https://ucp.dev/draft/specification/catalog/search",
"schema": "https://ucp.dev/draft/schemas/shopping/catalog_search.json"
}
],
"dev.ucp.shopping.catalog.lookup": [
{
"version": "draft",
"spec": "https://ucp.dev/draft/specification/catalog/lookup",
"schema": "https://ucp.dev/draft/schemas/shopping/catalog_lookup.json"
}
],
"dev.ucp.shopping.cart": [
{
"version": "draft",
"spec": "https://ucp.dev/draft/specification/cart",
"schema": "https://ucp.dev/draft/schemas/shopping/cart.json"
}
],
"dev.ucp.shopping.checkout": [
{
"version": "draft",
"spec": "https://ucp.dev/draft/specification/checkout",
"schema": "https://ucp.dev/draft/schemas/shopping/checkout.json"
}
],
"dev.ucp.common.loyalty": [
{
"version": "draft",
"extends": [
"dev.ucp.shopping.catalog.search",
"dev.ucp.shopping.catalog.lookup",
"dev.ucp.shopping.cart",
"dev.ucp.shopping.checkout"
],
"spec": "https://ucp.dev/draft/specification/loyalty",
"schema": "https://ucp.dev/draft/schemas/common/loyalty.json"
}
]
}
}
}
Dependencies:
- Catalog Search Capability
- Catalog Lookup Capability
- Cart Capability
- Checkout Capability
Schema¶
Entities¶
Loyalty¶
Key-value map whose keys represent buyer/platform asserted eligibility claims and whose values represent associated membership information. All loyalty keys MUST use reverse-domain naming to ensure provenance and prevent collisions when multiple extensions contribute to the shared namespace.
Key-value map whose keys represent buyer/platform asserted eligibility claims and whose values represent associated membership information. All loyalty keys MUST use reverse-domain naming to ensure provenance and prevent collisions when multiple extensions contribute to the shared namespace.
Loyalty Membership¶
Loyalty membership the business has accepted for the eligibility claim represented by the parent map key. Programs that can be joined independently MUST be modeled as separate sibling entries under the loyalty map, distinguished by reverse-domain naming (e.g., 'com.example.rewards' and 'com.example.rewards.card').
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique loyalty membership identifier. |
| name | string | Yes | Business specific name of the loyalty membership/program. |
| display_id | string | No | A masked or partial version of the membership id for user recognition (e.g., '****5678'). MUST NOT be set if the membership has not been verified. |
| tiers | Array[object] | No | Active or display-safe tier context for this membership. Most programs are single-status (one entry); programs with parallel status dimensions (e.g., current and lifetime) populate one entry per active tier. Omitted when no tier context has been resolved. |
| rewards | Array[object] | No | Reward types and earning forecasts associated with this membership. Each object encapsulates one type of reward. |
| provisional | boolean | Yes | True if this membership requires additional verification. |
Membership Tier¶
Specific achievement rank or status milestone that unlocks escalating value as a member progresses through activity or spend.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique identifier for the membership tier. |
| name | string | Yes | The human-readable name of the tier (e.g., 'Platinum'). |
| benefits | Array[object] | No | List of benefits associated with this tier. |
Membership Tier Benefit¶
Benefits associated with a membership tier.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique identifier for the tier benefit. |
| description | string | Yes | A display-ready, human-readable explanation of this benefit (e.g. 'Early access to sales'). |
Membership Reward¶
Quantifiable reward type and optional earning forecast for the current transaction.
| Name | Type | Required | Description |
|---|---|---|---|
| currency | object | Yes | A unit of value that customers can accumulate through various commercial activities. |
| earning_forecast | object | No | Preview of rewards to be earned from the current transaction. |
Reward Amount¶
Non-negative integer amount denominated in the minor unit of the associated reward currency. The associated reward currency's decimal_places defines the minor-to-major ratio and defaults to 0 when omitted.
Non-negative integer amount denominated in the minor unit of the associated reward currency. The associated reward currency's decimal_places defines the minor-to-major ratio and defaults to 0 when omitted.
Reward Currency¶
The currency of the loyalty reward.
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Human-readable name of the currency (e.g. 'LoyaltyStars'). |
| code | string | Yes | Business-specific representation of the currency (e.g. 'LST'). |
| decimal_places | integer | No | The position of a digit to the right of a decimal point. Applies to all amount related fields for rewards. |
Earning Forecast¶
Preview of rewards to be earned from the current transaction.
| Name | Type | Required | Description |
|---|---|---|---|
| amount | integer | Yes | Total rewards to be earned if the transaction completes. |
| breakdown | Array[object] | No | List of breakdown of earning contributing to the total. |
Earning Breakdown¶
Breakdown rule of the reward earnings
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique rewards breakdown rule identifier. |
| amount | integer | Yes | Rewards earned from this rule. |
| description | string | Yes | A display-ready, human-readable rationale for the specific rewards (e.g. 2x on footwear). |
| benefit_id | string | No | Optional id of the membership_tier_benefit that produced this rewards rule. Resolves against membership_tier_benefit.id within the same parent loyalty membership. |
Loyalty behavior¶
The loyalty extension holds a key-value map whose keys are reverse-domain identifiers —
the same convention as services, capabilities, and payment handlers in the business
profile. The keys represent eligibility claims about loyalty memberships that
businesses recognize. The values contain membership information corresponding to those
claims and use the required provisional field to indicate the verification state.
Programs that can be joined independently MUST be modeled as separate sibling entries under the loyalty map, distinguished by their reverse-domain naming.
Platforms MAY send buyer loyalty membership claims via context.eligibility in the
request to activate the loyalty extension and claim loyalty benefits. Alternatively,
when the buyer is authenticated and the business can determine loyalty membership from
the authenticated identity, businesses MAY populate the loyalty extension without an
explicit eligibility claim. In this case, the map key MUST be the same reverse-domain
identifier the business would accept as a claim value.
- When a business verifies a membership claim or determines membership from
authenticated identity, it MUST return
provisional: false. It MUST populate the active tier(s) the buyer holds within thetiersarray when the program has tier structure — for programs with no tier concept (flat-rate cashback, single-status memberships), businesses MAY omittiers. It also SHOULD setdisplay_idas a masked unique identifier for the buyer. - When a membership claim in the request is recognized and accepted but not verified by
the business, the business MUST return
provisional: true. It MAY return display-safe tier context for the state accepted during the session, and MUST NOT returndisplay_iduntil the membership is verified. - When a membership claim in the request is accepted but cannot be verified, the business
MUST communicate the failure via a recoverable
messagewithtype: "error"andcode: "eligibility_invalid". Platforms MAY then choose to remove the membership claim and proceed through checkout without loyalty benefits applied.
At checkout completion, all accepted but unverified loyalty claims MUST be resolved per the Eligibility Verification at Completion contract defined in the checkout capability.
Monetary loyalty benefits¶
Monetary price-impacting loyalty benefits (e.g. member pricing/shipping) can have conditions beyond membership, such as saving $10 only after a $500+ purchase. Businesses MUST evaluate those conditions before applying the benefit.
When the benefit applies, businesses MUST surface the price impact through the base
capability's price fields. Catalog responses use price / list_price and
price_range / list_price_range; cart and checkout responses use totals or
line_items[].totals with type: "items_discount" (for member pricing) / type: "discount"
(for member shipping) and display_text to attribute the loyalty source when possible.
For cart and checkout responses, when the discount extension is active, businesses
SHOULD also populate discounts.applied[] for structured attribution. In that case,
eligibility identifies the claim required for the discount. If the discount still
requires verification, for example because one or more accepted loyalty claims remain
unverified, the corresponding applied discount MUST set provisional: true.
If the benefit does not apply, businesses SHOULD notify the buyer via messages with
type: "warning" and explain the inapplicability of those monetary loyalty benefits.
Businesses MUST NOT put inapplicable benefits in the discount extension. Instead they
MAY set them as part of benefits within the loyalty extension and set the warning
message path to reference the relevant membership for additional context.
When loyalty membership claims are accepted, businesses MAY use type: "info" to
explain the effects of applied monetary loyalty benefits.
Loyalty benefits message codes:
| Type | Code | When |
|---|---|---|
info |
membership_benefit_eligible |
Specific benefit confirmed applicable to this order |
warning |
membership_benefit_ineligible |
Benefit exists but conditions not met for this order |
The examples below are abbreviated to focus on loyalty and discount extension fields;
complete cart and checkout responses also include base required fields such as ucp,
id, currency, and totals.
Building on the store loyalty card example from Eligibility Verification at Completion, assume the card offers one unconditional product discount and one conditional discount that the current checkout cart fails to satisfy. The platform can surface the first provisional discount with disclaimers like "verified at purchase" and additionally show a warning message to disclose the inapplicability of the second discount.
{
"discounts": {
"applied": [
{
"title": "Loyalty benefit 1",
"amount": 10,
"provisional": true,
"eligibility": "com.example.loyalty.store_card",
"allocations": [
{"path": "$.line_items[0]", "amount": 10}
]
}
]
},
"loyalty": {
"com.example.loyalty.store_card": {
"id": "membership_1",
"name": "My Loyalty Program",
"tiers": [
{
"id": "cardholder",
"name": "Store Cardholder",
"benefits": [
{ "id": "BEN_001", "description": "Get $10 off with $500+ purchase." },
{ "id": "BEN_002", "description": "Early access to sales" }
]
}
],
"provisional": true
}
},
"messages": [
{
"type": "warning",
"code": "membership_benefit_ineligible",
"path": "$.loyalty['com.example.loyalty.store_card']",
"content": "Cart size is smaller than required to receive the $10 discount."
}
]
}
The buyer can proceed to checkout without any cart update. If the claim is verified
successfully by the business, the unconditional member-pricing discount becomes
non-provisional and display_id is returned.
{
"discounts": {
"applied": [
{
"title": "Loyalty benefit 1",
"amount": 10,
"provisional": false,
"eligibility": "com.example.loyalty.store_card",
"allocations": [
{"path": "$.line_items[0]", "amount": 10}
]
}
]
},
"loyalty": {
"com.example.loyalty.store_card": {
"id": "membership_1",
"display_id": "****5678",
"name": "My Loyalty Program",
"tiers": [
{
"id": "tier_1",
"name": "Store Cardholder",
"benefits": [
{ "id": "BEN_001", "description": "Get $10 off with $500+ purchase." },
{ "id": "BEN_002", "description": "Early access to sales" }
]
}
],
"provisional": false
}
}
}
If the claim cannot be verified, the business MUST return a recoverable error via
messages[] with code: "eligibility_invalid".
Use Cases and Examples¶
With the help of the loyalty extension, the catalog, cart, and checkout capabilities can be further decorated to provide full visibility into buyers’ member-exclusive perks and allow the platform to render the extra information to facilitate the transaction.
Price-Impacting Benefits¶
The loyalty extension can provide buyer status information that helps the platform
explain member discounts. Price-impacting loyalty benefits are reflected in the base
capability's price fields. When the discount extension is also active, the platform can
explain each discount via discounts.applied[].title and correlate
discounts.applied[].eligibility back to loyalty entries to show which accepted
membership claims produced the monetary benefit. In the example below, the buyer
receives a 15% bonus discount and free shipping benefit respectively because they
hold the Retail Club membership and the Retail Card. The platform can then render
“Retail Club Gold Member and Retail Card benefits applied,” for example.
{
"line_items": [
{
"id": "li_1",
"item": {
"id": "prod_1",
"title": "T-Shirt",
"price": 1000
},
"quantity": 1,
"totals": [
{"type": "subtotal", "amount": 1000},
{"type": "items_discount", "display_text": "Club member benefit", "amount": -150},
{"type": "total", "amount": 850}
]
}
],
"discounts": {
"applied": [
{
"title": "Club Members get 15% Bonus",
"amount": 150,
"method": "each",
"provisional": false,
"eligibility": "com.example.retail_club",
"allocations": [
{"path": "$.line_items[0]", "amount": 150}
]
},
{
"title": "Free shipping for Retail Card holder",
"amount": 199,
"automatic": true,
"provisional": false,
"eligibility": "com.example.retail_card"
}
]
},
"loyalty": {
"com.example.retail_club": {
"id": "membership_1",
"display_id": "****5678",
"name": "Retail Club",
"provisional": false,
"tiers": [
{
"id": "gold",
"name": "Gold Member",
"benefits": [
{ "id": "BEN_001", "description": "Early access to sales" }
]
}
]
},
"com.example.retail_card": {
"id": "membership_2",
"display_id": "****1234",
"name": "Retail Card",
"provisional": false,
"tiers": [
{
"id": "cardholder",
"name": "Retail Card",
"benefits": [
{ "id": "BEN_002", "description": "Exclusive customer support" }
]
}
]
}
},
"totals": [
{"type": "subtotal", "display_text": "Subtotal", "amount": 1000},
{"type": "items_discount", "display_text": "Club member benefit", "amount": -150},
{"type": "discount", "display_text": "Free shipping for Retail Card holder", "amount": -199},
{"type": "fulfillment", "display_text": "Shipping", "amount": 0},
{"type": "total", "display_text": "Estimated Total", "amount": 651}
]
}
Reward Earnings Forecast¶
In addition to immediate-value benefits like member pricing/shipping, delayed-value
collectable reward benefits are another crucial element within the loyalty ecosystem.
Displaying earnings forecasts of these rewards before the buyer commits complements and
to some extent helps agents handle price objections: rewards earning becomes additional
value on top of any pricing discount. In this example, businesses provide the reward
earning forecast with a breakdown, using benefit_id to correlate the specific
membership_tier_benefit that produced the rule and giving platforms a way to explain
with full transparency into why the buyer is earning rewards and how the earning is
calculated.
{
"line_items": [
{
"id": "li_1",
"item": {
"id": "prod_1",
"title": "T-Shirt",
"price": 1000
},
"quantity": 1,
"totals": [
{"type": "subtotal", "amount": 1000},
{"type": "total", "amount": 1000}
]
}
],
"loyalty": {
"com.example.retail_club": {
"id": "membership_1",
"display_id": "****5678",
"name": "Retail Club",
"provisional": false,
"tiers": [
{
"id": "gold",
"name": "Gold",
"benefits": [
{ "id": "BEN_001", "description": "1 point per $1 on everything" },
{ "id": "BEN_002", "description": "2 extra point/dollar on footwear" }
]
}
],
"rewards": [
{
"currency": {
"name": "LoyaltyStars",
"code": "LST"
},
"earning_forecast": {
"amount": 30,
"breakdown": [
{
"id": "RULE_1",
"description": "1 point/dollar on everything",
"amount": 10,
"benefit_id": "BEN_001"
},
{
"id": "RULE_2",
"description": "2 extra point/dollar on footwear",
"amount": 20,
"benefit_id": "BEN_002"
}
]
}
}
]
}
},
"totals": [
{"type": "subtotal", "display_text": "Subtotal", "amount": 1000},
{"type": "total", "display_text": "Estimated Total", "amount": 1000}
]
}
Implementation guidelines¶
- Loyalty extension response MUST be data-minimized and MUST NOT expose raw stable member identifiers (as this would allow the platform to uniquely identify individual buyers).
- Loyalty extension response MUST only include
display_idafter verified/authenticated memberships. - Loyalty extension response MUST treat all
context.eligibilityvalues in the request as buyer claims rather than proof.