Skip to Content
Direct UCP

Direct UCP (Path B)

Path B is for platforms building their own commerce-grade UCP client instead of using the hosted bridge. You speak UCP directly against api.artos.sh.

The fastest way to do this is the @artos-commerce/ucp-client SDK — it owns the JSON-RPC envelope, the two-credential auth model, AP2 minting + canonical JSON, payment-rail selection, and the crypto PTB rail. You only implement OAuth and host your agent profile. Everything below the SDK section documents the raw transport the SDK speaks, for non-JS clients or deep debugging.

If you only need an agent to shop (Claude, Cursor, your assistant), use Path A — it handles everything below for you. Pick Path B when you are the platform.

Start with the SDK

npm install @artos-commerce/ucp-client # Only if you support the crypto rail: npm install @mysten/sui
import { UcpClient, Ap2Signer, createCheckoutHandlers } from '@artos-commerce/ucp-client'; const client = new UcpClient({ artosBaseUrl: 'https://api.artos.sh', platformApiKey: process.env.UCP_PLATFORM_API_KEY!, platformProfileUrl: 'https://your-app.example/.well-known/ucp', }); const shop = createCheckoutHandlers({ client, signer: new Ap2Signer(JSON.parse(process.env.AGENT_PRIVATE_JWK!), process.env.AGENT_KID!), }); const results = await shop.searchProducts({ query: 'running shoes' }); const checkout = await shop.createCheckout({ storeSlug: 'energy-sport', items: [{ id: 'variant_123', quantity: 1 }] }); const outcome = await shop.confirmPurchase({ storeSlug: 'energy-sport', checkoutId: checkout.id as string, paymentMethod: 'artos.card' });

See the Path B SDK quickstart for the full flow (crypto rail, CheckoutOutcome branches, the two-credential auth model, and the complete export list).

Endpoints

SurfaceURL
Global catalog MCPhttps://api.artos.sh/mcp
Global catalog RESThttps://api.artos.sh/catalog/search
Per-store commerce MCPhttps://api.artos.sh/s/:storeSlug/mcp
Per-store RESThttps://api.artos.sh/s/:storeSlug/{carts,checkout-sessions,orders,catalog}
Discovery (per-store)https://api.artos.sh/s/:storeSlug/.well-known/ucp
Discovery (platform apex)https://api.artos.sh/.well-known/ucp
OAuth AS metadatahttps://api.artos.sh/.well-known/oauth-authorization-server
OAuth tokenhttps://api.artos.sh/ucp/oauth2/token

OAuth (still yours)

The SDK forwards and relays the buyer bearer, but it does not acquire it. Implement OAuth 2.1 + PKCE: discover the AS, redirect the buyer to consent, then exchange the code — see Authentication for the full flow and all three grants (authorization_code, refresh_token, client_credentials). Use CIMD so your client_id is your hosted metadata document URL — no manual registration.

What you must implement

With the SDK, your remaining responsibilities are small:

  • OAuth 2.1 + PKCE + buyer consent (and refresh-token rotation) to obtain the buyer bearer you pass to UcpClient.
  • Host your agent profile at a public /.well-known/ucp with the public JWK whose kid matches your Ap2Signer.
  • RFC 9421 HTTP Message Signatures where you want the elevated signed tier and to verify order webhooks.

The SDK already handles AP2 minting, byte-parity canonical JSON, merchant-terms verification, rail selection, and crypto PTB signing/submission.

Under the hood: the raw transport

You only need this section if you are not using the SDK (e.g. a non-JS client) or are debugging the wire format. The SDK produces exactly these requests for you.

Request-envelope rules (headers, body, idempotency)

These are enforced server-side and are the most common reason a hand-built call fails.

Transport preamble (headers — MCP and REST). Every UCP shopping request (the global /mcp, per-store /s/:slug/mcp, and the REST /s/:slug/... surfaces) requires two headers:

Request-Id: <unique per request, e.g. a UUID> UCP-Agent: profile="https://your-app.example/.well-known/ucp"
  • Request-Id is always required (missing_request_id otherwise) and is echoed back on the response.
  • UCP-Agent must carry a profile="..." directive (missing_ucp_agent / invalid_ucp_agent otherwise) so capability negotiation and identity binding run. The buyer-account surface (/account/mcp) needs neither header — it is bearer-scoped and first-party.

Body rules (MCP):

  1. Every MCP tools/call also repeats the profile in the body as meta["ucp-agent"].profile — a non-empty URL. Missing it errors with Tool <name> requires meta.ucp-agent.profile.
  2. State-changing tools also require meta["idempotency-key"]create_cart, update_cart, cancel_cart, create_checkout, update_checkout, cancel_checkout, and complete_checkout.
  3. Domain args are wrapped under a namespace objectcatalog, cart, or checkout — not passed flat.
  4. Line items are { "item": { "id": "…" }, "quantity": n } — not the bridge’s flat { id, quantity }.
  5. Money in catalog filters is minor units (integer cents), unlike the SDK’s and bridge’s major-unit dollars.

On REST there is no meta wrapper — the body is the bare DTO. The profile is the UCP-Agent header, and state-changing operations carry an Idempotency-Key header (the MCP body’s meta["idempotency-key"] becomes a header here):

ConcernMCP transportREST transport
Request idRequest-Id headerRequest-Id header
Agent profileUCP-Agent header and body meta["ucp-agent"].profileUCP-Agent header
Idempotencybody meta["idempotency-key"]Idempotency-Key header

Discovery

