Storeware Docs

UCP and ACP

The Universal Commerce Protocol and the Agentic Commerce Protocol, as every Storeware shop speaks them.

Every Storeware shop speaks two agent commerce protocols: the Universal Commerce Protocol (UCP) and the Agentic Commerce Protocol (ACP). Both are served from https://api.storeware.ai for every shop, both take an agent key, and both are served by the same checkout underneath, so prices, stock and the shop's rules are the same whichever one an agent speaks.

Storeware pins each protocol to one dated release and tests its answers against that release's own schemas. Read the version from the shop's discovery document rather than from this page.

UCP

Discovery

A shop's profile is at https://<shop>/.well-known/ucp. It names the protocol version, the capabilities and extensions the shop offers, the endpoint for each transport, the payment handlers, and the keys the shop signs with. The platform's own profile, which names no shop, is at https://api.storeware.ai/.well-known/ucp.

A profile may also list supported_versions: an older release the shop still answers, each with a profile of its own at /.well-known/ucp/<version>. A platform on a release the shop does not answer is refused with version_unsupported, which is HTTP 422 over REST.

Transports

TransportEndpoint
RESThttps://api.storeware.ai/api/ucp/v1
MCPhttps://api.storeware.ai/ucp/mcp
A2Ahttps://api.storeware.ai/api/ucp/a2a, named in the shop's profile beside its agent card at https://<shop>/.well-known/agent-card.json

Calling it

POST https://api.storeware.ai/api/ucp/v1/catalog/search
Authorization: Bearer swa_...
UCP-Agent: profile="https://platform.example/.well-known/ucp"
Content-Type: application/json
  • The key. An agent key, as Authorization: Bearer. The REST routes also take it as X-API-Key, and refuse a request that names two different keys.
  • Your profile. Every request names the calling platform's own profile in UCP-Agent. Over MCP it goes in meta["ucp-agent"].profile instead. Capabilities are negotiated against it, so an extension your profile does not declare is left out of the answer.
  • Signatures. A request signed with RFC 9421 is checked against the keys in your profile, and a signature that does not verify is refused. An unsigned request is accepted.
  • Retries. Send Idempotency-Key on a write, and a retry with the same key gets the first answer.

What a shop offers

CapabilityREST routes
Catalogue search and lookupPOST /catalog/search, POST /catalog/lookup, POST /catalog/product
CheckoutPOST /checkout-sessions, GET and PUT /checkout-sessions/{id}, POST /checkout-sessions/{id}/complete, POST /checkout-sessions/{id}/cancel
CartPOST /carts, GET and PUT /carts/{id}, POST /carts/{id}/cancel
OrderGET /orders/{id}, and order webhooks

Beside those, a shop's profile declares fulfilment, discounts, buyer consent, AP2 mandates and payment authentication on checkout, and two extensions of Storeware's own: ai.storeware.shopping.answer, which explains a catalogue search (see Answers), and ai.storeware.shopping.terms, for a checkout that needs the shopper to accept terms.

A cart and a checkout session are the same basket, so a cart's id is its checkout id. A cart's totals are estimates, because tax and delivery need an address. See Universal Cart.

Identity linking

A shopper can link a platform to their account at a shop with OAuth on the shop's own hostname: the metadata is at https://<shop>/.well-known/oauth-authorization-server, and the shopper consents at the shop's /account/authorize. The one scope a shop names is dev.ucp.shopping.order:read, which lets the platform read the shopper's orders. Self registration is on the same server: see How an agent signs in.

A shop's profile also declares dev.ucp.shopping.permalink: a basket as a link a person opens. https://<shop>/buy/<item>:<quantity>, with pairs joined by commas, lands the shopper on the shop with those items in their basket.

ACP

Discovery

A shop's ACP discovery document is at https://<shop>/.well-known/acp.json, and the platform's at https://api.storeware.ai/.well-known/acp.json.

Calling it

The base is https://api.storeware.ai/api/acp, with an agent key as Authorization: Bearer.

ResourceRoutes
Checkout sessionsPOST /checkout_sessions, GET and POST /checkout_sessions/{id}, POST /checkout_sessions/{id}/complete, POST /checkout_sessions/{id}/cancel
CartsPOST /carts, GET and PUT /carts/{id}, POST /carts/{id}/cancel
  • Version. Send API-Version. A version the shop does not speak is a 400 with unsupported_api_version and a supported_versions list. A request without the header is served at the current version, and every response says which version answered.
  • Retries. Idempotency-Key is honoured on every POST.
  • Money is integer minor units everywhere.
  • Errors are flat objects with type, code, message and, where it helps, param.

A cart id and a checkout session id are the same identifier. A cart that has been placed, cancelled or has expired is a 404 at /carts, while /checkout_sessions still serves it.

Paying

Storeware takes delegated payment and does not issue it. The agent gets a shared payment token from Stripe and passes it to complete, using the payment handler the session advertises. Storeware never takes a card number, so delegate_payment is not served.

Store credit on a session is shown as a total of type store_credit, and the card is charged the total less that credit. A shop that has turned on stablecoins also advertises an x402 handler: see x402.

Signing requests

Signing is set per agent key. A key with a public key registered must sign every request; a key without one carries on unsigned.

AlgorithmEd25519, or ES256 over P-256. The key is registered as an SPKI PEM.
Signed valuetimestamp + \n + method + \n + path + \n + the raw body as sent
HeadersSignature (base64url) and Timestamp (RFC 3339)
Window300 seconds either side of now

Ed25519 is the one to choose unless you also sign AP2 mandates, which need ES256. An ES256 signature is the raw r||s pair, not DER.

Order webhooks

Orders a key placed are posted to the URL registered on that key: order_create once, then order_update for every change, each carrying the whole current order. A delivery is signed Merchant-Signature: t=<unix>,v1=<hex>, over the timestamp, a dot and the raw body, with a 300 second tolerance. Revoking the key stops delivery.

On this page