Summary
This proposal enhances dev.ucp.common.identity_linking to support a new class of identity
linkage: a buyer establishing a pre-session account relationship with a third-party Provider
(a payment instrument issuer, BNPL provider, loyalty program, rewards network, etc.) before or
at the start of a shopping journey.
Once linked, the Provider can surface shopping-phase benefits — installment pricing, reward
earning rates, loyalty discounts — that meaningfully influence purchase decisions during product
discovery and cart review. The core insight: if linking only happens at checkout, the benefit
arrives after the purchase decision has already been made (or abandoned).
This proposal introduces two additions to dev.ucp.common.identity_linking: (1) Provider
as a fourth UCP participant type alongside Buyer, Platform, and Business; and (2) offer
discovery — a protocol hook by which a linked Provider publishes the shopping-phase benefits
it can surface for a buyer.
Motivation
The existing dev.ucp.common.identity_linking capability covers session-scoped identity linking
between a Buyer and a Business — the Platform establishing who the Buyer is within an active
commerce session. This is well-suited for the checkout phase.
But many Providers have valuable signal and benefits that are only useful before the purchase
decision is made:
| Context |
Benefit |
Why checkout is too late |
| BNPL provider |
"As low as $25/mo" on product listings; pre-approved purchasing power |
Buyer evaluates affordability during browsing, not after selecting "pay with BNPL" |
| Airline card |
"Earn 3x miles on this United flight" vs. "1x on American" |
Buyer chooses between flights during search, not at payment |
| Cash-back card |
"2% back at this merchant; 1% at others" |
Buyer decides where to shop during discovery, not at checkout |
| Store card |
"10% off with your Macy's card — net price matches Nordstrom" |
Buyer compares prices during product discovery |
In each case, the Platform needs the linked account credential before the shopping journey
begins to surface the Provider's benefit at the moment it can actually influence the Buyer's
decision.
Goals
- Define Provider as a formal fourth UCP participant type with a clear, stable definition
- Introduce offer discovery as a standardized protocol hook for Providers to declare
shopping-phase benefit endpoints, enabling Platforms to integrate uniformly across Providers
without per-Provider custom logic
- Enable pre-session account linking via a
providers array in the platform's
dev.ucp.common.identity_linking capability config
- Remain additive — no breaking changes to existing
dev.ucp.common.identity_linking behavior
Non-Goals
- Defining any specific Provider's response schema (each Provider extends the
offer_discovery
hook with their own schema — e.g., BNPL installment pricing, airline miles rates)
- Payment tokenization or checkout credential issuance (those are Provider-specific extensions)
- Mandating that Platforms support any specific Provider type
- Defining the out-of-band Provider registration process (how a Platform obtains its
client_id)
Detailed Design
New UCP Participant: Provider
A Provider is an entity with which a Buyer holds a pre-existing account relationship, whose
participation in a commerce flow can deliver measurable benefit to the Buyer — through
preferential pricing, credit, rewards, or loyalty value — and whose ability to deliver that
benefit depends on being identified earlier in the shopping journey than checkout.
A Provider is distinct from a Business: a Business is relevant to one transaction at one
merchant, while a Provider's relationship is with the Buyer across all merchants.
A Provider is also distinct from a Platform: a Platform acts as the Buyer's agent, executing
the Buyer's intent; a Provider holds an account relationship with the Buyer that the Buyer
brings into the shopping session.
API / Schema Changes
Discovery profile (static configuration)
The enhancement adds a providers array to the existing dev.ucp.common.identity_linking
capability configuration in the platform's UCP discovery profile. Each entry is a
provider_linking_config — one per Provider the platform has registered with:
{
"provider_id": "com.affirm",
"provider_type": "bnpl",
"oauth_config": {
"authorization_endpoint": "https://auth.affirm.com/oauth2/authorize",
"token_endpoint": "https://auth.affirm.com/oauth2/token",
"scopes": ["bnpl:scopes:account_linking"],
"client_id": "plat_abc123",
"code_challenge_methods_supported": ["S256"],
"revocation_endpoint": "https://auth.affirm.com/oauth2/revoke"
},
"offer_discovery": {
"endpoint": "https://api.affirm.com/api/v2/ucp/installment-pricing",
"supported_contexts": ["product_discovery", "cart"]
}
}
Key fields:
oauth_config — the OAuth 2.0 endpoints and scopes the Platform uses to initiate the
linking flow. PKCE S256 is required (RFC 7636).
offer_discovery — declares the Provider's benefit endpoint and which UCP shopping
contexts it applies to (product_discovery, cart, checkout).
provider_type — informational classification (bnpl, payment_instrument,
loyalty_program, store_card, rewards_network); not used for protocol routing.
Runtime credential
After the buyer completes the OAuth flow, the Provider issues an account_credential — a
standard RFC 6749 Bearer token response. This is not part of the discovery profile; it is
produced at runtime and stored by the Platform, keyed by provider_id:
{
"access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"scope": "bnpl:scopes:account_linking",
"expires_in": 900,
"refresh_token": "rt_xyz...",
"constraints": {
"business_allowlist": [],
"subject": "usr_7f3a9b2c...",
"issued_at": "2026-04-24T10:00:00Z"
}
}
The Platform presents this credential when calling the Provider's offer_discovery endpoint
and, where applicable, payment or underwriting flows. Notable:
constraints.business_allowlist — Provider-set restriction to specific merchants;
absent or empty means valid at all merchants.
refresh_token — Providers SHOULD issue one; a buyer who links at the start of a session
may reach checkout with an expired access token.
Full schema: to be added in accompanying PR.
Behavioral Changes
No existing behavior changes. The providers array is additive to the existing
dev.ucp.common.identity_linking config. Platforms that do not declare any providers behave
identically to today.
Risks and Mitigations
Security — SSRF via offer_discovery endpoint
Platforms register Provider endpoints in their UCP profile. Providers MUST validate incoming
requests against known platform registrations to prevent injection of malicious endpoints.
Mitigation: offer_discovery endpoints are Provider-declared (not Platform-declared), so the
Provider controls what URL is invoked.
Security — authorization code interception
Mitigation: PKCE S256 is required for all authorization requests; the plain method is
explicitly not permitted. Providers MUST enforce PKCE server-side.
Security — credential replay / scope escalation
Mitigation: account_credential carries a constraints object (including optional
business_allowlist) that Providers enforce at the API level. Platforms MUST NOT present
credentials outside their declared constraints.
Backward compatibility
This is a purely additive change — the providers array is new and optional. No existing
dev.ucp.common.identity_linking fields or behaviors are modified. Existing platforms and
businesses are unaffected.
Complexity — Provider participant introduces a new integration surface
Mitigation: The protocol standardizes only the hook (account linking + offer discovery
invocation). The Provider's response format is entirely their own to define. Platforms
integrating a new Provider only need to handle the provider_linking_config structure — the
rest is Provider-specific.
Test Plan
Unit tests
provider_linking_config schema validation (required fields, PKCE constraint enforcement,
URI formats)
account_credential constraint parsing (business_allowlist empty vs. absent vs. populated)
offer_discovery_config context enum validation
Integration tests
- Full OAuth Authorization Code + PKCE S256 flow from Platform to Provider
account_credential presented to offer_discovery endpoint; valid vs. expired vs.
wrong-business responses
- Token refresh (silent re-auth when access token expires mid-session)
- Token revocation on buyer-initiated unlink
End-to-end tests
- Buyer links Provider at session start; Platform queries offer_discovery in
product_discovery context; benefit surfaced in product listing
- Buyer links Provider; credential has non-empty
business_allowlist; Platform correctly
withholds credential at unlisted merchant
- Buyer unlinks Provider; Platform calls revocation endpoint; subsequent offer_discovery
calls rejected
Graduation Criteria
Working Draft → Candidate:
Candidate → Stable:
Implementation History
- 2026-04-24: Proposal submitted.
- [YYYY-MM-DD]: TC approved "Provisional"; capability enters "Working Draft".
- [YYYY-MM-DD]: TC approved advancement to "Candidate".
- [YYYY-MM-DD]: TC approved "Implemented"; capability enters "Stable".
Code of Conduct
Summary
This proposal enhances
dev.ucp.common.identity_linkingto support a new class of identitylinkage: a buyer establishing a pre-session account relationship with a third-party Provider
(a payment instrument issuer, BNPL provider, loyalty program, rewards network, etc.) before or
at the start of a shopping journey.
Once linked, the Provider can surface shopping-phase benefits — installment pricing, reward
earning rates, loyalty discounts — that meaningfully influence purchase decisions during product
discovery and cart review. The core insight: if linking only happens at checkout, the benefit
arrives after the purchase decision has already been made (or abandoned).
This proposal introduces two additions to
dev.ucp.common.identity_linking: (1) Provideras a fourth UCP participant type alongside Buyer, Platform, and Business; and (2) offer
discovery — a protocol hook by which a linked Provider publishes the shopping-phase benefits
it can surface for a buyer.
Motivation
The existing
dev.ucp.common.identity_linkingcapability covers session-scoped identity linkingbetween a Buyer and a Business — the Platform establishing who the Buyer is within an active
commerce session. This is well-suited for the checkout phase.
But many Providers have valuable signal and benefits that are only useful before the purchase
decision is made:
In each case, the Platform needs the linked account credential before the shopping journey
begins to surface the Provider's benefit at the moment it can actually influence the Buyer's
decision.
Goals
shopping-phase benefit endpoints, enabling Platforms to integrate uniformly across Providers
without per-Provider custom logic
providersarray in the platform'sdev.ucp.common.identity_linkingcapability configdev.ucp.common.identity_linkingbehaviorNon-Goals
offer_discoveryhook with their own schema — e.g., BNPL installment pricing, airline miles rates)
client_id)Detailed Design
New UCP Participant: Provider
A Provider is an entity with which a Buyer holds a pre-existing account relationship, whose
participation in a commerce flow can deliver measurable benefit to the Buyer — through
preferential pricing, credit, rewards, or loyalty value — and whose ability to deliver that
benefit depends on being identified earlier in the shopping journey than checkout.
A Provider is distinct from a Business: a Business is relevant to one transaction at one
merchant, while a Provider's relationship is with the Buyer across all merchants.
A Provider is also distinct from a Platform: a Platform acts as the Buyer's agent, executing
the Buyer's intent; a Provider holds an account relationship with the Buyer that the Buyer
brings into the shopping session.
API / Schema Changes
Discovery profile (static configuration)
The enhancement adds a
providersarray to the existingdev.ucp.common.identity_linkingcapability configuration in the platform's UCP discovery profile. Each entry is a
provider_linking_config— one per Provider the platform has registered with:{ "provider_id": "com.affirm", "provider_type": "bnpl", "oauth_config": { "authorization_endpoint": "https://auth.affirm.com/oauth2/authorize", "token_endpoint": "https://auth.affirm.com/oauth2/token", "scopes": ["bnpl:scopes:account_linking"], "client_id": "plat_abc123", "code_challenge_methods_supported": ["S256"], "revocation_endpoint": "https://auth.affirm.com/oauth2/revoke" }, "offer_discovery": { "endpoint": "https://api.affirm.com/api/v2/ucp/installment-pricing", "supported_contexts": ["product_discovery", "cart"] } }Key fields:
oauth_config— the OAuth 2.0 endpoints and scopes the Platform uses to initiate thelinking flow. PKCE S256 is required (RFC 7636).
offer_discovery— declares the Provider's benefit endpoint and which UCP shoppingcontexts it applies to (
product_discovery,cart,checkout).provider_type— informational classification (bnpl,payment_instrument,loyalty_program,store_card,rewards_network); not used for protocol routing.Runtime credential
After the buyer completes the OAuth flow, the Provider issues an
account_credential— astandard RFC 6749 Bearer token response. This is not part of the discovery profile; it is
produced at runtime and stored by the Platform, keyed by
provider_id:{ "access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "scope": "bnpl:scopes:account_linking", "expires_in": 900, "refresh_token": "rt_xyz...", "constraints": { "business_allowlist": [], "subject": "usr_7f3a9b2c...", "issued_at": "2026-04-24T10:00:00Z" } }The Platform presents this credential when calling the Provider's
offer_discoveryendpointand, where applicable, payment or underwriting flows. Notable:
constraints.business_allowlist— Provider-set restriction to specific merchants;absent or empty means valid at all merchants.
refresh_token— Providers SHOULD issue one; a buyer who links at the start of a sessionmay reach checkout with an expired access token.
Full schema: to be added in accompanying PR.
Behavioral Changes
No existing behavior changes. The
providersarray is additive to the existingdev.ucp.common.identity_linkingconfig. Platforms that do not declare anyprovidersbehaveidentically to today.
Risks and Mitigations
Security — SSRF via offer_discovery endpoint
Platforms register Provider endpoints in their UCP profile. Providers MUST validate incoming
requests against known platform registrations to prevent injection of malicious endpoints.
Mitigation:
offer_discoveryendpoints are Provider-declared (not Platform-declared), so theProvider controls what URL is invoked.
Security — authorization code interception
Mitigation: PKCE S256 is required for all authorization requests; the plain method is
explicitly not permitted. Providers MUST enforce PKCE server-side.
Security — credential replay / scope escalation
Mitigation:
account_credentialcarries aconstraintsobject (including optionalbusiness_allowlist) that Providers enforce at the API level. Platforms MUST NOT presentcredentials outside their declared constraints.
Backward compatibility
This is a purely additive change — the
providersarray is new and optional. No existingdev.ucp.common.identity_linkingfields or behaviors are modified. Existing platforms andbusinesses are unaffected.
Complexity — Provider participant introduces a new integration surface
Mitigation: The protocol standardizes only the hook (account linking + offer discovery
invocation). The Provider's response format is entirely their own to define. Platforms
integrating a new Provider only need to handle the
provider_linking_configstructure — therest is Provider-specific.
Test Plan
Unit tests
provider_linking_configschema validation (required fields, PKCE constraint enforcement,URI formats)
account_credentialconstraint parsing (business_allowlistempty vs. absent vs. populated)offer_discovery_configcontext enum validationIntegration tests
account_credentialpresented to offer_discovery endpoint; valid vs. expired vs.wrong-business responses
End-to-end tests
product_discoverycontext; benefit surfaced in product listingbusiness_allowlist; Platform correctlywithholds credential at unlisted merchant
calls rejected
Graduation Criteria
Working Draft → Candidate:
docs/specification/identity-linking.md)Candidate → Stable:
Implementation History
Code of Conduct