checkout402_
Guides

Sell an API#

You have an endpoint. You want to be paid per call, by buyers you will never meet, without issuing anyone an API key.

That is a relay checkout. We put a paywall in front of your URL: the buyer pays us, we call you, and we hand back exactly what you returned.

buyer ──pays──▶ checkout402 ──calls──▶ your endpoint
      ◀──your response──────┘

Your URL never reaches the buyer. Neither does your upstream credential.


Make one#

Create a checkoutAn API endpoint → paste your URL and a price. Everything else has a working default.

c402 checkouts create \
  --title "Company financials" \
  --price 0.10 \
  --origin-url https://api.yourco.com/v1/financials

Create a checkout for https://api.yourco.com/v1/financials at 10 cents a call.

You do not need to say "relay" — a URL with no mcp_tool is one.

curl -X POST https://api.checkout402.com/v1/checkouts \
  -H "Authorization: Bearer c402_live_…" \
  -H "content-type: application/json" \
  -d '{"title": "Company financials",
       "price_usd": "0.10",
       "origin_url": "https://api.yourco.com/v1/financials"}'

You get back one URL. That URL is the product.

You did not have to name the mode

origin_url means relay. payload means inline. Adding mcp_tool means mcp. The mode is inferred from what you send, because a seller thinks "I am selling this for 10 cents", not "this is a relay checkout".


What the buyer experiences#

The same URL has two faces.

A human opening it in a browser gets a pay page: your title, your price, the fee broken out, and a wallet button.

An agent requesting it as JSON gets 402 Payment Required with everything needed to pay in one round trip — then repeats the request with an X-PAYMENT header and receives your endpoint's response.

