Path B quickstart (the SDK)
@artos-commerce/ucp-client
is the fastest way to build your own commerce-grade UCP client. It is the
reusable core extracted from the hosted bridge, so you get the hard parts done
correctly: the JSON-RPC envelope, the two-credential auth model, byte-parity
canonical JSON, AP2 mandate signing, payment-rail selection, and the crypto PTB
rail.
If you only need an agent to shop (Claude, Cursor, your assistant), use Path A — the hosted bridge handles everything below for you. Reach for the SDK when you are the platform.
What the SDK handles for you
| Concern | Without the SDK (raw Path B) | With the SDK |
|---|---|---|
| UCP transport + envelope | Hand-build Request-Id, UCP-Agent, meta["ucp-agent"], idempotency keys | UcpClient |
| Two-credential auth | Remember platform key vs buyer bearer per call | UcpClient (auth per method) |
| 401 silent refresh | Relay 401 + WWW-Authenticate yourself | onAuthChallenge hook |
| AP2 mandate | Mint compact ES256 JWS, match canonical JSON byte-for-byte | Ap2Signer |
| Merchant terms | Re-verify merchant_authorization before paying | done inside confirmPurchase |
Card / crypto / $0 rails | Branch + sign + submit the Sui PTB | confirmPurchase + resolveCryptoDeps |
| Money units | Convert minor units everywhere | integers in, toMinorUnits at the edge |
Still yours: OAuth 2.1 + PKCE (acquiring and refreshing the buyer bearer) and
hosting your agent profile at /.well-known/ucp. See
Authentication and Profiles & trust.
Install
npm install @artos-commerce/ucp-client
# Only if you support the crypto settlement rail:
npm install @mysten/suiRequires Node 20+ (for global fetch and base64url). @mysten/sui is an
optional peer dependency — card-only integrations skip it. This is a
server-side package: never ship the buyer bearer to a browser.
Bootstrap
import {
UcpClient,
Ap2Signer,
createCheckoutHandlers,
resolveCryptoDeps,
} from '@artos-commerce/ucp-client';
const client = new UcpClient(
{
artosBaseUrl: 'https://api.artos.sh',
platformApiKey: process.env.UCP_PLATFORM_API_KEY!, // "<clientId>.<secret>"
platformProfileUrl: 'https://your-app.example/.well-known/ucp',
},
{
// The current buyer's OAuth bearer (server-side only). Omit for
// anonymous / card-only sessions. The SDK forwards it on buyer-bound
// calls — it does not acquire or refresh it for you.
buyerToken: session.buyerBearer,
// Relay a 401 challenge so the agent can silently refresh its token.
onAuthChallenge: (wwwAuthenticate) => respondWith401(wwwAuthenticate),
},
);
const signer = new Ap2Signer(
JSON.parse(process.env.AGENT_PRIVATE_JWK!), // EC P-256 private JWK
process.env.AGENT_KID!, // must match your published profile's public JWK kid
);
// Optional crypto rail (needs @mysten/sui). Returns undefined with no Sui key.
const crypto = resolveCryptoDeps({
agentSuiPrivateKey: process.env.AGENT_SUI_PRIVATE_KEY, // suiprivkey1...
suiNetwork: 'mainnet',
agentMaxSpendAmount: 50_000, // optional client-side cap (minor units)
});
const shop = createCheckoutHandlers({ client, signer, crypto });Search → checkout → buy
// 1. Discover. Price filters are MAJOR units (dollars) — converted for you.
const results = await shop.searchProducts({
query: 'running shoes',
filters: { price: { max: 150, currency: 'USD' } },
});
// 2. Build a checkout, per store, using the slug from each search result's
// metadata.artos_seller.slug.
const checkout = await shop.createCheckout({
storeSlug: 'energy-sport',
items: [{ id: 'variant_123', quantity: 1 }],
shippingAddress: { country: 'US', postal_code: '94016' },
});
// 3. Confirm. Re-prices, verifies the store's signed terms, mints the AP2
// mandate, routes the rail, and returns a structured outcome.
const outcome = await shop.confirmPurchase({
storeSlug: 'energy-sport',
checkoutId: checkout.id as string,
paymentMethod: 'artos.card', // omit on a multi-rail store to be asked
});
switch (outcome.status) {
case 'completed':
return renderOrder(outcome.checkout);
case 'escalation_required':
return redirect(outcome.continueUrl); // 3-DS / hosted step
case 'payment_selection_required':
return askBuyerToPickRail(outcome.rails);
case 'error':
return showError(outcome.code, outcome.message);
}confirmPurchase never throws for a business outcome — it always resolves to
a CheckoutOutcome. Transport failures (network / non-2xx / JSON-RPC error)
still throw UcpClientError; an expired buyer bearer throws UcpAuthError
carrying the WWW-Authenticate challenge (relayed via onAuthChallenge).
Crypto in one call
To settle on-chain, pass resolveCryptoDeps(...) into createCheckoutHandlers
and confirm with the crypto rail:
const outcome = await shop.confirmPurchase({
storeSlug: 'energy-sport',
checkoutId: checkout.id as string,
paymentMethod: 'artos.crypto',
});Internally the SDK prepares the payment intent (buyer bearer only), re-prices,
re-verifies the merchant authorization, mints the checkout_mandate and
payment_mandate, signs and submits the Sui PTB via SuiSigner, then completes
with the tx digest. Crypto requires the buyer to have authorized your agent under
My Artos → Agents with a spend cap — OAuth consent alone is not enough.
The two-credential auth model
The Artos API’s identity guard prefers X-API-Key over a bearer, so the SDK
encodes the rule for you:
- Platform
X-API-Key(default) — catalog, cart, checkout create/update,get_order, card completion. - Buyer OAuth bearer only — crypto
prepare/completeand all/account/*tools (the API key is omitted so the buyer account + caps resolve).
You never send both on a buyer-bound call. See Authentication.
API surface
UcpClient—globalSearch,callGlobalTool,callStoreTool,listAccountTools,callAccountTool,fetchStoreProfile,fetchImage,hasBuyerToken,ucpAgentHeader.Ap2Signer—mintCheckoutMandate,mintPaymentMandate.createCheckoutHandlers— read tools +confirmPurchase.canonicalJson,verifyDetachedJws,verifyMerchantAuthorization.SuiSigner,resolveCryptoDeps(crypto rail; need@mysten/sui).- Helpers:
toMinorUnits,normalizeSearchFilters,resolveRail,grandTotal,currencyOf,asAllowanceId,isUcpError,messageOf.
Subpath entries are published too: @artos-commerce/ucp-client/ucp, /ap2,
/checkout, /sui, /crypto.
Next steps
- Authentication — the OAuth flow you still own
- Direct UCP — the raw transport the SDK speaks under the hood
- Checkout — rails, AP2, coupons,
$0orders - Reference — the SDK export list and raw UCP mapping