Buy as an agent#
No account. No API key. No subscription. You need a wallet with USDC and nothing else.
The whole thing in one call#
from pay402 import pay
goods = pay(
"https://api.checkout402.com/c/chk_abc123",
private_key=os.environ["AGENT_KEY"],
max_usd="1.00",
)
goods.payload # what you bought
goods.receipt.url # proof you bought it
import { pay } from "./pay402.js";
const goods = await pay("https://api.checkout402.com/c/chk_abc123", {
privateKey: process.env.AGENT_KEY,
maxUsd: "1.00",
});
That's request → read the 402 → sign → retry with X-PAYMENT → goods + receipt.
max_usd is required for live payments
An agent looping on a paid endpoint with no ceiling is the failure mode that matters. pay() refuses rather than defaulting to permissive. Nothing is signed until the amount passes your cap.
Look before you spend#
from pay402 import quote
q = quote("https://api.checkout402.com/c/chk_abc123")
q.total_usd # Decimal('0.105')
q.fee_usd # Decimal('0.005')
q.description # what you'd be buying
quote() reads the 402 and spends nothing. The unpaid challenge carries the offer card — schemas, examples, freshness, coverage — so you can decide suitability first.
Doing it by hand#
1. Read the challenge#
curl https://api.checkout402.com/c/chk_abc123
{
"x402Version": 1,
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"maxAmountRequired": "105000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x234fE2A1D1df93fD9B907F0e1ad0c8b94E81FB5c",
"extra": {
"providerAmount": "100000",
"feeAmount": "5000",
"providerWallet": "0xSeller...",
"feeRecipient": "0xFee..."
}
}]
}
Amounts are atomic USDC (6 decimals) as strings — exact at any size, and JavaScript can't silently round them.
2. Sign, per family#
One checkout may offer several rails at the same dollar price. Pick whichever you can pay.
payTo is the immutable splitter contract. Sign an EIP-3009
ReceiveWithAuthorization for the exact total.
Let the service build the typed data rather than recomputing the nonce commitment yourself:
curl -X POST https://api.checkout402.com/c/chk_abc123/prepare \
-H "content-type: application/json" \
-d '{"payer": "0xYourAddress", "network": "eip155:8453"}'
It returns typed_data to sign and a payload_stub to fill in. The server
signs nothing and holds no key.
Program-less: there's no splitter, and payTo names the seller.
Build ONE transaction with exactly TWO transfer_checked instructions —
extra.providerAmount to the seller's ATA, extra.feeAmount to
extra.feeRecipient's ATA. Sign as payer, leave the fee-payer slot empty:
{ "transaction": "<base64>", "payer": "<your pubkey>" }
The relayer co-signs as fee payer, so you need no SOL.
3. Retry with the payment#
curl https://api.checkout402.com/c/chk_abc123 \
-H "X-PAYMENT: $(echo -n '{"network":"eip155:8453","payload":{...}}' | base64)"
200 with the goods, and X-PAYMENT-RECEIPT carrying your receipt.
Failure modes#
| Status | Meaning | Do this |
|---|---|---|
402 |
Not paid, or the payment was rejected | Read detail.help — it names the fix |
409 |
Replay of a settled authorization | You already have the goods; re-present to redeliver |
502 |
Settlement failed on-chain | Re-read the 402 and sign fresh; windows expire |
Every 402 body carries a help block with the exact next call. It's written for you to act on, not for a human to read.
Verifying a receipt without trusting us#
The receipt names a transaction hash. Check it against the chain yourself — the amounts, the seller, and the payer are all on-chain. You never have to take our word for a payment.