Payments and x402
Payment is part of the request. There is no account to create, no key to rotate and no invoice to reconcile: the wallet that signs the payment is the identity, and the answer comes back in the same round trip.
x402 revives the HTTP status code that was reserved for this in 1997 and never used. An unpaid request is answered with the price. A paid one is answered with the resource.
The handshake
- 1Call the endpoint with no payment
Send the request you actually want to make. Nothing is charged and nothing is executed.
shellcurl -i -X POST https://www.tereno.xyz/api/v1/capabilities/prediction.evidence/invoke \ -H "Content-Type: application/json" \ -d '{"question":"Will Ethereum Pectra activate on mainnet before Q4 2026?"}' - 2Read the terms from the header
The server answers
402. The terms are in thepayment-requiredheader, not in the body: chain, asset, amount in base units, and the address to pay.payment-required (decoded){ "x402Version": 2, "resource": { "url": ".../prediction.evidence/invoke" }, "accepts": [{ "scheme": "exact", "network": "eip155:8453", "amount": "50000", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0xfbF218ACCBb0D57fc4E8fc3E2A885C910c4C9e94", "maxTimeoutSeconds": 300 }] } - 3Sign an EIP-3009 authorization and retry
Sign a USDC transfer authorization for exactly that amount and attach it as
payment-signature. Any x402 client library does this for you; the TypeScript client is on the command line page. - 4Payment settles, then the answer is released
Settlement happens before the response leaves. The confirmation comes back in the
payment-responseheader with the transaction hash.
What you are paying with
| Value | |
|---|---|
| Protocol | x402, scheme exact, EIP-3009 transferWithAuthorization |
| Chain | Base mainnet, eip155:8453 |
| Asset | USDC, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| Amount | Per call, from $0.001. Set by the capability and its cache tier. |
| Account needed | None. A funded wallet is the whole onboarding. |
Know the price before you sign
The live 402 challenge is always authoritative, but you do not have to trigger one to find out what something costs. Two free reads answer that:
GET /api/v1/capabilitieslists every capability with its price tiers. Free, no wallet.GET /api/v1/quotesreturns a wallet-bound quote: the exact price for you, including any credit you have earned, plus the invoke URL and method.
402 are right and the prose is stale.Why the same call has two prices
A capability that can be shared charges less when it serves a stored answer. The response always tells you which tier you were charged, so this is never ambiguous after the fact:
"pricing": { "listPriceUsd": 0.05, "pricePaidUsd": 0.01, "currency": "USDC" },
"artifact": { "cacheStatus": "hit", "expiresAt": "2026-09-20T15:50:40.320Z" }cacheStatus is miss when you paid for fresh work, hit when you were served a stored answer, and partial when only part of the answer could be reused.
One payment, one execution
Idempotency is keyed to the signed payment authorization itself, never to a header a caller can choose. Replaying the same authorization cannot buy a second execution.
- A retry while the first call is still running returns
409withalreadySettled: false. - A retry after it settled returns
409withalreadySettled: trueand the receipt URL of the execution it bought, so a client that lost the response can still collect what it paid for. - A request that fails before the work is done is never charged.
What a receipt proves, and what it does not
Every paid answer carries a deterministic receipt and a content address. Both are reproducible: same inputs, same hashes.
| Field | Means |
|---|---|
invocationId | This specific purchase. Fetch it later at GET /api/v1/receipts/{invocationId}. |
artifactId | The content address of the answer: sha256 over capability, version, normalized input and output. |
receipt.hash | Commits to the question, the observation time and the capability version. |
expiresAt | The moment this answer stops being claimed as current. After it, the answer is history, not a fact. |
Free reads around a paid answer
If another agent hands you an artifactId, you can check where it came from without paying and without any relationship to Tereno:
curl -s https://www.tereno.xyz/api/v1/evidence/sha256:1bf295935c1cfca0f107cd193f345b7d...That returns origin, declared validity and the exact normalized input needed to reproduce the work. It never returns payer identity or the original paid output.
