Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions protocol/schemas/common/identity_linking.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/schemas/common/identity_linking.json",
"name": "dev.ucp.common.identity_linking",
"title": "Identity Linking",
"description": "Capability schema for identity linking. Businesses declare the user-authenticated scopes they offer in a flat 'scopes' map. Each key is the OAuth scope string as it appears on the wire ('{capability}:{scope}', e.g. 'dev.ucp.shopping.order:read'). Scope presence implies that the corresponding operations require user authentication. Operations not gated by any listed scope operate at whatever access level the business permits; UCP does not prescribe a default.",
"$comment": "Auth mechanism: business-hosted OAuth 2.0 with RFC 8414 discovery. The 'config' object is open for non-breaking extension. Reserved extension point: 'providers' (map of trusted identity providers keyed by reverse-domain, with a 'type' discriminator defaulting to 'oauth2' — enabling delegated IdP, identity chaining, and future non-OAuth mechanisms such as wallet attestation). When 'providers' is absent, platforms MUST use OAuth 2.0 with RFC 8414 discovery on the business domain. Platforms MUST ignore unrecognized fields in 'config' and fall back to this default behavior.",

"$defs": {
"scope_policy": {
"type": "object",
"title": "Scope Policy",
"description": "Per-scope policy and metadata — auth constraints (e.g. min_acr, max_token_age), declarative metadata (e.g. claims produced, consent descriptions), or any other scope-specific configuration. An empty object means user authentication is required with no additional policy. Open for non-breaking extension.",
"properties": {
"description": {
"$ref": "types/description.json",
"description": "Optional human-readable description of the scope that platforms can use to present and explain context (requirement and value) to the user."
}
},
"additionalProperties": true
},

"scope_token": {
"type": "string",
"description": "OAuth scope string formed by joining a capability name and a scope name with a colon: '{capability}:{scope}', e.g. 'dev.ucp.shopping.order:read'. Capability names use reverse-DNS naming; scope names denote the permission granted, defined by each capability's spec (e.g. 'read', 'manage', 'create'). Platforms request these strings verbatim in OAuth 'scope' parameters; issued tokens carry them in the 'scope' claim.",
"pattern": "^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+:[a-z][a-z0-9_]*$"
},

"dev.ucp.common.identity_linking": {
"platform_schema": {
"title": "Identity Linking (Platform)",
"description": "Platform-level identity linking capability declaration. Platforms advertise support for identity linking; no auth-specific config is required.",
"allOf": [
{ "$ref": "../capability.json#/$defs/platform_schema" }
]
},
"business_schema": {
"title": "Identity Linking (Business)",
"description": "Business-level identity linking configuration. Businesses declare the user-authenticated scopes they offer in 'config.scopes'.",
"allOf": [
{ "$ref": "../capability.json#/$defs/business_schema" },
{
"type": "object",
"required": ["config"],
"properties": {
"config": {
"type": "object",
"$comment": "Reserved extension point: 'providers' (map of trusted identity providers keyed by reverse-domain). See schema-level $comment for details.",
"required": ["scopes"],
"properties": {
"scopes": {
"type": "object",
"description": "Map of user-authenticated scopes offered by this business. Each key is an OAuth scope string formed as '{capability}:{scope}' (e.g. 'dev.ucp.shopping.order:read'). Scope presence in this map declares that the corresponding operations require a user identity token. Operations not gated by any listed scope operate at whatever access level the business permits; UCP does not prescribe a default. Each value is a per-scope policy object (empty object means user auth required with no additional policy).",
"propertyNames": { "$ref": "#/$defs/scope_token" },
"additionalProperties": { "$ref": "#/$defs/scope_policy" }
}
},
"additionalProperties": true
}
}
}
]
}
}
}
}
272 changes: 272 additions & 0 deletions protocol/schemas/common/loyalty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/schemas/common/loyalty.json",
"name": "dev.ucp.common.loyalty",
"title": "Loyalty Extension",
"description": "Extends various Capabilities with loyalty support using memberships info.",
"$defs": {
"reward_amount": {
"type": "integer",
"minimum": 0,
"description": "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."
},
"earning_breakdown": {
"type": "object",
"description": "Breakdown rule of the reward earnings",
"required": ["id", "amount", "description"],
"properties": {
"id": {
"type": "string",
"description": "Unique rewards breakdown rule identifier."
},
"amount": {
"$ref": "#/$defs/reward_amount",
"description": "Rewards earned from this rule."
},
"description": {
"type": "string",
"description": "A display-ready, human-readable rationale for the specific rewards (e.g. 2x on footwear)."
},
"benefit_id": {
"type": "string",
"description": "Optional `id` of the membership_tier_benefit that produced this rewards rule. Resolves against `membership_tier_benefit.id` within the same parent loyalty membership."
}
}
},
"earning_forecast": {
"type": "object",
"description": "Preview of rewards to be earned from the current transaction.",
"required": ["amount"],
"properties": {
"amount": {
"$ref": "#/$defs/reward_amount",
"description": "Total rewards to be earned if the transaction completes."
},
"breakdown": {
"type": "array",
"items": {
"$ref": "#/$defs/earning_breakdown"
},
"description": "List of breakdown of earning contributing to the total."
}
}
},
"reward_currency": {
"type": "object",
"description": "The currency of the loyalty reward.",
"required": ["name", "code"],
"properties": {
"name": {
"type": "string",
"description": "Human-readable name of the currency (e.g. 'LoyaltyStars')."
},
"code": {
"type": "string",
"description": "Business-specific representation of the currency (e.g. 'LST')."
},
"decimal_places": {
"type": "integer",
"minimum": 0,
"default": 0,
"description": "The position of a digit to the right of a decimal point. Applies to all amount related fields for rewards."
}
}
},
"membership_reward": {
"type": "object",
"description": "Quantifiable reward type and optional earning forecast for the current transaction.",
"required": ["currency"],
"properties": {
"currency": {
"type": "object",
"$ref": "#/$defs/reward_currency",
"description": "A unit of value that customers can accumulate through various commercial activities."
},
"earning_forecast": {
"type": "object",
"$ref": "#/$defs/earning_forecast",
"description": "Preview of rewards to be earned from the current transaction."
}
}
},
"membership_tier_benefit": {
"type": "object",
"description": "Benefits associated with a membership tier.",
"required": ["id", "description"],
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier benefit."
},
"description": {
"type": "string",
"description": "A display-ready, human-readable explanation of this benefit (e.g. 'Early access to sales')."
}
}
},
"membership_tier": {
"type": "object",
"description": "Specific achievement rank or status milestone that unlocks escalating value as a member progresses through activity or spend.",
"required": ["id", "name"],
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the membership tier."
},
"name": {
"type": "string",
"description": "The human-readable name of the tier (e.g., 'Platinum')."
},
"benefits": {
"type": "array",
"items": {
"$ref": "#/$defs/membership_tier_benefit"
},
"description": "List of benefits associated with this tier."
}
}
},
"loyalty_membership": {
"type": "object",
"description": "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').",
"required": ["id", "name", "provisional"],
"properties": {
"id": {
"type": "string",
"description": "Unique loyalty membership identifier."
},
"name": {
"type": "string",
"description": "Business specific name of the loyalty membership/program."
},
"display_id": {
"type": "string",
"description": "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": {
"type": "array",
"items": {
"$ref": "#/$defs/membership_tier"
},
"description": "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": {
"type": "array",
"items": {
"$ref": "#/$defs/membership_reward"
},
"description": "Reward types and earning forecasts associated with this membership. Each object encapsulates one type of reward."
},
"provisional": {
"type": "boolean",
"description": "True if this membership requires additional verification."
}
}
},
"loyalty": {
"type": "object",
"description": "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.",
"propertyNames": {
"$ref": "types/reverse_domain_name.json",
"description": "Reverse-domain identifier that represents eligibility claim accepted by the Business for this membership."
},
"additionalProperties": {
"$ref": "#/$defs/loyalty_membership",
"description": "Payload that describes the membership information corresponding to the claim.",
"ucp_request": "omit"
}
},
"dev.ucp.shopping.catalog.search": {
"title": "Catalog Search with Loyalty",
"description": "Catalog Search response extended with Loyalty capability.",
"allOf": [
{
"$ref": "../shopping/catalog_search.json#/$defs/search_response"
},
{
"type": "object",
"properties": {
"loyalty": {
"$ref": "#/$defs/loyalty",
"ucp_request": "omit"
}
}
}
]
},
"dev.ucp.shopping.catalog.lookup": {
"title": "Catalog Lookup with Loyalty",
"description": "Catalog Lookup response extended with Loyalty capability.",
"oneOf": [
{
"allOf": [
{
"$ref": "../shopping/catalog_lookup.json#/$defs/lookup_response"
},
{
"type": "object",
"properties": {
"loyalty": {
"$ref": "#/$defs/loyalty",
"ucp_request": "omit"
}
}
}
]
},
{
"allOf": [
{
"$ref": "../shopping/catalog_lookup.json#/$defs/get_product_response"
},
{
"type": "object",
"properties": {
"loyalty": {
"$ref": "#/$defs/loyalty",
"ucp_request": "omit"
}
}
}
]
}
]
},
"dev.ucp.shopping.cart": {
"title": "Cart with Loyalty",
"description": "Cart extended with Loyalty capability.",
"allOf": [
{
"$ref": "../shopping/cart.json"
},
{
"type": "object",
"properties": {
"loyalty": {
"$ref": "#/$defs/loyalty",
"ucp_request": "omit"
}
}
}
]
},
"dev.ucp.shopping.checkout": {
"title": "Checkout with Loyalty",
"description": "Checkout extended with Loyalty capability.",
"allOf": [
{
"$ref": "../shopping/checkout.json"
},
{
"type": "object",
"properties": {
"loyalty": {
"$ref": "#/$defs/loyalty",
"ucp_request": "omit"
}
}
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/schemas/shopping/types/amount.json",
"$id": "https://ucp.dev/schemas/common/types/amount.json",
"title": "Amount",
"description": "Monetary amount in the currency's minor unit as defined by ISO 4217. Refer to the currency's exponent to determine minor-to-major ratio (e.g., 2 for USD, 0 for JPY, 3 for KWD).",
"type": "integer",
Expand Down
22 changes: 22 additions & 0 deletions protocol/schemas/common/types/description.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/schemas/common/types/description.json",
"title": "Description",
"description": "Description content in one or more formats. At least one format must be provided.",
"type": "object",
"properties": {
"plain": {
"type": "string",
"description": "Plain text content."
},
"html": {
"type": "string",
"description": "HTML-formatted content. Security: Platforms MUST sanitize before rendering—strip scripts, event handlers, and untrusted elements. Treat all rich text as untrusted input."
},
"markdown": {
"type": "string",
"description": "Markdown-formatted content."
}
},
"minProperties": 1
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/schemas/shopping/types/error_code.json",
"$id": "https://ucp.dev/schemas/common/types/error_code.json",
"title": "Error Code",
"description": "Error code identifying the type of error. Standard errors are defined in specification (see examples), and have standardized semantics; freeform codes are permitted.",
"description": "Error code identifying the type of error. Standard errors are defined in capability specifications (see examples) and have standardized semantics; freeform codes are permitted.",
"type": "string",
"examples": [
"not_found",
"out_of_stock",
"item_unavailable",
"address_undeliverable",
"payment_failed",
"eligibility_invalid"
"eligibility_invalid",
"identity_required",
"insufficient_scope"
]
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/schemas/shopping/types/error_response.json",
"$id": "https://ucp.dev/schemas/common/types/error_response.json",
"title": "Error Response",
"description": "Generic error response when business logic prevents resource creation or failed to retrieve resource. Used when no valid resource can be established.",
"type": "object",
Expand Down
Loading
Loading