Schema Authoring Guide¶
This guide documents conventions for authoring UCP JSON schemas: metadata fields, the registry pattern, schema variants, and versioning.
Schema Metadata Fields¶
UCP schemas use standard JSON Schema fields plus UCP-specific metadata:
| Field | Standard | Purpose | Required For |
|---|---|---|---|
$schema |
JSON Schema | Declares JSON Schema draft version (SHOULD use draft/2020-12) |
All schemas |
$id |
JSON Schema | Schema's canonical URI for $ref resolution |
All schemas |
title |
JSON Schema | Human-readable display name | All schemas |
description |
JSON Schema | Schema purpose and usage | All schemas |
name |
UCP | Reverse-domain identifier; doubles as registry key | Capabilities, services, handlers |
version |
UCP | Entity version (YYYY-MM-DD format) |
Capabilities, services, payment handlers |
id |
UCP | Instance identifier for multiple configurations | Payment handlers only |
Why Self-Describing?¶
Capability schemas must be self-describing: when a platform fetches a schema, it should determine exactly what capability and version it represents without cross-referencing other documents. This matters because:
-
Independent versioning: Capabilities may version independently. The schema must declare its version explicitly—you can't infer it from the URL.
-
Validation: Validators can cross-check that a capability declaration's
schemaURL points to a schema whose embeddedname/versionmatch the declaration. Mismatches are authoring errors caught at build time. -
Developer experience: When reading a schema file, integrators immediately see what capability it defines without reverse-engineering the
$idURL. -
Compact namespace: The
namefield provides a standardized reverse-domain identifier (e.g.,dev.ucp.shopping.checkout) that's more compact and semantic than the full$idURL.
Why Both $id and name?¶
| Field | Role | Format |
|---|---|---|
$id |
JSON Schema primitive for $ref resolution and tooling |
URI (required by spec) |
name |
Registry key and stable identifier | Reverse-domain |
$id must be a valid URI per JSON Schema spec. name is the key used in
registries (capabilities, services, payment_handlers) and the wire protocol
identifier used in capability negotiation—decoupled from schema hosting so that
schema URLs can change as infrastructure evolves.
The reverse-domain format provides namespace governance: domain owners control
their namespace (dev.ucp.*, com.shopify.*), avoiding collisions between UCP
and vendor entities. This stable identity layer allows trust and resolution
mechanisms to evolve independently—future versions could adopt verifiable
credentials, content-addressed schemas, or other verification methods without
breaking capability negotiation.
Why version Uses Dates?¶
The version field uses date-based versioning (YYYY-MM-DD) to enable:
- Capability negotiation: Platforms request specific versions they support
- Breaking change management: New versions get new dates; old versions remain valid and resolvable
- Independent lifecycles: Extensions can release on their own schedule
Schema Categories¶
UCP schemas fall into six categories based on their role in the protocol.
Capability Schemas¶
Define negotiated capabilities that appear in ucp.capabilities{} registries.
- Top-level fields:
$schema,$id,title,description,name,version - Variants:
platform_schema,business_schema,response_schema
Examples: checkout.json, fulfillment.json, discount.json, order.json
Service Schemas¶
Define transport bindings that appear in ucp.services{} registries. Each transport
(REST, MCP, A2A, Embedded) is a separate entry.
- Top-level fields:
$schema,$id,title,description,name,version - Variants:
platform_schema,business_schema - Transport requirements:
- REST/MCP:
endpoint,schema(OpenAPI/OpenRPC URL) - A2A:
endpoint(Agent Card URL) - Embedded:
schema(OpenRPC URL)
- REST/MCP:
Payment Handler Schemas¶
Define payment handler configurations in ucp.payment_handlers{} registries.
- Top-level fields:
$schema,$id,title,description,name,version - Variants:
platform_schema,business_schema,response_schema - Instance
id: Required to distinguish multiple configurations of the same handler
Examples: com.google.pay, dev.shopify.shop_pay, dev.ucp.processor_tokenizer
→ See Payment Handler Guide for detailed guidance on handler structure, config/instrument/credential schemas, and the full specification template.
Component Schemas¶
Data structures embedded within capabilities but not independently negotiated. Do not appear in registries.
- Top-level fields:
$schema,$id,title,description - Omit:
name,version(not independently versioned)
Examples:
schemas/shopping/payment.json— Payment configuration (part of checkout)
Type Schemas¶
Reusable definitions referenced by other schemas. Do not appear in registries.
- Top-level fields:
$schema,$id,title,description - Omit:
name,version
Examples: types/buyer.json, types/line_item.json, types/postal_address.json
Meta Schemas¶
Define protocol structure rather than entity payloads.
- Top-level fields:
$schema,$id,title,description - Omit:
name,version
Examples: ucp.json (entity base), capability.json, service.json, payment_handler.json
The Registry Pattern¶
UCP organizes capabilities, services, and handlers in registries—objects keyed
by name rather than arrays of objects with name fields.
{
"capabilities": {
"dev.ucp.shopping.checkout": [{"version": "2026-01-11"}],
"dev.ucp.shopping.fulfillment": [{"version": "2026-01-11"}]
},
"services": {
"dev.ucp.shopping": [
{"version": "2026-01-11", "transport": "rest"},
{"version": "2026-01-11", "transport": "mcp"}
]
},
"payment_handlers": {
"com.google.pay": [{"id": "gpay_1234", "version": "2026-01-11"}]
}
}
Registry Contexts¶
The same registry structure appears in three contexts with different field requirements:
| Context | Location | Required Fields |
|---|---|---|
| Platform Profile | Advertised URI | version, spec, schema |
| Business Profile | /.well-known/ucp |
version; may add config |
| API Responses | Checkout/order payloads | version (+ id for handlers) |
The Entity Pattern¶
All capabilities, services, and handlers extend a common entity base schema:
| Field | Type | Description |
|---|---|---|
version |
string | Entity version (YYYY-MM-DD) — always required |
spec |
URI | Human-readable specification |
schema |
URI | JSON Schema URL |
id |
string | Instance identifier (handlers only) |
config |
object | Entity-specific configuration |
Schema Variants¶
Each entity type defines three variants for different contexts:
platform_schema — Full declarations for discovery
{
"dev.ucp.shopping.fulfillment": [{
"version": "2026-01-11",
"spec": "https://ucp.dev/specification/fulfillment",
"schema": "https://ucp.dev/schemas/shopping/fulfillment.json",
"config": {
"supports_multi_group": true
}
}]
}
business_schema — Business-specific overrides
{
"dev.ucp.shopping.fulfillment": [{
"version": "2026-01-11",
"config": {
"allows_multi_destination": {"shipping": true}
}
}]
}
response_schema — Minimal references in API responses
Define all three in your schema's $defs:
"$defs": {
"platform_schema": {
"allOf": [{"$ref": "../capability.json#/$defs/platform_schema"}]
},
"business_schema": {
"allOf": [{"$ref": "../capability.json#/$defs/business_schema"}]
},
"response_schema": {
"allOf": [{"$ref": "../capability.json#/$defs/response_schema"}]
}
}
Versioning Strategy¶
UCP Capabilities (dev.ucp.*)¶
UCP-authored capabilities version with protocol releases by default. Individual capabilities may version independently when needed.
Vendor Capabilities (com.{vendor}.*)¶
Capabilities outside dev.ucp.* version fully independently:
{
"name": "com.shopify.loyalty",
"version": "2025-09-01",
"spec": "https://shopify.dev/ucp/loyalty",
"schema": "https://shopify.dev/ucp/schemas/loyalty.json"
}
Vendor schemas follow the same self-describing requirements.
Complete Example: Capability Schema¶
A capability schema defines both payload structure and declaration variants:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/schemas/shopping/checkout.json",
"name": "dev.ucp.shopping.checkout",
"version": "2026-01-11",
"title": "Checkout",
"description": "Base checkout schema. Extensions compose via allOf.",
"$defs": {
"platform_schema": {
"allOf": [{"$ref": "../capability.json#/$defs/platform_schema"}]
},
"business_schema": {
"allOf": [{"$ref": "../capability.json#/$defs/business_schema"}]
},
"response_schema": {
"allOf": [{"$ref": "../capability.json#/$defs/response_schema"}]
}
},
"type": "object",
"required": ["ucp", "id", "line_items", "status", "currency", "totals", "links"],
"properties": {
"ucp": {"$ref": "../ucp.json#/$defs/response_checkout_schema"},
"id": {"type": "string", "description": "Checkout identifier"},
"line_items": {"type": "array", "items": {"$ref": "types/line_item.json"}},
"status": {"type": "string", "enum": ["open", "completed", "expired"]},
"currency": {"type": "string", "pattern": "^[A-Z]{3}$"},
"totals": {"$ref": "types/totals.json"},
"links": {"$ref": "types/links.json"}
}
}
Key points:
- Top-level
nameandversionmake the schema self-describing $defsvariants enable validation in different contexts- Payload properties define the actual checkout response structure