Skip to content

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:

{reverse-domain}.{service}.{capability}

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:

  1. Parse the URL with a conformant (WHATWG) URL parser. It MUST parse, MUST use the https scheme, and MUST NOT contain userinfo (a user:pass@ component). Substring matching on the raw URL is NOT permitted — e.g. https://ucp.dev@evil.example/x.json has host evil.example, not ucp.dev.
  2. 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.
  3. 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 the authority_prefix (host ucp.devdev.ucp).
  4. The binding is valid if and only if either of the following holds:
  5. Exact matchname equals authority_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 handler com.example.pay served from pay.example.com (reversed host com.example.pay equals the name).
  6. Prefixedname is authority_prefix, then a ., then one or more further labels; that is, the character immediately after authority_prefix in name is a .. Requiring that separating . keeps the match on a label boundary — it stops com.example (host example.com) from matching a neighboring namespace like com.examplecorp.*, where com.example is 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:

POST https://business.example.com/api/v2/checkout-sessions

Rules:

  • endpoint MUST be a valid URL with scheme (https)
  • endpoint SHOULD NOT have a trailing slash
  • OpenAPI paths are relative and appended directly to endpoint
  • Same resolution applies to MCP endpoints for JSON-RPC calls
  • endpoint for 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_earned for checkout, loyalty_preview for cart)
  • See Intersection Algorithm for negotiation rules

Extensions can be:

  • Official: dev.ucp.shopping.fulfillment extends dev.ucp.shopping.checkout
  • Vendor: com.example.installments extends dev.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 allOf composition.
  • 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:

{
  "$defs": {
    "discounts_object": { ... },
    "dev.ucp.shopping.checkout": {
      "title": "Checkout with Discount",
      "allOf": [
        {"$ref": "checkout.json"},
        {
          "type": "object",
          "properties": {
            "discounts": {