How Universal Commerce Protocol Achieves Dynamic Discovery via /.well-known/ucp
Universal Commerce Protocol enables permissionless platform onboarding by publishing a JSON discovery document at https://<business-domain>/.well-known/ucp, allowing any platform to dynamically discover services, capabilities, and security credentials without pre-configuration.
The Universal Commerce Protocol (UCP) eliminates manual integration setup by mandating that every business host a machine-readable discovery profile at a standardized well-known URL. According to the Universal-Commerce-Protocol/ucp source code, this design allows platforms to fetch service endpoints, negotiate protocol versions, and retrieve public signing keys through a single HTTP request.
The Discovery Document Structure
Every UCP-compliant business must serve a business discovery profile at /.well-known/ucp that conforms to the JSON-Schema defined in source/discovery/profile_schema.json.
Required Fields and Schema
The document structure follows the top-level definitions in source/schemas/ucp.json and contains these critical sections:
- ucp.version – The protocol version supported by the business
- services – Advertised services (REST, MCP, A2A, Embedded) with their endpoint URLs and transport types
- capabilities – Names, versions, and optional extensions available for negotiation
- payment_handlers – Optional registry of supported payment handlers
- signing_keys – JWK public keys used for HTTP-Message-Signature verification and key discovery
The Dynamic Discovery Flow
When initiating UCP operations, platforms must fetch and process the business profile following the sequence documented in docs/specification/overview.md.
Fetch and Validation Steps
- Fetch the document via
GET https://<business-domain>/.well-known/ucpusing HTTPS only (redirects forbidden) - Validate the response against
profile_schema.jsonto ensure schema compliance - Cache the document respecting the
Cache-Control: public, max-age>=60header (the spec mandates a minimum 60-second TTL) - Extract endpoint URLs from
services[<service-id>][transport].endpointfor subsequent API calls - Compute capability intersection between the platform's advertised features and the business's
capabilitieslist using the Intersection Algorithm - Compose request/response schemas by fetching base schemas and any active extensions found in the negotiated intersection
Versioning and Extension Mechanisms
The discovery mechanism supports graceful protocol evolution through version-specific profiles and capability extensions.
Version-Specific Profiles
Businesses may expose multiple dated profiles at paths like /.well-known/ucp/2026-01-11 and advertise them in the supported_versions map. This allows platforms to request exactly the version they understand, ensuring backward compatibility while enabling protocol upgrades.
Extension-Driven Evolution
New capabilities are added via the extends field within the capabilities object. Platforms only load extensions whose parent capabilities appear in the negotiated intersection, preventing breaking changes while supporting feature expansion.
Security and Caching Requirements
The specification enforces strict security controls for the discovery process:
- HTTPS-only transport with no redirects permitted
- Mandatory caching directives requiring
Cache-Control: publicwith a minimum max-age of 60 seconds - In-band key discovery via the
signing_keysarray, enabling platforms to verify HTTP-Message-Signatures using the same profile - Graceful error handling with defined error codes
profile_unreachableandprofile_malformedwhen discovery fails
Implementation Example
Below is a complete Python implementation demonstrating profile fetching, validation, and endpoint extraction:
import requests
import json
import jsonschema
# URL of the discovery document (must be HTTPS)
DISCOVERY_URL = "https://example-business.com/.well-known/ucp"
# 1. Fetch the profile
resp = requests.get(DISCOVERY_URL, timeout=5)
resp.raise_for_status() # 2xx → OK
profile = resp.json()
# 3. Validate against the JSON-Schema (downloaded once, cached)
schema_url = "https://raw.githubusercontent.com/Universal-Commerce-Protocol/ucp/main/source/discovery/profile_schema.json"
schema = requests.get(schema_url).json()
jsonschema.validate(instance=profile, schema=schema)
# 4. Extract endpoint for the Shopping REST service
shopping = profile["ucp"]["services"]["dev.ucp.shopping"]
rest_entry = next(s for s in shopping if s["transport"] == "rest")
api_base = rest_entry["endpoint"] # e.g. https://biz.example.com/api/v2
# 5. Use the endpoint to call a Checkout operation
checkout_url = f"{api_base}/checkout-sessions"
checkout_resp = requests.post(
checkout_url,
json={"line_items": [{"price_id": "sku_123", "quantity": 1}]},
headers={"UCP-Agent": f'profile="{DISCOVERY_URL}"'}
)
checkout_resp.raise_for_status()
print(json.dumps(checkout_resp.json(), indent=2))
Sample Discovery Document
The following JSON illustrates the structure defined in source/schemas/ucp.json and source/discovery/profile_schema.json:
{
"ucp": {
"version": "2026-01-23",
"services": {
"dev.ucp.shopping": [
{
"version": "2026-01-23",
"transport": "rest",
"endpoint": "https://biz.example.com/api/v2",
"schema": "https://ucp.dev/2026-01-23/services/shopping/rest.openapi.json"
},
{
"version": "2026-01-23",
"transport": "mcp",
"endpoint": "https://biz.example.com/mcp",
"schema": "https://ucp.dev/2026-01-23/services/shopping/mcp.openrpc.json"
}
]
},
"capabilities": {
"dev.ucp.shopping.checkout": [{ "version": "2026-01-23" }],
"dev.ucp.shopping.fulfillment": [{ "version": "2026-01-23", "extends": "dev.ucp.shopping.checkout" }]
},
"signing_keys": [{ "kid": "biz2025", "kty": "EC", "crv": "P-256", "...": "..." }]
}
}
Summary
- Dynamic discovery occurs via
GET /.well-known/ucp, returning a JSON document defined bysource/discovery/profile_schema.json - No pre-configuration is required—platforms learn service endpoints, protocol versions, and signing keys directly from the business domain
- Capability negotiation uses the Intersection Algorithm to determine compatible features between platform and business
- Mandatory caching requires
Cache-Control: public, max-age>=60to reduce load while maintaining freshness - Security combines HTTPS-only transport with in-band key discovery via the
signing_keysarray for HTTP-Message-Signature verification
Frequently Asked Questions
What is the exact URL path for UCP discovery?
The discovery document must be hosted at /.well-known/ucp on the business's primary domain, accessed via https://<business-domain>/.well-known/ucp. The specification mandates HTTPS-only transport with no redirects permitted, as documented in docs/specification/overview.md and demonstrated in docs/specification/playground.md.
How does UCP handle protocol version negotiation?
Businesses advertise supported versions in the supported_versions map and may host version-specific profiles at dated paths like /.well-known/ucp/2026-01-11. Platforms compute the intersection of their supported versions with the business's advertised versions, then fetch the appropriate schema definitions from the endpoints specified in the discovery document.
What security mechanisms protect the discovery process?
UCP enforces HTTPS-only transport with strict Cache-Control requirements (public, minimum 60-second TTL). The signing_keys array within the discovery profile provides JWK public keys for HTTP-Message-Signature verification, enabling platforms to authenticate subsequent API calls using credentials discovered in-band. The specification also defines error codes profile_unreachable and profile_malformed for handling discovery failures gracefully.
How long should platforms cache the discovery document?
The specification mandates a minimum cache duration of 60 seconds via the Cache-Control: public, max-age>=60 header. Platforms should respect the actual max-age value provided by the server while maintaining a local cache to avoid repeated requests during high-frequency operations, as detailed in docs/specification/overview.md.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →