Universal Commerce Protocol (UCP) Official Specification¶
Overarching guidelines¶
The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Schema notes:
- Date format: Always specified as RFC 3339 unless otherwise specified
- Amounts format: Minor units (cents)
Discovery, Governance, and Negotiation¶
UCP separates protocol version selection from capability negotiation. A Business advertises its current protocol version and links to profiles for older supported versions. After the Platform selects one exact version, the Business determines the active capabilities from the versions both parties advertise. Version lifecycle, including when to remove an older version, is a Business policy decision; UCP does not prescribe a deprecation schedule. Business and Platform profiles can be cached by both parties.
Namespace Governance¶
UCP uses reverse-domain naming to encode governance authority directly into capability identifiers. This eliminates the need for a central registry.
Naming Convention¶
All capability and service names MUST use the format:
Components:
{reverse-domain}- Authority identifier derived from domain ownership{service}- Service/vertical category (e.g.,shopping,common){capability}- The specific capability name
Examples:
| Name | Authority | Service | Capability |
|---|---|---|---|
dev.ucp.shopping.checkout |
ucp.dev | shopping | checkout |
dev.ucp.shopping.fulfillment |
ucp.dev | shopping | fulfillment |
dev.ucp.common.identity_linking |
ucp.dev | common | identity_linking |
com.example.payments.installments |
example.com | payments | installments |
Authority Binding¶
Reverse-domain names serve two purposes: collision-safe identifiers (keys
and references), and entities — capabilities, services, and payment
handlers — that declare a fetched schema URL describing them. Authority
binding applies to every entity with a remote schema: a declared schema
URL's origin MUST match the namespace authority in its name.
A capability MUST declare a schema; services and payment handlers declare
one where their transport or handler defines it. Each entity MAY also
declare a spec URL (human-readable documentation).
This binding guarantees provenance, not trust: a valid binding proves only that the reverse-domain name is controlled by the party that owns the corresponding domain — an entity cannot be published under a namespace its author does not control. It does not assert that the entity is trustworthy, correct, or worth supporting. Whether to negotiate, trust, or implement it is always the client's decision; this binding only tells the client who is making the claim. Provenance is established from domain ownership and evaluated at negotiation time.
The spec URL is documentation, not part of the machine trust path, so its
origin is not authority-bound: it MUST be https but MAY be served
from any host (e.g. a docs subdomain or third-party docs host). Only the
schema URL carries the authority binding defined below.
Derivation algorithm¶
The authority is derived from the schema URL host — which names the
owning domain directly, with no ambiguity about where the domain ends — and
validated as a label-aligned prefix of, or an exact match for, the entity's
name. For the schema URL of an entity whose name is name, a platform
MUST apply the following:
- Parse the URL with a conformant (WHATWG) URL parser. It MUST parse,
MUST use the
httpsscheme, and MUST NOT contain userinfo (auser:pass@component). Substring matching on the raw URL is NOT permitted — e.g.https://ucp.dev@evil.example/x.jsonhas hostevil.example, notucp.dev. - The host MUST be a registered domain name of at least two labels.
IP-literal hosts (
https://203.0.113.10/...) and single-label hosts (https://localhost/...) are invalid authorities. - Take the URL's hostname (the host without any port), normalize it (lowercase;
strip a trailing
.; internationalized domains in A-label / punycode form), and reverse its labels to form theauthority_prefix(hostucp.dev→dev.ucp). - The binding is valid if and only if either of the following holds:
- Exact match —
nameequalsauthority_prefix. The name is itself the reversed host, so the publisher demonstrably controls the entire namespace. This is the shape for an entity whose identity is a bare controlled domain, such as a payment handlercom.example.payserved frompay.example.com(reversed hostcom.example.payequals the name). - Prefixed —
nameisauthority_prefix, then a., then one or more further labels; that is, the character immediately afterauthority_prefixinnameis a.. Requiring that separating.keeps the match on a label boundary — it stopscom.example(hostexample.com) from matching a neighboring namespace likecom.examplecorp.*, wherecom.exampleis a textual prefix but not a label-aligned one.
Authority binding establishes provenance only — that the name is controlled
by the party serving its schema. It does not require any label beyond the
authority itself. The {reverse-domain}.{service}.{capability} shape is a
separate Naming Convention that governs capability and
service names — it does not apply to payment handlers — and is validated
independently of this check.
Any labels after the authority prefix are treated as opaque by this check; they are not inspected or split.
| Entity name | schema host |
authority_prefix |
Result |
|---|---|---|---|
dev.ucp.shopping.checkout |
ucp.dev |
dev.ucp |
accept (prefix) |
dev.ucp.shopping.checkout |
shopping.ucp.dev |
dev.ucp.shopping |
accept (prefix) |
com.example.payments.installments |
example.com |
com.example |
accept (prefix) |
com.example.pay |
pay.example.com |
com.example.pay |
accept (exact) |
com.example.pay |
example.com |
com.example |
accept (prefix) |
com.example.pay |
evil.example |
example.evil |
reject |
dev.ucp.shopping.checkout |
evil.example |
example.evil |
reject |
com.examplecorp.pay |
example.com |
com.example |
reject |
com.example.pay |
cdn.example.com |
com.example.cdn |
reject |
An entity's schema is served from a host whose reversed labels either equal
its name or are a label-aligned prefix of it. A host whose reversed labels
are exactly the name (pay.example.com for com.example.pay) satisfies the
exact case; a canonical apex host (example.com for com.example.*) satisfies
the prefix case; a subdomain satisfies the prefix case only when its labels line
up with the namespace path (shopping.ucp.dev for dev.ucp.shopping.*). Because
a parent domain's reversed labels are also a prefix, a name such as
com.example.pay binds equally from its exact host (pay.example.com) or a
parent authority (example.com) — both prove control. Unrelated subdomains such
as a shared CDN do not satisfy any case — host the canonical schema on a
name-aligned origin.
The check uses the schema URL host directly and does not consult the
Public Suffix List, so it treats a public
suffix — a domain under which independent parties can register names, from
co.uk to the list's private-section suffixes operated by services that let
third parties register subdomains or buckets (github.io, object storage, app
platforms) — as an ordinary authority. Co-tenants under such a suffix satisfy
the same prefix, so declare entities only under a registrable domain (a
public suffix plus one label) that you exclusively control.
Enforcement¶
A platform MUST validate each business-declared schema URL before fetching
it. If the URL's origin does not match the entity's namespace authority (per
Derivation algorithm), the platform MUST NOT fetch
it and MUST reject the entity — treated as not present and never
activated. A spec URL MUST be a valid https URL. A platform MUST NOT follow redirects (3xx) when fetching a schema URL, consistent with profile fetches.
The platform fetches and composes business-declared schemas to validate every request and response, so validating the binding ensures each composed schema is sourced from the party that owns the entity's namespace. A business SHOULD apply the same check to the platform profile and exclude any entity whose binding fails.
Binding validates the declared hostname for provenance; it is not a
fetch-safety control and does not authorize dereferencing. Fetching the schema
URL — like any URL fetched during discovery — is additionally subject to the
protocol's URL fetch-safety requirements, which guard the resolved address
(not just the hostname) against server-side request forgery toward special-use
or cloud-metadata addresses and DNS rebinding. The hostname check and the
resolved-address check are independent, and both apply.
Governance Model¶
| Namespace Pattern | Authority | Governance |
|---|---|---|
dev.ucp.* |
ucp.dev | UCP governing body |
com.{vendor}.* |
{vendor}.com | Vendor organization |
org.{org}.* |
{org}.org | Organization |
The dev.ucp.* namespace is reserved for capabilities sanctioned by the UCP
governing body. Vendors MUST use their own reverse-domain namespace for
custom capabilities.
Services¶
A service defines the API surface for a vertical (shopping, common, etc.). Services include operations, events, and transport bindings defined via standard formats:
- REST: OpenAPI 3.x (JSON format)
- MCP: OpenRPC (JSON format)
- A2A: Agent Card Specification
- EP(embedded): OpenRPC (JSON format)
A service is identified by its reverse-domain registry key (e.g.,
dev.ucp.shopping). In a profile, services are keyed by that name, and each
entry in services[name][] pairs the service with one transport binding and
declares the service version: in release D that version is D. This is the
service version, not a transport version — the binding has no separate version.
The OpenAPI or OpenRPC artifact a binding references carries its own
info.version as release metadata, not a separate version to negotiate. See
Component Versioning and Release Snapshots.
Service Definition¶
Full service declaration for platform-level discovery. All transports require version, spec, and transport. REST, MCP, and embedded additionally require schema.
| Name | Type | Requirement | Description |
|---|---|---|---|
| version | string | Required | Entity version in YYYY-MM-DD format. |
| spec | string | Required | URL to human-readable specification document. |
| schema | string | Optional | URL to JSON Schema defining this entity's structure and payloads. |
| id | string | Optional | Unique identifier for this entity instance. Used to disambiguate when multiple instances exist. |
| config | object | Optional | Entity-specific configuration. Structure defined by each entity's schema. |
| transport | string | Required | Transport protocol for this service binding. Enum: rest, mcp, a2a, embedded |
| endpoint | string | Optional | Endpoint URL for this transport binding. |
Transport definitions MUST be thin: they declare method names and reference base schemas only. See Requirements for details.
Endpoint Resolution¶
The endpoint field provides the base URL for API calls. OpenAPI paths are
appended to this endpoint to form the complete URL.
Example:
{
"version": "2026-08-25",
"transport": "rest",
"schema": "https://ucp.dev/2026-08-25/services/shopping/rest.openapi.json",
"endpoint": "https://business.example.com/api/v2"
}
With OpenAPI path /checkout-sessions, the resolved URL is:
Rules:
endpointMUST be a valid URL with scheme (https)endpointSHOULD NOT have a trailing slash- OpenAPI paths are relative and appended directly to endpoint
- Same resolution applies to MCP endpoints for JSON-RPC calls
endpointfor A2A transport refers to the Agent Card URL for the agent
Capabilities¶
A capability is a feature within a service. It declares what functionality is supported and where to find documentation and schemas.
Capability Definition¶
Full capability declaration for platform-level discovery. Includes spec/schema URLs for agent fetching.
| Name | Type | Requirement | Description |
|---|---|---|---|
| version | string | Required | Entity version in YYYY-MM-DD format. |
| spec | string | Required | URL to human-readable specification document. |
| schema | string | Required | URL to JSON Schema defining this entity's structure and payloads. |
| id | string | Optional | Unique identifier for this entity instance. Used to disambiguate when multiple instances exist. |
| config | object | Optional | Entity-specific configuration. Structure defined by each entity's schema. |
| extends | OneOf[string, array] |
Optional | Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. |
Extensions¶
An extension is an optional module that augments another capability.
Extensions use the extends field to declare their parent(s):
{
"dev.ucp.shopping.fulfillment": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/shopping/extensions/fulfillment",
"schema": "https://ucp.dev/2026-08-25/schemas/shopping/fulfillment.json",
"extends": "dev.ucp.shopping.checkout"
}
]
}
Multi-Parent Extensions¶
Extensions MAY extend multiple parent capabilities by using an array:
{
"dev.ucp.shopping.discount": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/shopping/extensions/discount",
"schema": "https://ucp.dev/2026-08-25/schemas/shopping/discount.json",
"extends": ["dev.ucp.shopping.checkout", "dev.ucp.shopping.cart"]
}
]
}
When an extension declares multiple parents:
- The extension MAY define different fields for each capability it extends
(e.g.,
loyalty_earnedfor checkout,loyalty_previewfor cart) - See Intersection Algorithm for negotiation rules
Extensions can be:
- Official:
dev.ucp.shopping.fulfillmentextendsdev.ucp.shopping.checkout - Vendor:
com.example.installmentsextendsdev.ucp.shopping.checkout
Schema Composition¶
Extensions can add new fields and modify shared structures (e.g., discounts
modify totals, fulfillment adds fulfillment to totals.type).
Requirements¶
- Transport definitions (OpenAPI/OpenRPC) MUST reference base schemas only. They MUST NOT enumerate fields or define payload shapes inline.
- Extensions MUST be self-describing. Each extension schema MUST
declare the types it introduces and how it modifies base types using
allOfcomposition. - Platforms MUST resolve schemas client-side by fetching and composing base schemas with active extension schemas.
Extension Schema Pattern¶
Extension schemas define composed types using allOf. The $defs key MUST
use the full parent capability name (reverse-domain format) to enable
deterministic schema resolution: