Sell an API#
Put an endpoint you already have behind a per-call paywall, without changing it.
Your existing subscriptions, API keys and enterprise contracts stay exactly as they are. This adds a new purchasing surface for ad-hoc agent demand — buyers who will never sign up, but will pay $0.10 right now.
Relay fulfilment#
relay is the mode that matters here: checkout402 settles the payment, then calls your endpoint and forwards its response to the buyer.
c402 checkouts create \
--title "Swiss company financials" \
--description "Latest filed financials for a Swiss registered company." \
--price 0.10 \
--pay-to 0xYourPayoutWallet \
--fulfillment relay \
--origin-url https://api.yourco.com/v1/financials
Your API never learns about checkout402. It gets a normal request and returns a normal response.
Your origin is validated at creation AND at every call
A host that resolves to a public address today can resolve to a link-local one tomorrow, so the SSRF check runs on every forward, not just once.
Wrapping an endpoint that needs a key#
Most real APIs answer 401 without a credential. Give checkout402 your
upstream key and it authenticates server-to-server on each forward — the buyer
pays instead of holding a key, and never sees yours.
c402 checkouts create \
--title "Company financials" --price 0.10 --pay-to 0x... \
--fulfillment relay --origin-url https://api.yourco.com/v1/financials \
--origin-auth-type bearer --origin-auth-value "$YOUR_UPSTREAM_KEY"
--origin-auth-type |
Sent upstream as |
|---|---|
bearer |
Authorization: Bearer <value> |
basic |
Authorization: Basic <value> |
api_key / custom |
<--origin-auth-header>: <value> |
The credential is encrypted at rest (Fernet) in its own table; the checkout row
holds only a reference. It is never returned by any endpoint, never appears in a
402, a receipt or the pay page, and is never logged.
POST, PUT and PATCH endpoints#
Many data APIs aren't GET — GraphQL, search, bulk lookup. Tell the checkout
which method your endpoint speaks:
c402 checkouts create --title "Company search" --price 0.05 --pay-to 0x... \
--fulfillment relay --origin-url https://api.yourco.com/v1/search \
--origin-method POST
GET | POST | PUT | PATCH. Anything else is a 400 rather than a silent
downgrade to GET.
One key, many endpoints#
Passing --origin-auth-value stores a fresh encrypted copy per checkout. If you
have seventy endpoints behind one API key, that is seventy copies and seventy
edits when it rotates.
Store it once instead:
c402 origin-keys add --auth-type bearer --value "$UPSTREAM_KEY" --name "Zefix production"
# Stored 3f1c…. Use it with: c402 checkouts create --origin-key 3f1c… ...
c402 checkouts create --origin-key 3f1c… --fulfillment relay --origin-url ... \
--title "Company profile" --price 0.05 --pay-to 0x...
Then rotation is one call, and every checkout picks up the new value on its next forward:
c402 origin-keys rotate 3f1c… --value "$NEW_UPSTREAM_KEY"
c402 origin-keys list # shows how many checkouts use each key
Revoking tells you what it breaks rather than letting you find out from a buyer.
Keys are account-scoped
You can only reference credentials your own account stored. Someone else's
key id returns 404 — not 403, which would confirm it exists.
This key spends your quota
Everyone who buys the checkout is drawing on your upstream allowance. Price for that, and prefer a scoped or rate-limited upstream key over your account-wide one.
Describe it well enough to sell#
A bare title and price will get you some sales. An offer card gets you more, because an agent can tell whether your data fits its task before spending.
c402 checkouts create \
--title "Swiss company financials" --price 0.10 --pay-to 0x... \
--fulfillment relay --origin-url https://api.yourco.com/v1/financials \
--input-schema '{"company_uid": "string"}' \
--output-schema '{"company_name": "string", "revenue": "number", "employees": "number"}' \
--example-input '{"company_uid": "CHE-123.456.789"}' \
--freshness "updated daily" \
--coverage "all 26 cantons" \
--source "Zefix commercial register"
Schemas and examples accept @file.json — real ones outgrow a shell argument:
c402 checkouts create --output-schema @schema.json --example-output @sample.json ...
Only publish claims you can stand behind
freshness, coverage and source are commercial assertions. A buyer will rely on them, and an agent reading your card can't tell an aspiration from a fact.
Pricing#
Set the price you want. The fee is added on top and shown separately, so the buyer sees both numbers and you receive exactly what you asked for.
| You set | Buyer pays | You receive |
|---|---|---|
$0.10 |
$0.105 |
$0.10 |
$1.00 |
$1.005 |
$1.00 |
$49.00 |
$49.245 |
$49.00 |
Sub-cent prices work — the floor is $0.005, not a rounded cent.
Two things you can sell#
Every checkout is one of two products. The difference is calls.
| Single call | Package | |
|---|---|---|
| What the buyer gets | One response | N responses |
| Settlements on-chain | One per purchase | One, total |
| Minimum price | None | $1.00 |
| How to create it | omit calls |
calls: N |
c402 checkouts create --title "Company financials" --price 0.10
c402 checkouts create --title "Financials, 500 lookups" --price 25.00 --calls 500
Create → pick A single call or A package of calls.
{"title": "…", "price_usd": "0.10"}
{"title": "…", "price_usd": "25.00", "calls": 500}
Why a package has a floor and a single call doesn't#
Settling on-chain costs us gas, once per payment. A single call pays that cost once and can therefore be priced at almost nothing — which is the point, because a cheap single call is the trial that leads to a larger purchase.
A package amortises one settlement over many calls. That is how a per-call price below a cent actually works: 500 calls for $25.00 is $0.05 each, with one settlement rather than 500. Below $1.00 there is nothing to amortise, so a package priced under it is refused — at that size the buyer is genuinely better served by single calls.
max_deliveries is the old name
The API field was originally max_deliveries and still works. calls is
the name every surface now uses; prefer it.
Getting paid#
Money lands in pay_to on-chain, in the same transaction that takes the fee. There's no balance, no payout schedule, and no withdrawal request, because checkout402 never holds it.
Check your payout address character by character
pay_to is validated as a real address for a supported chain, so a typo
that mangles the format is rejected. But a well-formed and wrong address
is indistinguishable from a correct one, and on-chain payments cannot be
reversed. Nothing can save you from a valid address that isn't yours.
Checking it once beats checking it every time: set a
payout wallet on your account and pay_to becomes
optional, so there is one address to get right instead of one per checkout.
Your payout address decides which rails you're offered
An address belongs to one chain family. An EVM 0x… wallet is offered Base;
a Solana pubkey is offered Solana. A checkout is never advertised on a rail
its payout address could not receive on.
Test first, then go live#
c402 keys create --name dev --mode test # test keys can't move real money
c402 checkouts create --test ... # settles with a synthetic receipt
When it works end to end, mint a --mode live key and drop --test. Nothing else changes.
Selling many endpoints#
Don't create sixty checkouts by hand. Point Claude at your API and let it read your routes and docs — see Connect Claude.
Or loop the CLI, since every command speaks JSON:
jq -c '.[]' products.json | while read -r p; do
c402 checkouts create --json \
--title "$(jq -r .title <<<"$p")" \
--price "$(jq -r .price <<<"$p")" \
--origin-url "$(jq -r .url <<<"$p")" \
--fulfillment relay --pay-to "$PAYOUT"
done
Knowing when you've been paid#
Poll GET /v1/checkouts, or register a webhook and be told — see Webhooks.