Quickstart#
From nothing to a checkout an agent can pay, in about five minutes. Everything below creates an unarmed checkout — it rehearses with simulated settlement, so no real money moves until you arm it.
1. Get a key#
c402 login --wallet $AGENT_PRIVATE_KEY # signs a challenge, no email needed
c402 keys create --name quickstart --mode test
# Sign in at https://checkout402.com/login, then from the browser session:
curl -X POST https://api.checkout402.com/v1/keys \
-H "content-type: application/json" \
-d '{"name": "quickstart", "mode": "test"}'
The key is shown once. Store it now.
2. Create a checkout#
c402 checkouts create \
--title "Swiss company financials" \
--description "Latest filed financials for a Swiss registered company." \
--price 0.10 \
--pay-to 0xYourPayoutWallet \
--payload '{"company_name": "Example AG", "revenue": 12500000}' \
--test
curl -X POST https://api.checkout402.com/v1/checkouts \
-H "Authorization: Bearer $C402_KEY" \
-H "content-type: application/json" \
-d '{
"title": "Swiss company financials",
"description": "Latest filed financials for a Swiss registered company.",
"price_usd": "0.10",
"pay_to": "0xYourPayoutWallet",
"payload": {"company_name": "Example AG", "revenue": 12500000},
"test_mode": true
}'
import httpx
r = httpx.post(
"https://api.checkout402.com/v1/checkouts",
headers={"Authorization": f"Bearer {KEY}"},
json={
"title": "Swiss company financials",
"price_usd": "0.10",
"pay_to": "0xYourPayoutWallet",
"payload": {"company_name": "Example AG", "revenue": 12500000},
"test_mode": True,
},
)
print(r.json()["url"])
You get back a URL:
{
"id": "chk_aiQQt5DHCTZMSIul",
"url": "https://api.checkout402.com/c/chk_aiQQt5DHCTZMSIul",
"test_url": "https://api.checkout402.com/c/chk_aiQQt5DHCTZMSIul/test",
"status": "open",
"test_mode": true
}
That URL is the product. Share it anywhere. test_mode: true means it is
not armed yet: payments rehearse — same flow, simulated settlement, no
funds — until you go live in step 5. test_url is the rehearsal address it
keeps forever, even after arming.
3. See both faces#
Open it in a browser and you get a pay page. Ask for JSON and you get the machine face:
curl https://api.checkout402.com/c/chk_aiQQt5DHCTZMSIul
{
"x402Version": 1,
"accepts": [{
"network": "eip155:8453",
"maxAmountRequired": "110500",
"payTo": "0x234fE2A1D1df93fD9B907F0e1ad0c8b94E81FB5c",
"extra": { "providerAmount": "100000", "feeAmount": "10500" }
}],
"endpoint": {
"title": "Swiss company financials",
"description": "Latest filed financials for a Swiss registered company."
}
}
HTTP status is 402. An agent can read the price, the fee, and what it's buying — before spending anything. The seller asked $0.10 and keeps $0.10; the $0.0105 fee (0.5% plus a fixed $0.01) is added on top, so the buyer pays $0.1105.
4. Pay it#
from pay402 import pay
goods = pay(
"https://api.checkout402.com/c/chk_aiQQt5DHCTZMSIul",
private_key=os.environ["BUYER_KEY"],
max_usd="1.00", # required for live payments
)
print(goods.payload) # {'company_name': 'Example AG', 'revenue': 12500000}
print(goods.receipt.url) # https://api.checkout402.com/r/pay_...
That one call does the whole loop: request, read the 402, sign an authorization, retry with X-PAYMENT, and hand back the goods plus a receipt.
max_usd is not optional for live payments
An agent looping on a paid endpoint with no ceiling is the failure that matters. pay() refuses a live payment without max_usd rather than defaulting to permissive.
5. Go live#
One command:
c402 checkouts go-live chk_aiQQt5DHCTZMSIul
That arms the checkout in place — same id, same URL, idempotent. The link
you already shared is the one that now settles real USDC on Base, splits the
fee on-chain, and writes a receipt. Rehearsal doesn't stop: the /test face
keeps settling with no money, forever, and can never be armed — point your
scripts there, share the bare URL.
(A live key — c402 keys create --mode live — creates checkouts that are
born armed, for when you no longer want the rehearsal step.)
Check your payout address before you go live
pay_to is where the money lands, and on-chain payments are irreversible. Verify it character by character — a wrong-but-valid address sends funds somewhere unrecoverable.
Or verify it once: set a payout wallet on your account
and you can drop pay_to from every request above. No wallet yet?
c402 wallets new generates one on your machine.
What next#
- Concepts — the object model, in one page
- Sell an API — relay fulfilment, so buyers hit your real endpoint
- Payout wallets — set where you get paid once, per chain
- Connect Claude — let an agent create checkouts across your whole API