{ "fulfillment": { "mode": "relay",
                   "content_type": "application/json",
                   "payload": {  your endpoint's body  } },
  "receipt": { "id": "pay_…", "url": "https://…/r/pay_…", "tx_hash": "0x…" } }

No account, no signup, no key issued by you. The payment is the authorisation.


Passing parameters#

Buyers rarely want the same answer twice. Query parameters pass straight through to your endpoint:

GET /c/chk_abc123?symbol=ACME&year=2026
      ↓
GET https://api.yourco.com/v1/financials?symbol=ACME&year=2026

You can also template the path:

c402 checkouts create --title "Country profile" --price 0.05 \
  --origin-url 'https://api.yourco.com/countries/{iso3}/profile'

A buyer calling ?iso3=CHE reaches /countries/CHE/profile. Placeholder values are percent-encoded, and scheme and host can never come from a parameter — a buyer cannot redirect the call somewhere else.

A missing placeholder is the buyer's mistake, and it is charged

If your URL has {iso3} and the buyer sends nothing, the call fails before we ever reach you — and it still costs them a call. That is deliberate: see who pays when a call fails.

POST endpoints#

Plenty of data APIs are POST — GraphQL, search, bulk lookup. A relay that could only GET could not wrap them.

c402 checkouts create --title "Search" --price 0.02 \
  --origin-url https://api.yourco.com/search --origin-method POST

The buyer's request body arrives as the reserved body param: one JSON string, signed with their payment like any other param, forwarded to your origin verbatim as application/json. The 402 tells them so (endpoint.request_body), so an agent buying a GraphQL query sends

body={"query": "{ company(uid: \"CHE-1\") { revenue } }"}

and your API receives exactly that as its POST body — the payment is bound to that exact body, so it cannot be swapped after signing. Malformed JSON and bodies over 8,000 characters are refused before your origin is dialled, and charged as the buyer's own error. PUT, PATCH and DELETE origins work the same way (DELETE carries no body).


Wrapping an endpoint that needs a key#

This is the part that makes relay worth using. Your API can answer 401 to the entire internet; the buyer pays instead of holding a credential.

c402 checkouts create --title "Financials" --price 0.10 \
  --origin-url https://api.yourco.com/v1/financials \
  --origin-auth-type bearer --origin-auth-value "$UPSTREAM_KEY"
c402 origin-keys add --auth-type bearer --value "$UPSTREAM_KEY" --name prod
c402 origin-keys list                      # note the id
c402 checkouts create --title "…" --price 0.10 \
  --origin-url https://api.yourco.com/v1/x --origin-key <key-id>

Rotate it once and every checkout using it follows: c402 origin-keys rotate <key-id> --value "$NEW_KEY"

The credential is encrypted at rest, injected server-to-server at call time, and never appears in anything a buyer can see — not in the response, not in a header, not in an error.

The one thing to get right

Wrap an endpoint you control, at a price that covers what the call costs you. A relay checkout will happily call an expensive upstream a thousand times if a thousand people pay for it.


Selling calls in bulk#

calls: N sells N calls for one payment. One on-chain settlement, then a pk_… key that spends a counter — no signature, no gas, no confirmation wait on any call after the first.

c402 checkouts create --title "500 lookups" --price 25.00 \
  --origin-url https://api.yourco.com/v1/financials --calls 500
Minimum price $1.00 when calls > 1
Expiry 30 days, --package-ttl-days to change it
Rate 60 calls/minute per key by default

Why the minimum: settling on-chain costs about a cent regardless of size. A package settles once and covers N calls, which is the only way per-call prices below a cent work at all. A single call has no minimum, deliberately — a cheap one is how a buyer tries your API before committing.

Why the rate limit: you sold 500 calls, not 500 calls per second. Without it a package is a loaded gun pointed at your own endpoint.

See Packages for the full model.


When a call fails#

Not every failure is refunded, and the rule is whose fault it was.

Your endpoint returns Buyer charged Buyer sees
5xx, timeout, unreachable No 502
401, 403 — your credential is broken No 502
429 — your capacity No 502
400, 404, 422 — their request was wrong Yes your own status code

The asymmetry is the point. Refunding a bad request would make "does record X exist?" free whenever the answer is no — buyers could enumerate your dataset at no cost while your endpoint absorbed every probe, paying only for hits.

Every call is logged with counted and fault, so a buyer can always answer "was I charged for that one, and whose fault was it" — and so can you.

You are not billed for your own outage

A seller-fault failure hands the call back to the buyer automatically. You do not have to do anything, and it does not count against your delivery record as their mistake.


Before you go live#

A relay checkout has one dependency you do not control at purchase time: your own endpoint. Two things follow.

Rehearse on the test face. Every checkout carries one at /c/{id}/test — same product, same call to your origin, simulated settlement. Point a script at it, confirm the response is what you expect, then share the bare URL.

c402 checkouts create --title "Financials" --price 0.10 \
  --origin-url https://api.yourco.com/v1/financials   # created unarmed
c402 checkouts go-live chk_abc123                     # arms it — same id, same URL

Arming changes nothing about the rehearsal: the /test face keeps settling with no money, forever, and can never be armed. Origin-backed rehearsals draw on a daily budget (25 per checkout by default, test_delivery_daily_cap to change it), so a rehearsal loop cannot hammer your endpoint for free.

Say what the buyer gets. An agent decides whether to buy from the offer card, and a checkout with no description is one they cannot evaluate:

c402 checkouts update chk_abc123 \
  --description "Latest filed financials for a Swiss registered company" \
  --freshness "daily" --coverage "all 26 cantons" --source "commercial register"

Those are commercial claims

freshness, coverage and source are assertions a buyer relies on. If an agent is filling them in for you, it should be proposing them, not deciding them.


Getting paid#

The money never touches us. Payment settles buyer → your wallet in one on-chain transaction, with the fee split out in the same transaction.

0.5% + $0.01, charged on top of your price. You always receive your full price.

Your price Buyer pays You receive
$0.10 $0.1105 $0.10
$25.00 $25.135 $25.00

Set a payout wallet per chain and every rail you can be paid on is offered: Payout wallets.


What next#