Reference
HTTP gateway API
Everything the CLI does to pay for an item is plain HTTP, so any client can do it. This page is for building your own.
Items live at https://api.v4.cryptocadet.app/v4/g/<handle>/<slug>. Proxy items also accept a sub-path, /v4/g/<handle>/<slug>/<path>, and any method. Hosted items accept GET and HEAD. All amounts are strings in micro-dollars (USDC base units): "50000" is $0.05.
The 402 response
A paid item requested without payment answers 402. A free item is served straight away. The body:
{
"error": "payment_required",
"message": "Payment required",
"x402Version": 1,
"resource": "https://api.v4.cryptocadet.app/v4/g/acme/market-brief",
"product": {
"name": "Market brief",
"description": "One-page summary for agents",
"kind": "hosted",
"seller": "seller@example.com"
},
"price": { "amount": "0.05", "currency": "USD", "micros": "50000" },
"per": "query",
"restricted": false,
"accepts": [
{
"scheme": "cryptocadet-credits",
"rail": "credits",
"header": "X-CryptoCadet-Agent",
"format": "ccx_agent_…",
"maxAmountRequired": "50000",
"resource": "https://api.v4.cryptocadet.app/v4/g/acme/market-brief",
"description": "Spends the agent owner’s card-funded credits"
},
{
"scheme": "cryptocadet-v4",
"rail": "usdc",
"network": "base",
"chainId": 8453,
"asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"maxAmountRequired": "50000",
"payTo": "0xSellerPayoutAddress",
"resource": "https://api.v4.cryptocadet.app/v4/g/acme/market-brief",
"quoteUrl": "https://api.v4.cryptocadet.app/v4/gateway/quotes",
"quoteBody": { "handle": "acme", "slug": "market-brief" },
"proofHeader": "X-PAYMENT",
"proofFormat": "{\"quoteId\",\"txHash\",\"redeemToken\"} as JSON or base64 JSON",
"router": "0xffe91f1582080c85f7651fa32de2186df1b9343b",
"routerMethod": "payWithToken(address,address,uint256)",
"calldataSuffix": "quoteId (16 bytes, uuid without dashes)",
"feePercent": 3
}
]
}
acceptslists the ways to pay, in the x402 style. Pick one byrail.- The
usdcentry is present only when the seller has a payout address and the item isn't private. Therouterfields appear when payments go through the CryptoCadetRouter, as they do on Base mainnet. - For a private item,
peris"grant"andrestrictedistrue. - When a payment attempt is refused, the 402 body also carries a
codeand a specificmessage(see Errors).
Paying with card credits
Send an agent token in the header the 402 names:
curl https://api.v4.cryptocadet.app/v4/g/acme/market-brief -H "X-CryptoCadet-Agent: ccx_agent_…"
Authorization: Bearer ccx_agent_… also works; for proxy items that header is removed before the request is forwarded, so the token never reaches the seller. The server checks the token, its caps and allowed sellers, and your balance, then charges and serves the item in one step. No retry is needed.
https://api.v4.cryptocadet.app. Anyone who holds it can spend your credits up to its caps. The CLI enforces this for you.Paying with USDC
Get a quote. POST the
quoteBodyto thequoteUrl. No account is needed.Requestcurl -X POST https://api.v4.cryptocadet.app/v4/gateway/quotes \ -H "Content-Type: application/json" \ -d '{"handle":"acme","slug":"market-brief"}'201 Created{ "quote": { "quoteId": "4f9c2a1e-8b7d-4c3a-9e21-0d5f6a7b8c9d", "chainId": 8453, "token": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "recipient": "0xSellerPayoutAddress", "amount": "50000", "purpose": "per_call", "expiresAt": 1793520300, "router": "0xffe91f1582080c85f7651fa32de2186df1b9343b", "serverSig": "…" }, "redeemToken": "ccr_…", "resource": "https://api.v4.cryptocadet.app/v4/g/acme/market-brief" }Keep the
redeemTokenprivate: it proves you are the one who requested and paid this quote.serverSigis an Ed25519 signature; the public key is atGET /v4/quotes/pubkey.Pay it on Base. From your wallet, call
approve(router, amount)on the USDC contract, then callpayWithToken(recipient, token, amount)on the router, with the quote'squoteIdappended to the calldata as 16 bytes (the UUID's 32 hex digits without dashes). The router sends the seller 97% and the 3% fee in that transaction. Ifrouterisnull, transferamountoftokentorecipientdirectly.Repeat the request with proof. Put the quote id, the transaction hash and the redeem token in
X-PAYMENT, as JSON or base64-encoded JSON:RequestPROOF=$(printf '%s' '{"quoteId":"4f9c2a1e-…","txHash":"0x…","redeemToken":"ccr_…"}' | base64 | tr -d '\n') curl https://api.v4.cryptocadet.app/v4/g/acme/market-brief -H "X-PAYMENT: $PROOF"
The gateway checks the payment on-chain before serving. It must have enough block confirmations, so a request sent right after the transaction may get 422 VERIFY_FAILED: wait a few seconds and send the same proof again. Redeem before the quote's expiresAt. One paid quote buys one access; sending the proof again after it has been redeemed returns PROOF_REDEEMED.
Response headers
| Header | Meaning |
|---|---|
X-CryptoCadet-Access | paid, unlock (first read of an unlock grant), granted_read (already unlocked), free_read, or own (the seller reading their own item, no charge) |
X-CryptoCadet-Charge | The charge id, when something was charged |
X-CryptoCadet-Charged | The amount charged, in micro-dollars |
X-CryptoCadet-Rail | credits or usdc |
For a proxy item, the rest of the response (status, headers, body) is the seller's, except Set-Cookie, which is dropped.
Errors
Refused payments answer 402 with the full body above plus code and message. Other errors answer { "error": "<message>", "code": "<code>" }, where code may be absent.
| Status | Code | Meaning |
|---|---|---|
| 402 | INSUFFICIENT_CREDITS | Not enough credits. Top up. |
| 402 | AGENT_PER_CALL_CAP | The price is above the token's per-call cap. |
| 402 | AGENT_DAILY_CAP | The token has reached its 24-hour cap. |
| 402 | BAD_PROOF | X-PAYMENT is malformed, the quote was not issued for this item, or the redeemToken is missing or wrong. |
| 402 | PROOF_REDEEMED | This payment has already been used. |
| 402 | CREDITS_ONLY | A USDC proof was sent for a private item. (Asking for a quote for one answers 400 with the same code.) |
| 401 | Unknown or revoked agent token. | |
| 403 | The token may not buy from this seller, or the private item isn't shared with you. | |
| 404 | No such item, or it is disabled. | |
| 405 | Hosted items are read with GET. | |
| 409 | TX_ALREADY_USED | That transaction already paid a different quote. |
| 422 | VERIFY_FAILED | The payment isn't verified yet, or doesn't match the quote. Retry shortly if you just paid. |
| 422 | QUOTE_EXPIRED | The quote expired before it was redeemed. |
| 502 | ORIGIN_ERROR | The seller's server failed. The body has refunded (true for a refunded credits charge) and chargeId. |
Sellers: the headers your origin receives, and how to verify them, are in Selling.