Reference note
The /.well-known/ucp manifest, field by field
The real structure of the UCP discovery manifest: services, capabilities, payment_handlers, signing keys, version negotiation and error codes.
Published . Part of the UCP implementation guide.
A merchant adopting UCP publishes a JSON file at a fixed address, /.well-known/ucp.
It is the single entry point: an agent discovering a domain reads that document and
infers what it can do with that merchant. Most write-ups stop at that sentence. This
note goes one level down, into the fields the document actually contains.
Everything below follows the official specification, version 2026-04-08. UCP versions by date: the structure described here is the one in that revision, and the negotiation mechanism described further down is precisely what allows revisions to coexist.
Two top-level keys
The document exposes two keys at the root:
{
"ucp": { },
"signing_keys": [ ]
}
ucp holds all protocol metadata. signing_keys holds the public keys, in JWK
format, used to verify signatures. That second key is not decorative: the
specification defines dedicated errors signature_missing, signature_invalid and
key_not_found, all returning HTTP 401. A manifest without usable keys therefore
closes the door on any signed exchange.
Inside ucp sit four registries:
version, the protocol version, inYYYY-MM-DDform;services, the API surfaces and their transports;capabilities, the declared features;payment_handlers, how payment instruments are processed.
services: where and how to talk to the merchant
A service describes an API surface for a functional area. Each entry combines
version, spec, transport, endpoint and schema. The specification sets a
minimum rule: all transports require version, spec and transport; REST, MCP
and the embedded mode additionally require schema.
Four transports are defined:
| Transport | Nature |
|---|---|
| REST | HTTP/1.1 and above, JSON payloads, standard verbs and status codes |
| MCP | JSON-RPC over Model Context Protocol, tools/call |
| A2A | Agent-to-Agent protocol, via the Agent Card specification |
| EP | OpenRPC, host / embedded communication |
REST is the core transport; the others are optional and depend on the capabilities you target. This is what quick readings most often miss: UCP does not mandate MCP. A merchant can be fully compliant over REST alone. The distinction between the two protocols is covered on the UCP vs MCP page.
capabilities: what the merchant can do
A capability declares a supported feature. Required fields are version, spec
and schema. Optional fields are id, config and extends.
extends is the extension mechanism. A capability can inherit from a single
parent, given as a string, or from several parents, given as an array. The
specification illustrates this with a fulfillment extension that extends checkout:
{
"dev.ucp.shopping.fulfillment": [
{
"version": "2026-04-08",
"spec": "https://ucp.dev/2026-04-08/specification/fulfillment",
"schema": "https://ucp.dev/2026-04-08/schemas/shopping/fulfillment.json",
"extends": "dev.ucp.shopping.checkout"
}
]
}
Naming uses reverse namespaces, dev.ucp.shopping.checkout. A merchant or a vendor
can therefore declare its own capabilities under its own namespace without
colliding with the official registry.
payment_handlers: a specification, not a provider
This is the nuance the specification takes care to state explicitly: payment handlers are specifications, not entities. They describe how a class of instrument is processed, not which provider processes it.
An entry combines id, version, spec, schema, available_instruments and
config. The practical consequence is direct: switching PSP does not necessarily
change the manifest, as long as the processing specification stays the same. The
wider landscape of payment rails is on the
agentic payments page.
Negotiation: an intersection, not an agreement
When an agent and a merchant meet, neither imposes its version. The specification defines a four-step intersection algorithm:
- compute the intersection, keeping only capabilities present on both sides;
- select, for each retained capability, the highest mutually supported version;
- prune orphaned extensions, whose parent is not in the intersection;
- repeat the pruning until stable.
And the rule that settles empty cases, quoted verbatim: “If the set is empty (no mutual version), exclude the capability from the intersection.” A capability with no common version is not an error, it simply drops out of the exchange.
That repeated pruning loop deserves an implementation team’s attention. Removing a base capability can cascade through every extension depending on it, including ones assumed to be independent.
Error codes, and their double translation
Every error carries a stable code, translated differently per transport. This is the most operational part of the document, and the part to wire into merchant-side monitoring.
| Code | REST | MCP |
|---|---|---|
invalid_profile_url |
400 | -32001 |
profile_unreachable |
424 | -32001 |
profile_malformed |
422 | -32001 |
version_unsupported |
422 | -32001 |
capabilities_incompatible |
200 | result |
signature_missing |
401 | -32000 |
signature_invalid |
401 | -32000 |
key_not_found |
401 | -32000 |
Two rows are worth reading twice.
profile_unreachable returns HTTP 424, Failed Dependency. In other words, when
your manifest is unreachable, the failure is not reported as a client error but as
a failed dependency. Monitoring that only watches 5xx on your own domain will never
see this case.
capabilities_incompatible returns HTTP 200. Incompatibility is not a transport
error: the request succeeded, it is the negotiation that found nothing in common. A
dashboard counting HTTP status codes will file this commercial failure under
technical success. That is a blind spot to fix in monitoring
before, not after, going to production.
What to settle before implementing
The manifest is not a static configuration file you drop once. It is a versioned, signed contract, negotiated at every encounter, whose failures are partly reported outside the usual HTTP channel. Three consequences for a roadmap:
- version the manifest like code, with review, since removing a capability prunes its extensions;
- publish and rotate
signing_keys, without which the whole signed path is unusable; - instrument negotiation codes, not just HTTP status codes.
For upstream setup, Merchant Center prerequisites and the protocol route, see the implementation guide. For the standard and its partners, see the UCP reference page.
Sources
- Universal Commerce Protocol, official specification 2026-04-08
- Google Developers Blog, Under the Hood: Universal Commerce Protocol
Back to the UCP implementation guide · All notes · Lire en français