Fetch the store’s profile to learn its signing keys, enabled payment handlers, and capabilities. The platform apex (no store resolved) advertises the global catalog profile instead. (The SDK exposes this as client.fetchStoreProfile.)

curl https://api.artos.sh/s/energy-sport/.well-known/ucp

Walkthrough — MCP transport

JSON-RPC 2.0 tools/call with params: { name, arguments }.

Search the global catalog:

curl -X POST https://api.artos.sh/mcp \ -H "Content-Type: application/json" \ -H "Request-Id: $(uuidgen)" \ -H 'UCP-Agent: profile="https://your-app.example/.well-known/ucp"' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "search_catalog", "arguments": { "catalog": { "query": "running shoes", "filters": { "price": { "max": 10000 } } }, "meta": { "ucp-agent": { "profile": "https://your-app.example/.well-known/ucp" } } } } }'

A successful response is HTTP 200 with result.structuredContent carrying { ucp, products, pagination, facets }; read metadata.artos_seller.slug from each product to route the next steps.

Create a cart (per-store, state-changing):

curl -X POST https://api.artos.sh/s/energy-sport/mcp \ -H "Content-Type: application/json" \ -H "Request-Id: $(uuidgen)" \ -H 'UCP-Agent: profile="https://your-app.example/.well-known/ucp"' \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "create_cart", "arguments": { "cart": { "line_items": [{ "item": { "id": "variant_123" }, "quantity": 1 }] }, "meta": { "ucp-agent": { "profile": "https://your-app.example/.well-known/ucp" }, "idempotency-key": "cart-2f9c…" } } } }'

Create + complete the checkout. create_checkout takes { checkout: { cart_id | line_items, shipping_address, … } }. complete_checkout takes { id, checkout: { payment, ap2 } } and needs a token-tier credential with purchase:complete:

{ "jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": { "name": "complete_checkout", "arguments": { "id": "checkout_456", "checkout": { "payment": { "instruments": [{ "handler_id": "artos.card", "selected": true, "credential": { "type": "token", "token": "pm_1Pxxxx" } }] }, "ap2": { "checkout_mandate": "<base64url-header>..<base64url-signature>" } }, "meta": { "ucp-agent": { "profile": "https://your-app.example/.well-known/ucp" }, "idempotency-key": "complete-7b1a…" } } } }

Walkthrough — REST transport

The same operations map onto REST under /s/:slug/checkout-sessions. The body is the bare DTO (no MCP wrapping); the agent profile is the UCP-Agent header and state-changing calls carry an Idempotency-Key header.

# create a checkout (POST /s/:slug/checkout-sessions) curl -X POST https://api.artos.sh/s/energy-sport/checkout-sessions \ -H "Content-Type: application/json" \ -H "Request-Id: $(uuidgen)" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'UCP-Agent: profile="https://your-app.example/.well-known/ucp"' \ -H "X-API-Key: $PLATFORM_KEY" \ -d '{ "cart_id": "cart_123", "shipping_address": { "line1": "1 Market St", "city": "San Francisco", "region": "CA", "postal_code": "94105", "country": "US" } }' # complete it (card) (POST /s/:slug/checkout-sessions/:id/complete) curl -X POST https://api.artos.sh/s/energy-sport/checkout-sessions/checkout_456/complete \ -H "Content-Type: application/json" \ -H "Request-Id: $(uuidgen)" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'UCP-Agent: profile="https://your-app.example/.well-known/ucp"' \ -H "X-API-Key: $PLATFORM_KEY" \ -d '{ "payment": { "instruments": [{ "handler_id": "artos.card", "selected": true, "credential": { "type": "token", "token": "pm_1Pxxxx" } }] }, "ap2": { "checkout_mandate": "<header>..<signature>" } }'

Completing a crypto purchase

Crypto is buyer-bound — use the buyer OAuth bearer only (no X-API-Key). With the SDK this is just paymentMethod: 'artos.crypto'; the three steps below are what it does internally.

  1. Prepare the payment intent → returns an unsigned Sui PTB:
curl -X POST https://api.artos.sh/s/energy-sport/checkout-sessions/checkout_456/payment-intent \ -H "Authorization: Bearer $BUYER_TOKEN" \ -H "Content-Type: application/json" \ -H "Request-Id: $(uuidgen)" \ -H 'UCP-Agent: profile="https://your-app.example/.well-known/ucp"' \ -d '{ "buyer_address": "0xBUYER", "input_coin_type": "0x2::sui::SUI" }'
  1. Sign and submit the PTB with the buyer’s wallet; capture the tx digest.
  2. Complete with the tx digest as the instrument token and the AP2 mandate, passing ap2.intent_mandate (the user-signed spend allowance) for crypto settlement:
curl -X POST https://api.artos.sh/s/energy-sport/checkout-sessions/checkout_456/complete \ -H "Authorization: Bearer $BUYER_TOKEN" \ -H "Content-Type: application/json" \ -H "Request-Id: $(uuidgen)" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'UCP-Agent: profile="https://your-app.example/.well-known/ucp"' \ -d '{ "payment": { "instruments": [{ "handler_id": "artos.crypto", "selected": true, "credential": { "type": "token", "token": "0xTXDIGEST" } }] }, "ap2": { "checkout_mandate": "<header>..<signature>", "intent_mandate": "<base64-json-envelope>" } }'

Spec source of truth

The bundled UCP spec (schemas, services, documentation) lives in artos-api/ucp-spec . Treat it as canonical for UCP shapes; this page documents the Artos specifics on top of it.

Last updated on