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/suiimport { 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
| Surface | URL |
|---|---|
| Global catalog MCP | https://api.artos.sh/mcp |
| Global catalog REST | https://api.artos.sh/catalog/search |
| Per-store commerce MCP | https://api.artos.sh/s/:storeSlug/mcp |
| Per-store REST | https://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 metadata | https://api.artos.sh/.well-known/oauth-authorization-server |
| OAuth token | https://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/ucpwith the public JWK whosekidmatches yourAp2Signer. - RFC 9421 HTTP Message Signatures where you want the elevated
signedtier 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-Idis always required (missing_request_idotherwise) and is echoed back on the response.UCP-Agentmust carry aprofile="..."directive (missing_ucp_agent/invalid_ucp_agentotherwise) 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):
- Every MCP
tools/callalso repeats the profile in the body asmeta["ucp-agent"].profile— a non-empty URL. Missing it errors withTool <name> requires meta.ucp-agent.profile. - State-changing tools also require
meta["idempotency-key"]—create_cart,update_cart,cancel_cart,create_checkout,update_checkout,cancel_checkout, andcomplete_checkout. - Domain args are wrapped under a namespace object —
catalog,cart, orcheckout— not passed flat. - Line items are
{ "item": { "id": "…" }, "quantity": n }— not the bridge’s flat{ id, quantity }. - 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):
| Concern | MCP transport | REST transport |
|---|---|---|
| Request id | Request-Id header | Request-Id header |
| Agent profile | UCP-Agent header and body meta["ucp-agent"].profile | UCP-Agent header |
| Idempotency | body 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/ucpWalkthrough — 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.
- 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" }'- Sign and submit the PTB with the buyer’s wallet; capture the tx digest.
- 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.