# agents.u.cash > The agent + merchant product surface for the 402 Online Protocol - multi-coin, non-custodial HTTP-402 payments for AI agents, humans, and merchants. Two-sided: the same account can SELL (get paid to its own wallet) and BUY (pay for resources). A live reference implementation. Protocol spec: https://402.onl Human-browsable API reference (every endpoint, parameter, response): https://agents.u.cash/reference - generated from the same source as the OpenAPI spec below. ## Key properties - Non-custodial: every payment goes direct buyer->seller (the seller's own wallet). The platform never holds funds or keys; it only verifies, detects on-chain, and notifies. Not a money transmitter. - Multi-coin: the detect scheme covers any asset with an address. Supported assets (the engine master list; grouped): - Native L1s: Bitcoin (BTC, +Lightning), Ethereum (ETH), Solana (SOL), Tron (TRX), XRP, Dogecoin (DOGE), Litecoin (LTC), Bitcoin Cash (BCH), Algorand (ALGO), Monero (XMR), Polkadot (DOT), Stellar (XLM), Tezos (XTZ), Polygon (POL), Avalanche (AVAX), BNB Chain (BNB), Toncoin (TON) - EVM L2s: Base, Arbitrum, Optimism, Linea, Mantle, Berachain, Sonic, Unichain, World, Monad, Plasma, Abstract, Scroll, Ink, Hyperliquid (native gas + ETH-bridged), Robinhood Chain (native gas + ALL 194 issuer-listed tokenized-asset rails; featured set in pickers, u.cash/tokenized-assets for the full list) - Stablecoins: USDT (Ethereum / BSC / Tron), USDC (Ethereum), and USDC-on-Base (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) - the x402 gasless rail - Platform token: UCASH (Ethereum ERC-20, 0x92e52a1A235d9A103D970901066CE910AAceFD37) - also the non-withdrawable fee-credit unit - Custom tokens: Any ERC-20 / BEP-20 / Base-20 / Polygon-20 / Arb-20 / Op-20 / Avax-20 / Linea / Mantle / Sonic / Bera / ... / TRC-20 / SPL / TON Jetton / Robinhood-20 added via POST /v1/custom-tokens (auto-detected on-chain like any coin) A coin becomes payable for a seller once it has a receive address (POST /v1/wallets); custom tokens extend the set per-merchant. - x402 / exact rail: USDC on Base via EIP-3009 transferWithAuthorization. The buyer signs an off-chain authorization; the platform verifies it and settles non-custodially (confirm-then-settle by default; a relayer is ON by default - opt-out via POST /v1/exact-relay {enabled:0} - adding instant, gasless settlement, billed to the seller as gas + 21%). The /r/ door speaks the x402 wire format (WWW-Authenticate: x402, X-PAYMENT) and exposes facilitator POST /v1/exact-verify + /v1/exact-settle, so an off-the-shelf x402 client pays without changes. - Two-sided: an agent can both sell (create priced resources) and buy (pay for others' resources). - Agent-native: API-key auth, idempotent verify, HMAC-signed webhooks, a settlement ledger, and an MCP tool surface (see 'For AI agents'). - Buyer-push verify: a buyer needs no seller key - the public challengeId authorizes a verify, gated by on-chain confirmation. ## Auth Send the api_key from POST /v1/signup in the X-Api-Key header. Sign up is free and instant: the account works immediately at $0 (no verification, no payment required to start). Email is OPTIONAL: sign up with an email OR a username. With no email the agent gets a synthetic @agents.u.cash address (no inbox; it is webhook-only) and the human overseer signs in at pay.u.cash/admin with the username. The tenant database is created LAZILY on the agent's first WRITE (e.g. POST /v1/wallets or POST /v1/resources). Verifying an email, when provided, is an optional REWARD: $5 free starter credit (up to $2,500 in transactions; CLOUD_INITIAL_FREE_CREDIT_AGENTS) + the overseer dashboard. POST /v1/top-up (>= $1) adds platform credit once the free balance runs low. ## Concepts - resource (res_id): a priced listing a seller creates (fiat amount + accepted assets). NOT a single transaction - it accepts many coins. - accepts[]: the multi-coin 402 challenge - one entry per accepted asset {scheme:'detect', asset, network, payTo (the seller's own address), amount (exact, or dust-randomized for a reused address - see the next item), challengeId, expiresAt}. - payment amount (EXACT vs DUST): the platform settles by matching the buyer's on-chain transfer to the EXACT amount + receive address it issued. Save 3+ receive addresses for a coin (the Address List, comma-separated, e.g. POST /v1/wallets {asset:'ucash', address:'0xaaa...,0xbbb...,0xccc...'}) and each buyer gets a UNIQUE address -> the door demands the EXACT amount (e.g. 500.00000000). With only 1-2 addresses the address is reused, so the door adds a tiny DUST variation in the LAST decimals only (e.g. 500.00004180, never a visible overcharge like 500.235) so two concurrent same-amount payments to the one address stay distinguishable - the buyer pays effectively the listed amount. For a crypto-denominated resource (a literal token amount, asset + asset_amount) prefer EXACT mode: save 3+ addresses for that coin. For fiat-priced resources the coin amount is computed live from the rate, so dust is negligible either way. With 3+ addresses, a reused address is held for the configured address-reuse-cooldown (default 24h; PUT /v1/settings {address_reuse_cooldown:0} for immediate release once the payment settles) before it is reused again. - challengeId (chal_...): links a buyer's payment to a specific resource+asset; used to verify. Public (safe to share); settlement still requires real on-chain payment. - one door on pay.u.cash: a resource's checkout_url (/checkout/?cloud=) and a payment-request's url (/id/) are BOTH the 402 door - a human gets the HTML checkout, an agent gets the ?agent=1 JSON manifest (HTTP 402 + accepts[] + next + instructions), and an x402 client pays with an X-PAYMENT header. agents.u.cash /r/{res_id} is a back-compat alias of /checkout/ (same engine, same challenges). - settlement: a completed resource payment (status C). Recorded read-only in the seller's settlement log; funds already went to the seller's wallet. ## Account endpoints (all require X-Api-Key except signup) Auth + transport notes: - Rate limits are per API key when one is presented (keyless callers are limited per IP). Every response carries X-RateLimit-Limit + X-RateLimit-Remaining; a 429 also carries Retry-After (seconds to the window reset). - API keys are stored hashed server-side (sha256) and shown exactly once at signup/rotation; the dashboard and lists only ever display the masked hint (first6..last4). If you lose a key, rotate it. - POST /v1/resources + POST /v1/checkouts accept an Idempotency-Key header: a retry with the same key + same body replays the original response (Idempotency-Replayed: true) instead of creating a duplicate; the same key with a different body is a 400; failures release the key. - POST /v1/signup {email OR username, password, primary_wallet?, agreed_to_terms?} -> {api_key, activated, username, next, relay_to_user} [no key; works immediately at $0. An agent can sign its HUMAN user up by passing the user's email + password; the user then verifies the email to claim about $5 free fee credit (up to $2,500 in transaction volume). relay_to_user is a short, human-facing message the agent should show its user (see 'Credit, onboarding, and tier unlocks' below)] - POST /v1/top-up {amount} -> {url, amount, activated, next} [>= $1; adds platform credit (funding); allowed pre-activation] - POST /v1/reset-password {new_password} -> {reset:true} [OWNER KEY ONLY: an sa_ subagent key gets 403 uxc_forbidden. Resets the overseer web-login password (no-email agents have no email-reset path)] - POST /v1/account-email {email} -> {email, verification_required, next, relay_to_user} [OWNER KEY ONLY: an sa_ subagent key gets 403 uxc_forbidden. Add/change the email on the account; verifying grants $5 free credit (up to $2,500 in transactions). Login anchor + encryption key stay stable, so wallets keep decrypting + login works by username OR email] - POST /v1/resend-verification -> {sent, next, relay_to_user} [X-Api-Key auth; resend the verification link (3-day expiry for agents; 3h for merchants); no-op if already verified; 5 resends/account] - GET /v1/agent -> {email, activated, primary_wallet, credit_balance, webhook_url, stripe_configured, custom_tokens[{code,type,network,name,contract_address,decimals,confirmations?}], wallets{asset:address}, settlements_count, total_earned} - Human operators can also sign in to the full dashboard at https://pay.u.cash/admin with the email + password from signup to view the agent 402 sales, credit balance, and per-sale fee history. An existing pay.u.cash merchant can flip its account into an agent operator in-place: dashboard -> AI Agents -> 'Link an AI agent' (sets is_ai_agent; mints an api_key if none). ## Credit, onboarding, and tier unlocks (the agent cannot override these) - relay_to_user (on /v1/signup, /v1/account-email, /v1/resend-verification): a short, human-facing message. Show it to the user. It explains that verifying their email earns about $5 UCASH fee credit (up to $2,500 in transaction volume), that there are more ways to earn UCASH via onboarding at pay.u.cash, and that spend tiers unlock greater capability (branding, invoicing, logo, colors, custom fields, flat fees, custom domain). - GUARDRAIL: agent accounts CANNOT override or skip email verification, onboarding, or tier unlocks. There is no API path that flips verification_status, is_ai_agent_activated, onboarding_progress, ucash_tier_cached, ucash_points, or any *-enabled unlock toggle. PUT /v1/settings writes only a safe subset (confirmations, webhook, currency, payment prefs, notifications, branding, texts). The human completes verification (click the emailed link), onboarding (at pay.u.cash/admin), and earns tiers (by funding the account) exactly like a human merchant. A no-email (synthetic) agent is auto-verified but receives no credit until it adds + verifies a real email. - POST /v1/agent {webhook_url} -> sets the settlement webhook URL (back-compat alias of POST /v1/webhook) - POST /v1/webhook {url} -> set the settlement webhook URL + auto-generate the HMAC signing secret on first set (shown once). {rotate_secret:true} rotates the secret; GET reads it masked; {url:""} clears. Writes (incl. secret rotation) need the settings.edit capability for an sa_ subagent key, and the whole endpoint is live-key only (a sandbox key gets 403 uxc_sandbox_key_rejected). The settlement engine signs agent-resource sales with this secret, so set it here (not the merchant webhook). - GET /v1/me + GET /v1/account (+ /v1/account-info) -> aliases for GET /v1/agent (the account snapshot) - GET /v1/webhooks -> discoverability alias (the canonical webhook surface is POST/GET /v1/webhook) - GET /v1/wallets -> {wallets{asset:address}} [your receive addresses; save 3+ comma-separated for a coin for exact-amount unique-address payments, e.g. {asset:'ucash', address:'0xaaa...,0xbbb...,0xccc...'}] - POST /v1/wallets {asset, address} -> {asset, address} [sets your own receive address for an asset (accepts comma-separated for the Address List); where buyer funds land] - POST /v1/stripe {secret_key, product_id, webhook_secret, publishable_key?} -> masked config + webhook_endpoint [connect your OWN Stripe account to enable card payments (the redirect rail); key + product are verified against Stripe first; secrets are never returned in full; non-custodial - card payments settle direct to your Stripe; writes need the payments.processors.edit capability for an sa_ subagent key] - GET /v1/stripe -> masked Stripe config + the webhook endpoint URL to register (subscribe checkout.session.completed) - DELETE /v1/stripe (or POST {clear:1}) -> disconnect Stripe (disables the card rail) [payments.processors.edit for an sa_ key] - POST /v1/exact-relay {enabled:1|0} -> manage the exact relayer for instant, gasless USDC-on-Base settlement. ON by default (opt-out): a stock x402 client pays gasless and the seller is billed gas + 21% per settle; POST {enabled:0} / DELETE opts out to verify-only (buyer-pays-gas). GET returns masked config + funding source. Writes need the settings.edit capability for an sa_ subagent key. - POST /v1/custom-tokens {type, code, contract_address, decimals, name, rate?, rate_url?, confirmations?, img?} -> the stored token [self-serve a custom token: type is erc-20/bep-20/base-20/polygon-20/arb-20/op-20/avax-20/trc-20/spl/ton-jetton/robinhood-20 (picks the chain); code must not collide with a built-in coin; contract + decimals; then POST /v1/wallets {asset:code, address} to set where buyers pay - the token is auto-detected on-chain like any coin; writes need settings.edit for an sa_ subagent key] - GET /v1/custom-tokens -> {custom_tokens:[{code, type, network, name, contract_address, decimals, confirmations?}]} - DELETE /v1/custom-tokens?code= -> removes one custom token [settings.edit for an sa_ key] - GET /v1/settlements -> {settlements:[{transaction_id, res_id, asset, amount, amount_fiat, currency, status, tx_hash, created_at, settled_at}]} - GET/PUT /v1/integrations -> {integrations} [read/write third-party credentials you have stored: Discord, Telegram, BigCommerce, Ecwid, Wix. Returned in plaintext to the authenticated agent; BOTH methods need the settings.edit capability for an sa_ subagent key (reads expose secrets)] ## Storefront (Shop Page) endpoints - Capability note: every non-GET /v1/site op (site save, template create/update/delete/apply, asset upload) needs the design.edit capability for an sa_ subagent key. GETs stay open. - GET /v1/site -> {site:{values, template, themes, layouts, layout, style_vars}} [your storefront chrome (shop-* values) + the free-canvas block layout] - PUT /v1/site {chrome?:{shop-*:val}, layout?:{v,blocks}} -> {saved:true} [shop-* whitelist only; saves MERGE so wallets/fees are never wiped; layout is cleaned] - GET /v1/site?resource=templates -> {templates:[...]} ; ?resource=templates&id=N -> {template:{...}} - POST /v1/site?resource=templates {name, base_template?, base_theme?, chrome, css?, layout?} -> {id, saved:true} - PUT /v1/site?resource=templates&id=N {chrome, css?, layout?} -> {saved:true} [chrome is required on PUT] - POST /v1/site?resource=templates&action=apply&id=N -> {applied:true} [apply a template to the live storefront] - DELETE /v1/site?resource=templates&id=N -> {deleted:true} - POST /v1/site?resource=assets (multipart file) -> {url} [image upload; real image only (JPG/PNG/WebP/GIF), max 5MB; served from shop.u.cash/uploads//] - The storefront is account-scoped (one Shop Page per account). Edits appear on https://pay.u.cash/shop and in the shop.u.cash visual builder. The shop-* key whitelist + the chrome/layout cleaners + the CSS sanitizer are the security boundary (no CSRF; header-key auth, like every /v1/* endpoint). ## 402 payment endpoints - POST /v1/resources {amount, currency?=USD, accepted_assets?=all wallets, webhook_url?, asset?, asset_amount?} -> {res_id, amount, currency, cryptocurrency, crypto_denominated, accepted_assets, checkout_url} [**checkout_url** is the canonical buyer door on pay.u.cash (/checkout/?cloud=): a human opens the HTML chooser, an agent fetches ?agent=1 for the 402 manifest, an x402 client sends X-PAYMENT. amount + currency price it in fiat (the coin amount is computed live at the door). OR pass asset + asset_amount to price it in a specific token, e.g. {asset:'ucash', asset_amount:500} -> a single-coin resource where the buyer pays about 500 UCASH (crypto_denominated:true; amount_fiat is derived for the fee). A crypto-denominated resource accepts only that coin and skips the card/x402 rails. Send an Idempotency-Key header to make retried creates replay the same response instead of duplicating] - DELETE /v1/resources?res_id= -> {disabled:true, res_id} [SOFT disable: removes the resource from the list and the /r/ door, but keeps its settled history in GET /v1/settlements. Owner-gated] - POST /v1/payment-requests {amount, currency?=USD, cryptocurrency?, title?, note?, expiry?=24h, redirect?, external_reference?, asset?, asset_amount?} -> {transaction_id, encrypted_id, url, amount, currency, cryptocurrency, expiry, status} [ONE-OFF: a pay.u.cash /id/ link that CLOSES when paid or after the expiry (15m/1h/24h/7d/30d/never). The buyer pays via the url (HTML checkout, + 'Let your agent pay' + the ?agent=1 manifest). Distinct from /v1/resources (persistent). {asset, asset_amount} prices it in a token (about that amount; drifts with the rate). external_reference makes retries idempotent] - GET /v1/payment-requests -> {payment_requests:[{transaction_id, url, amount, amount_fiat, currency, cryptocurrency, status, title, expiry, created_at}]} [list your one-off requests; ?id= fetches one] - POST /v1/challenge {res_id} -> {res_id, amount, currency, accepts:[...]} [mints/reuses per-asset challenges; idempotent within TTL] - POST /v1/verify {challengeId, hash} -> {settled:true, transaction_id} | {settled:false, status:'pending', confirmations, required} | {status:'underpaid'} [detect: optional instant settle; the platform also auto-detects the on-chain payment; buyer-push, no key needed. The on-chain hash field is "hash" (txhash, tx_hash, transactionHash, transaction_hash are also accepted). A challenge lives 1 hour; on uxc_challenge_expired, fetch a fresh door first] OR {challengeId, authorization, signature} for the exact (EIP-3009) scheme -> verify the signed authorization + settle. - GET /r/{res_id} (BACK-COMPAT ALIAS of pay.u.cash /checkout/ - same 402 engine, same challenges; the canonical buyer door is the resource's checkout_url on pay.u.cash, but /r/ keeps working for existing links + the x402 PaymentRequirements.resource URL + the catalog) -> HTTP 402 + {accepts[], next, instructions} (machine; + WWW-Authenticate: x402 + paymentRequirements[] when an exact entry is offered; next + instructions are the short-form steps a buyer-agent follows to pay) OR a payable HTML page (browser, Accept: text/html) OR 200 {settled:true} if paid. With an X-PAYMENT header (base64url x402 PaymentPayload) it verifies + settles an exact payment and returns X-PAYMENT-RESPONSE (or 202 pending). GET /r/{res_id}?status=1 -> {res_id, settled, expires_at} is a no-mint status poll (wait for settlement without minting challenges). Public, no key. - POST /v1/exact-verify {x402Version, paymentPayload:{payload:{authorization, signature}}, paymentRequirements:{resource}} -> {isValid, invalidReason?, payer} [x402 facilitator verify: pure signature check, no settle] - POST /v1/exact-settle {x402Version, paymentPayload, paymentRequirements} -> {success, transaction, network, payer, errorReason?} [x402 facilitator settle: verify + settle. A hard failure (malformed/invalid signature, payee mismatch, amount, expiry, no open challenge) ALSO returns this documented shape with success:false + errorReason, never a bare error envelope] ## Management endpoints (account operations; all X-Api-Key) - GET /v1/transactions -> {transactions:[...]} [full history with filters: status, cryptocurrency, checkout_id, date_from/date_to, search, limit/offset; ?webhook_log=N appends the delivery log; ?download=csv exports] - POST /v1/transactions?transaction_id=N&action=refund [cap transactions.refund; self-guarding - needs a refund-capable btc/eth node or coinbase-refunds, and only refunds the exact received value to the sender] | &action=resend-webhook [force a delivery retry; transaction must be completed with a URL set] | &action=submit-hash {hash} [attach an on-chain hash to a pending payment] - GET /v1/billing -> {credit_balance, ucash, lander_slots} [your prepaid fee credit, UCASH balance + points, and lander slots] - POST /v1/billing {action:'buy-lander-pack', pack:'1|5|10|25|50|100|1000|10000', qty?} [one-time reusable lander slots from credit; 402 uxc_insufficient_balance if short] | {action:'redeem-ucash', amount} [redeem UCASH points into fee credit at the market rate; caps landers.edit / financials.edit] - GET/POST/PUT/DELETE /v1/stores [multi-store: {label} create/update; POST rotate mints a fresh per-store cloud_token; POST test_webhook sends a signed test; DELETE removes. Store api_keys are hash-at-rest: the list returns the masked api_key_hint (first6..last4); rotate=api_key returns a fresh raw key exactly once] - GET/POST/PUT/DELETE /v1/subagents (OWNER-ONLY) [scoped sa_ keys: POST {display_name, role} returns the key exactly once; PUT changes the role; POST action=subagent-rotate-key replaces it. Every sa_ key is capability-gated per endpoint (the RBAC model) - it can never exceed the role's permissions] - GET/POST/PUT/DELETE /v1/landers [landing pages: hero/copy/CTA/domain per lander, offers (Make Offer), CSV import/export; slot-gated (10 free, then packs via /v1/billing); cap landers.edit] - POST /v1/payout-info {amount, currency_code|asset, email, title?, note?} -> {transaction_id, url} [OTC payout flow: the payee submits their wallet/email through a tokenized link; GET ?transaction_id=N tracks Unopened/Opened/Submitted] ; POST /v1/payout-info?transaction_id=N&action=complete [mark paid after sending out of band; caps payouts.create + financials.edit] - GET/POST/PUT/DELETE /v1/custom-fields [the checkout form-field repeater: text/textarea/select/checkbox, name, required, plus the section title; cap checkouts.edit] - GET/POST/PUT/DELETE /v1/discount-codes [repeater {code, amount, checkout_ids} - percentage codes buyers apply at checkout; cap checkouts.edit] ## Sell flow (become a seller) 1. POST /v1/signup -> api_key (works instantly at $0; a verification link is emailed, optional). 2. POST /v1/wallets {asset:BTC, address:bc1q...} for each coin you accept (the tenant DB is created on this first write). 3. POST /v1/resources {amount:0.05} -> {res_id, checkout_url}. 4. Share the checkout_url (the pay.u.cash buyer door; /r/{res_id} still works as an alias). 5. On payment, you get an HMAC-signed webhook + the settlement appears in GET /v1/settlements. Buyer funds went straight to your wallet. (Optional any time: verify your email for $5 free credit, up to $2,500 in transactions.) ## Test mode (zero-fund testing) To try the whole flow with no real money: POST /v1/resources {amount:0.05, test:true} (needs NO wallet). GET /r/{res_id} -> accepts[] with one test entry {test:true, challengeId}. POST /v1/verify {challengeId, hash:'0xtest'} -> {settled:true, test:true} (instant $0 settle) + fires an HMAC webhook marked test:true. Test settlements are EXCLUDED from GET /v1/settlements and from your earnings summary. A test resource is for your own testing; do not share it as a real payable link. ## Sandbox keys (sk_test_ / pk_test_) Self-provisioned TEST credentials for whole-integration rehearsal before you go live: create them in the dashboard (Account > Sandbox) or POST /v1/sandbox-keys {kind:secret}. An sk_test_ key authenticates the agent + merchant APIs in sandbox mode: every record it creates is tagged is_test, no funds ever move, no fees meter, and nothing appears in live reporting, balances, or payouts (live-only operations return 403 uxc_sandbox_key_rejected; a live key cannot create test-only artifacts and vice versa). pk_test_ is the publishable twin for hosted test-checkout URLs. Add test_confirmations (0-12, default 1) at resource creation to rehearse multi-confirmation settles step by step with POST /v1/simulate-payment {challengeId} (or the Simulate payment button on the hosted test page); each call advances the simulated confirmations and the settlement webhook fires with test:true at the threshold. Full guide: https://pay.u.cash/docs/sandbox ## Card rail (optional) To also accept card payments, POST /v1/stripe {secret_key, product_id, webhook_secret} with your OWN Stripe account keys (the key + product are verified against Stripe before storing). Then register the returned webhook_endpoint (subscribe checkout.session.completed) in your Stripe dashboard. Buyers then see a redirect (card) entry in accepts[] alongside the coin entries; card payments settle direct to your Stripe account (non-custodial). The platform fee is debited from your prefunded credit. GET /v1/stripe returns the masked config + the endpoint to register; DELETE /v1/stripe disconnects. ## Custom tokens (optional) To accept a token that is not built in (an ERC-20/BEP-20/Base/Polygon/Arbitrum/Optimism/Avalanche/TRC-20/SPL/TON Jetton/Robinhood-20), POST /v1/custom-tokens {type, code, contract_address, decimals, name} to add it to your account, then POST /v1/wallets {asset:code, address} to set where buyers pay it. The token is auto-detected on-chain like any built-in coin (the cron scans the contract transfers). GET /v1/custom-tokens lists them; DELETE /v1/custom-tokens?code= removes one. ## Settings GET /v1/settings -> {confirmations:{default, coins:{:n}, increase:{threshold, percentage}}, webhook:{url, has_secret, secret_last4}, currency, address_reuse_cooldown (seconds; 0=immediate, default 86400), web3_names{code:name}, payment:{accept_underpayments, redirect}, notifications:{email}, branding:{brand_name, logo_url, logo_url_dark, color_1, color_2, color_3}}. PUT /v1/settings {any subset} updates (partial). The webhook secret is returned masked (secret_last4) so it can never be re-read; send webhook.rotate_secret=true to mint a new one - the rotation response shows the plaintext exactly once, so save it then (it is what you verify X-Webhook-Signature with on settlement webhooks). address_reuse_cooldown controls how long a receive address is held before reuse (Immediate frees it when the payment settles; 24h improves on-chain privacy). This is the safe subset only; nothing custodial/node/private-key/payout/multi-store is exposed. notifications.email defaults to your signup email when unset, so sale + underpayment notices work out of the box. ## Fees The buyer pays the seller the full amount, direct - to the seller's own wallet (detect) or the seller's own Stripe account (redirect). The platform never touches the payment. The platform fee is a small percentage that the seller prepays as a non-withdrawable credit balance; it is debited separately when a sale settles - never taken from the payment, never held. No intermediary ever controls the transaction funds. ## Private by design No custodial accounts, no KYC, no held balances. The platform sees only resource metadata and on-chain payment confirmation - it holds no user funds and no user private keys. Every payment is peer-to-peer, direct buyer->seller. That is what keeps it out of money-transmitter scope. ## Human oversight Agents transact autonomously, and the human behind each agent has full oversight: sign in to the dashboard at https://pay.u.cash/admin with the signup email + password to audit, track, and analyze every transaction - the 402 sales, settlements, credit balance, and per-sale fee history. Autonomy and transparency, together. ## Buy flow (pay for a resource) 1. GET /r/{res_id} -> accepts[] + next + instructions (or the HTML page in a browser). 2. Pick an entry; pay entry.payTo exactly entry.amount from your wallet (out of band - the API never touches your keys). 3. The platform detects the on-chain payment and settles automatically (within a few minutes). Optionally POST /v1/verify {challengeId, hash} to settle instantly (poll while confirmations accumulate; or poll GET /r/{res_id}?status=1 for settled:true). The door, /v1/challenge, and /v1/verify responses each carry a short next + instructions a buyer-agent can follow verbatim. No seller key needed; no client proof required. ## Webhooks On settlement, agents.u.cash POSTs an HMAC-signed (X-Webhook-Signature: t=..,v1=..) JSON {transaction, event_id} to your configured webhook_url. Verify the signature with your webhook secret; the body is authoritative (never trust amounts from the request). ## Rate limits (per-IP fixed windows; over -> 429 uxc_rate_limited) signup 10/hr, top-up/resources/challenge/verify/settlements/agent 30-60/min, /r/ 120/min. ## For AI agents - MCP server (drive the whole seller surface as tools, e.g. in Claude Desktop): https://github.com/UdotCash/agents/tree/main/mcp - env UXC_API_KEY, 79 tools (uxc_get_agent, uxc_set_wallet, uxc_create_resource, uxc_list_resources, uxc_create_challenge, uxc_get_settlements, uxc_view_door, uxc_verify_payment, uxc_list_checkouts, uxc_list_stores, ...; full list in tools/list). - SDKs (zero-dependency): JS `npm install @ucash/agents`, Python `pip install agents-u-cash` - https://github.com/UdotCash/agents/tree/main/sdk - Structured API: this server's OpenAPI at https://agents.u.cash/openapi.json ## Agentic discovery + checkout (UCP) Each merchant business is machine-discoverable + buyable via UCP (ucp.dev, an open standard, not Stripe). Discovery: GET /catalog.json (or a merchant custom domain, or pay.u.cash?cloud=) returns a schema.org/Product JSON-LD catalog; GET /catalog.txt is a plain-text index; GET /.well-known/ucp returns the UCP profile (catalog.lookup + checkout + ap2_mandate capabilities, our non-custodial detect + redirect payment handlers, an EC signing key). Checkout sessions are the UCP buyer flow (resolve the merchant from the host or ?cloud=; no X-Api-Key): - POST /checkout-sessions?cloud= {line_items:[{item:{id}, quantity}], currency?, buyer?, context?} -> {id, status, currency, line_items, totals, ap2:{merchant_authorization, nonce}}. Multi-item + mixed-currency carts: per-item keeps its original amount+currency; the cart total converts to the cart currency (the `currency` field, else the merchant default). - GET /checkout-sessions/{id}?cloud= -> the session (status: incomplete -> ready_for_complete -> completed). - POST /checkout-sessions/{id}/complete?cloud= {ap2?:{checkout_mandate}} -> ready_for_complete + payment_handlers[] (the per-coin detect challenges + the redirect). AP2 is optional (dev.ucp.shopping.ap2_mandate): submit a buyer-signed checkout_mandate (SD-JWT-VC) for holder-proof authorization; it is verified (issuer signature + checkout-hash binding + key-binding JWT + open-mandate constraints), else 401 mandate_invalid_signature / mandate_expired / mandate_scope_mismatch. - POST /checkout-sessions/{id}/cancel?cloud= -> canceled. - GET /orders/{id}?cloud= -> the order (per-item fulfillment status; completed once every line item settles). - POST /catalog/search | /catalog/lookup | /catalog/product ?cloud= -> structured catalog access (search with filters, batch lookup by id, single product). - GET /v1/market (public, NO cloud token) -> the cross-merchant market catalog: {items:[{id,title,description,price,currency,merchant,domain,slug,url,lander}],count}. Every item url is a payable /checkout/ link on pay.u.cash (the 402 door answers ?agent=1). ?q= filters by title/merchant/description, ?limit= (max 200, default 50). Every merchant-origin response is RFC 9421-signed (ES256) with the profile key (Content-Digest + Signature-Input + Signature headers); signed inbound requests are passively verified. Settlement is async + non-custodial: the buyer pays each item's challenge on-chain (detect) or via the seller's Stripe (redirect); the session reaches completed once all items settle. SDK helpers: createCheckout / getCheckout / completeCheckout / getOrder. ## Error envelope Errors: {success:false, error_code:'uxc_*', message}. Common: uxc_unauthorized (401), uxc_rate_limited (429), uxc_not_found (404), uxc_invalid_request (400), uxc_provision_failed (500), uxc_challenge_expired, uxc_replay (reused tx hash). ## Input contracts (strict; nothing is ever silently ignored) On every authenticated write (POST/PUT/DELETE) the server validates the request body BEFORE touching state: - Unknown or misspelled top-level keys are rejected with 400 uxc_unknown_param: 'unrecognized key(s): x. Allowed: .' (the same contract the JS SDK's assertKeys enforces client-side). - Wrong shapes are rejected with 400 uxc_invalid_request naming the field and the expected shape, e.g. confirmations must be an object {default, coins, increase} (a bare number 2 is a 400, never a silent no-op). - Booleans must be real booleans (true/false, or the strings "true"/"false"/"1"/"0"). A truthy string like "false" or "yes" is a 400 - it can never silently flip a setting. - A write with no recognized keys (e.g. PUT /v1/settings {}) is a 400 listing what you can send; it never returns a pointless success. - Partial updates are TRUE partials: unsent fields keep their values (PUT /v1/checkouts {price:5} changes only the price). - Success responses are unambiguous: e.g. re-setting an existing webhook secret returns has_secret:true + a rotate hint instead of a bare empty string. ## Prime member surface (prime.u.cash; member-consented via a one-time link code) Members mint a one-time pl- code at https://prime.u.cash/agents-link choosing scopes and UNITS/day spend ceilings; POST /v1/prime/link claims it once. Scopes: prime.read, prime.characters.read, prime.characters.act, prime.explore.act, prime.games.act, prime.pvp.act, prime.pvp.wager, prime.tournaments.act, prime.tournament.entry, prime.inventions.propose, prime.studio.spend, prime.feed.read. - GET /v1/prime/me, /v1/prime/characters, /v1/prime/character?id= - member summary and champion sheets - POST /v1/prime/act {action: train|travel|mission-start|mission-claim} - champion actions - GET /v1/prime/games - the trial catalog (slugs, ceilings, caps, your standing); POST {action: start|finish} - server-authoritative play - GET /v1/prime/explore - the Atlas catalog (nodes, fragments, progress); POST {action: scan|claim} - galaxy-gated scans - POST /v1/prime/pvp {action: create|accept|decline|cancel} - Crucible duels; accept resolves the defender from the CALLER'S OWN LINK (an agent can never accept a duel aimed at a non-consenting member); wagers burn the fail-closed prime.pvp.wager grant (402 wager-grant-ceiling); GET lists ladder + your duels - POST /v1/prime/tournaments {action: enter, slug} - arcade heat entry (prime.tournament.entry grant before escrow); GET lists open heats with live boards - GET /v1/prime/quests - quest board + streaks + running season (claims stay member-only); GET /v1/prime/feed?since_id= - this link's activity journal (prime.feed.read) - POST /v1/prime/inventions - propose (2+ fragments + 1+ skill + thesis; operator publishes); GET/POST /v1/prime/studio - Forge media under the UNITS/day grant Agent economics: agent-attributed plays and scans earn a reduced UNITS rate (agent_reward_pct, default 25%) with FULL training and skill XP, capped per link per day (agent_earn_cap_units), fail closed. Human play always out-earns automation on the pool. ## Links - Human docs (the page this file mirrors): https://agents.u.cash/ - Human-browsable API reference: https://agents.u.cash/reference - MCP server (tools for Claude + any MCP client): https://agents.u.cash/mcp - x402 page (live gasless USDC-on-Base flow + facilitator): https://agents.u.cash/x402 - Fund relayer (top up the gas wallet behind gasless x402): https://agents.u.cash/fund-relayer - Protocol spec (vendor-neutral): https://402.onl - This API's OpenAPI: https://agents.u.cash/openapi.json - Source / SDKs / MCP: https://github.com/UdotCash/agents