c402 — the seller CLI#
Run your checkouts from a terminal. Four ways to sell exist and they all hit the
same API: the web page at /create, the REST API, the MCP tools, and this.
c402 sells. pay402 buys. Neither needs the other.
Written 2026-07-28. Source: clients/c402.py.
Install#
There is nothing to install. It is one stdlib-only file:
python clients/c402.py --help
Put it on your PATH if you want the short name:
ln -s "$PWD/clients/c402.py" /usr/local/bin/c402 && chmod +x /usr/local/bin/c402
The single optional dependency is eth-account, needed only for
login --wallet. Every other command runs on a bare Python install — an agent
that has to resolve a dependency tree before it can create a checkout will not
create the checkout.
Sign in#
Three ways in. Pick by what you are.
An agent, with a wallet — no email, no browser#
c402 login --wallet $AGENT_PRIVATE_KEY
Signs a challenge, proves control of the address, and mints an automation key in one round trip. The signing wallet is registered as your verified payout address. This is the fast path and the one to reach for in automation.
Use --wallet - to be prompted instead of putting a key in your shell history.
A human, with an email#
c402 login --email you@company.com
A CLI has no inbox, so this sends the link and stops. Open it, then
c402 keys create and paste the result:
c402 login --key c402_live_…
CI, with a key already in the environment#
export CHECKOUT402_API_KEY=c402_live_…
c402 whoami # no login step at all
Where the key lives#
~/.checkout402/config.json, mode 0600. It can create live checkouts, so it is
never world-readable and the CLI never prints it back in full.
c402 logout forgets it locally. That is not the same as revoking it — if a
key may have leaked, c402 keys revoke <id> is the one that matters.
Precedence: --key › CHECKOUT402_API_KEY › the config file.
Same shape for the API base: --base › CHECKOUT402_BASE_URL › config ›
https://api.checkout402.com.
Profile#
c402 whoami
c402 profile show
c402 profile set --name "Klaus Kopper" \
--bio "Swiss company data, priced per call." \
--website-url https://checkout402.com
Editable: --name, --bio, --website-url, --avatar-url, --slug,
--profile-type (individual | team | company).
Pass an empty string to clear a field. Omitting it leaves it alone.
--slug is your public handle. Changing it breaks URLs anyone has already
saved, so claim a good one early. A taken handle is a clean 409.
verified is not settable — it is a manual review, never self-serve.
Checkouts#
Create#
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}'
Prints the URL. Share it: a human gets a pay page, an agent gets a 402. Same URL.
Fulfilment — what the buyer receives after settlement:
--fulfillment |
What happens | Needs |
|---|---|---|
inline (default) |
--payload returned verbatim |
--payload |
relay |
your API is called, response forwarded | --origin-url |
webhook |
receipt only, you're notified async | a registered webhook |
Offer card — optional, and the difference between an agent buying and an agent moving on. It answers what do I provide, what will I get, is it suitable before any money moves:
c402 checkouts create --title "…" --price 0.10 --pay-to 0x… \
--input-schema '{"company_uid": "string"}' \
--output-schema '{"revenue": "number", "employees": "number"}' \
--example-input '{"company_uid": "CHE-123.456.789"}' \
--freshness "updated daily" \
--coverage "all 26 cantons" \
--source "Zefix commercial register" \
--origin-url https://api.yourco.com/v1/financials \
--fulfillment relay
Any JSON flag accepts @file.json — real payloads and schemas outgrow a shell
argument:
c402 checkouts create --payload @goods.json --output-schema @schema.json …
Only publish claims you can stand behind.
freshness,coverageandsourceare commercial assertions a buyer will rely on. If an agent is filling these in on your behalf, it should be proposing them, not deciding them.
List, show, update#
c402 checkouts list
c402 checkouts show chk_abc123
c402 checkouts update chk_abc123 --coverage "all 26 cantons" --freshness "hourly"
update changes --title, --description, and the offer-card fields.
It deliberately cannot change --price or --pay-to: those alter what a
buyer may already have been quoted, so they need a decision, not a flag. Create a
new checkout instead.
An unknown field is a 422, not a silent no-op — the CLI will not tell you a
change landed when it didn't.
Keys#
c402 keys create --name ci --mode test # test is the default
c402 keys list
c402 keys revoke <key-id>
A key is shown once. Modes are real: a c402_test_… key drives every
endpoint but cannot move real money, and pairing a live key with --test is a
400 rather than a surprise.
New keys default to test on purpose. A key minted by a one-liner should not be
able to spend.
For agents#
Every command takes --json and prints the raw API object:
c402 checkouts list --json | jq -r '.checkouts[] | select(.status=="open") | .url'
Exit codes: 0 success, 1 a stated error (printed to stderr, no traceback),
2 bad arguments, 130 interrupted.
Bulk creation is a shell loop, no bulk endpoint required:
jq -c '.[]' products.json | while read -r p; do
c402 checkouts create --json \
--title "$(jq -r .title <<<"$p")" \
--price "$(jq -r .price <<<"$p")" \
--pay-to "$PAYOUT" \
--origin-url "$(jq -r .url <<<"$p")" --fulfillment relay
done
For an agent working inside your repo, the MCP surface at /mcp is usually the
better tool — it already knows your routes and schemas. See
CLAUDE_CREATE.md.
Troubleshooting#
| Message | Meaning |
|---|---|
not signed in |
No key found. c402 login … or set CHECKOUT402_API_KEY. |
a live key can only create live-mode resources |
Drop --test, or use a test key. |
cannot reach … |
Wrong --base, or the service is down. |
--wallet needs eth-account |
pip install eth-account, or log in with --key. |
handle '…' is taken |
Someone owns that slug. Pick another. |
422 … extra_forbidden |
You passed a field that command doesn't accept. |