UCP Implementation
This page covers the build details of Firmly’s UCP wrapper — service architecture, how UCP operations map to Firmly APIs, session state management, and the well-known manifest that Google uses for discovery.
Service architecture
The UCP wrapper is a Firmly edge service that sits between Google and Firmly’s existing APIs. It translates UCP protocol requests into Firmly cart and checkout calls.
| Component | Purpose |
|---|---|
| Wrapper service | Request routing, signature verification, API translation |
| Session store | Session state mapping (UCP session ↔ Firmly cart), idempotency cache |
| Configuration | Firmly API credentials, signing keys |
Running at the edge gives low latency globally and DDoS protection, with the session store holding checkout session state.
API mapping
The wrapper translates each UCP operation to one or more Firmly API calls. All use V2 except order completion, which uses V1:
| UCP Operation | Firmly API Call |
|---|---|
| Create session | POST /api/v2/domains/{domain}/cart/line-items?flush_cart=false |
| Get session | GET /api/v2/domains/{domain}/cart |
| Update shipping address | POST /api/v2/domains/{domain}/cart/shipping-info |
| Update shipping method | POST /api/v2/domains/{domain}/cart/shipment/methods |
| Apply / replace discount codes | POST /api/v2/domains/{domain}/cart/promo-codes |
| Clear discount codes | DELETE /api/v2/domains/{domain}/cart/promo-codes |
| Complete order (card / direct) | POST /api/v1/domains/{domain}/cart/complete-order |
| Complete order (Google Pay) | POST /api/v1/domains/{domain}/express/google-pay/complete-order |
| Cancel session | No carts-service call — the session status is flipped to canceled in the session store |
Key details:
flush_cart=false— line items are added without clearing existing cart contents- Shipping method — requires a
shipment_idfrom the cart response to select a method - Discount codes —
discounts.codesuses replacement semantics; the promo-codes endpoint is additive, so removals are handled as a clear-and-re-add - Cancel — cancellation only flips the checkout session’s status; it does not clear the backing cart
- Device ID = Session ID — the UCP session ID is used as the Firmly device ID
Session ID format
The UCP session ID is a UUID generated by Firmly’s UCP service. The same value identifies both the UCP checkout session and the underlying Firmly cart / device session, so the wrapper resolves the session against Firmly’s cart layer without any additional mapping step.
9cf76530-1344-420f-9ca6-c7a96fb6db45
UCP clients pass this value back in the path of subsequent calls (UpdateCheckoutSession, CompleteCheckoutSession) to refer to the same session.
Session state
Each checkout session is held in the session store (the ucp_sessions KV namespace) under a session:{sessionId} key, and expires after 7 days:
| Key pattern | Value |
|---|---|
session:{sessionId} |
{ checkout_id, cart_id, domain, status, created_at, updated_at, expires_at, pending_shipping_info, submitted_shipping_info, selected_fulfillment_option_id } |
The session stores both the Firmly cart mapping and transient data that UCP needs but Firmly doesn’t store natively — pending shipping info (for buffering partial address entry), the submitted shipping info, and the selected fulfillment option. The buyer’s email is folded into the pending shipping info; the payment instrument is supplied on the complete call and is not held in session state.
Idempotency handling
Idempotency responses are stored separately from session metadata, in the same ucp_sessions KV namespace but under standalone keys of the form idempotency:{operation}:{key} — where operation is create, update, complete, or cancel (and the cart-resource equivalents cart_create, cart_update, cart_cancel). Each entry stores { response, fingerprint }, where the fingerprint is derived from the request body. When a request arrives with an Idempotency-Key header:
- Look up
idempotency:{operation}:{key} - If found and the fingerprint matches, return the cached response immediately
- If found but the fingerprint differs, reject with
409(see Idempotency) - If not found, process the request and store the response
Idempotency entries are honored for 24 hours (see Idempotency for the canonical behavior). This prevents duplicate orders from network retries or agent errors.
Internal service communication
The UCP wrapper reaches Firmly’s cart and checkout services over Firmly’s internal network — traffic that never traverses the public internet, so no external integrator interacts with it. On each translated call the wrapper generates a device_id via crypto.randomUUID() (which doubles as the UCP session_id) and forwards it alongside the App ID.
Well-known manifest
Google discovers Firmly’s UCP capabilities through a manifest served from the merchant’s own domain at /.well-known/ucp. The merchant forwards that path to Firmly, and api.firmly.work is the origin that generates the manifest (see UCP Configuration for how forwarding is set up):
GET https://<merchant-domain>/.well-known/ucpAccept: application/json
The manifest tells Google what checkout capabilities are available, which endpoints to call, how to process payments, and how to verify response signatures. The example below is the current default (2026-04-08) manifest, redacted:
{"ucp": {"version": "2026-04-08","supported_versions": {"2026-01-11": "https://staging.luma.gift/.well-known/ucp/2026-01-11","2026-01-23": "https://staging.luma.gift/.well-known/ucp/2026-01-23","2026-04-08": "https://staging.luma.gift/.well-known/ucp/2026-04-08"},"services": {"dev.ucp.shopping": [{"version": "2026-04-08","spec": "https://ucp.dev/2026-04-08/specification/overview","transport": "rest","schema": "https://ucp.dev/2026-04-08/services/shopping/rest.openapi.json","endpoint": "https://api.firmly.work/api/2026-04-08/ucp/rest/domain/staging.luma.gift"},{"version": "2026-04-08","spec": "https://ucp.dev/2026-04-08/specification/overview","transport": "mcp","schema": "https://ucp.dev/2026-04-08/services/shopping/mcp.openrpc.json","endpoint": "https://api.firmly.work/api/2026-04-08/ucp/mcp/domain/staging.luma.gift"}]},"capabilities": {"dev.ucp.shopping.cart": [{ "version": "2026-04-08", "spec": "https://ucp.dev/2026-04-08/specification/cart", "schema": "https://ucp.dev/2026-04-08/schemas/shopping/cart.json" }],"dev.ucp.shopping.checkout": [{ "version": "2026-04-08", "spec": "https://ucp.dev/2026-04-08/specification/checkout", "schema": "https://ucp.dev/2026-04-08/schemas/shopping/checkout.json" }],"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" }],"dev.ucp.shopping.discount": [{ "version": "2026-04-08", "spec": "https://ucp.dev/2026-04-08/specification/discount", "schema": "https://ucp.dev/2026-04-08/schemas/shopping/discount.json", "extends": ["dev.ucp.shopping.cart", "dev.ucp.shopping.checkout"] }],"dev.ucp.shopping.catalog.search": [{ "version": "2026-04-08", "spec": "https://ucp.dev/2026-04-08/specification/catalog/search", "schema": "https://ucp.dev/2026-04-08/schemas/shopping/catalog_search.json" }],"dev.ucp.shopping.catalog.lookup": [{ "version": "2026-04-08", "spec": "https://ucp.dev/2026-04-08/specification/catalog/lookup", "schema": "https://ucp.dev/2026-04-08/schemas/shopping/catalog_lookup.json" }]},"payment_handlers": {"com.google.pay": [{"id": "gpay","version": "2026-01-23","spec": "https://pay.google.com/gp/p/ucp/2026-01-23/","config_schema": "https://pay.google.com/gp/p/ucp/2026-01-23/schemas/config.json","instrument_schemas": ["https://pay.google.com/gp/p/ucp/2026-01-23/schemas/card_payment_instrument.json"],"config": { "gateway": "<psp-gateway>", "gatewayMerchantId": "<merchant-id>" }}]}},"signing_keys": [{"kid": "ucp-firmly-key-1","kty": "EC","crv": "P-256","x": "<base64url_x>","y": "<base64url_y>","use": "sig","alg": "ES256"}]}
The signing_keys array supports key rotation — add the new public key, start signing with it, then remove the old key after a transition period. signing_keys is a top-level sibling of ucp; payment_handlers is nested inside ucp. The Firmly Card handler (a proposal) also appears under payment_handlers (keyed by ai.firmly.card) for merchants where it is enabled.
Related
- UCP overview — what UCP is and why Firmly implements it
- UCP checkout flow — the three-step session walkthrough
- UCP security — request authentication and abuse prevention