Payments with x402
Pay for an HTTP resource per request with the open x402 protocol on Tenzro Network: read the 402 challenge, send a signed credential, check the receipt, and sell your own paid routes.
x402 is an open protocol that puts the HTTP 402 Payment Required status to work. A client asks for a resource, the server answers with a price, the client retries with a signed payment, and the server returns the resource with a receipt. There are no accounts or subscriptions, which makes it a good fit for agents and machines that buy one call at a time. Every paid route on a Tenzro node, including the OpenAI-compatible inference APIs, accepts x402.
Prerequisites
- A wallet with TNZO or stablecoins: a passkey wallet from the console, or a delegated agent wallet from Build an AI payment agent.
curlandjq.- Background: x402.
1. Ask for a paid resource
Call a paid route with no credential. Here it is chat completions:
curl -si https://rpc.tenzro.com/v1/chat/completions \
-H 'content-type: application/json' \
-d '{"model":"qwen3.6-35b-a3b","messages":[{"role":"user","content":"hello"}]}'The node answers with a challenge. The WWW-Authenticate header carries it in the IETF Payment auth-scheme form, the PAYMENT-REQUIRED header carries the x402 payment requirements, and the body carries the same challenge as JSON for clients that want the details:
HTTP/1.1 402 Payment Required
WWW-Authenticate: Payment id="ch_7k9m2p4q8r", realm="/v1/chat/completions", method="x402", intent="charge", request="eyJjaGFsbGVuZ2VfaWQiOi...", expires="2026-10-02T12:35:00Z"
PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6Mi...
Content-Type: application/json{
"challenge_id": "ch_7k9m2p4q8r",
"protocol": "x402",
"resource": "/v1/chat/completions",
"amount": 100,
"asset": "USDC",
"recipient": "0x<provider-wallet>",
"chain": "<caip2>",
"expires_at": "2026-10-02T12:35:00Z",
"extra": { "scheme": "tenzro-hybrid" }
}amount is in the smallest unit of asset. extra.scheme names how the payment will be verified.
2. See which schemes the resource accepts
The schemes, networks and assets a resource accepts are listed in the PAYMENT-REQUIRED header of its 402 answer. The rails a payment can settle on, and the smallest worthwhile payment on each, come from the open method tenzro_settlementNetworks:
curl -s https://rpc.tenzro.com \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tenzro_settlementNetworks","params":{}}' | jq .| Scheme | How the payment is authorised |
|---|---|
tenzro-hybrid | A hybrid classical and ML-DSA-65 signature from the payer's account over the canonical payment message. Quantum-safe. |
exact-eip3009 | An EIP-3009 transferWithAuthorization for stablecoins on EVM chains. |
permit2 | A signed Permit2 transfer for ERC-20 tokens. |
erc7710 | Redemption of an ERC-7710 delegation from a smart account. |
3. Send a signed credential
The client answers the challenge with a credential that names the challenge, the payer, the amount and the asset, signed by the payer's key. With the tenzro-hybrid scheme the signature is composite: a P-256 leg and an ML-DSA-65 leg over the payment authorization. Your wallet produces the signatures inside its hardware; the credential never contains a private key.
{
"credential_id": "cr_2f61c0",
"challenge_id": "ch_7k9m2p4q8r",
"protocol": "x402",
"payer_did": "did:tenzro:machine:<controller>:<agent-id>",
"payer_address": "0x<agent-wallet>",
"amount": 100,
"asset": "USDC",
"signature": {
"classical": { "form": "...", "signature": "...P-256 signature..." },
"pq": "...ML-DSA-65 signature..."
},
"extra": { "public_key": "...payer's P-256 public key..." }
}Base64-encode the JSON and retry the same request with it in the Payment-Credential header. (An x402 client that builds an x402 payment payload sends it in PAYMENT-SIGNATURE instead; payingFetch does that for you.)
CRED=$(base64 -w0 < credential.json)
curl -si https://rpc.tenzro.com/v1/chat/completions \
-H 'content-type: application/json' \
-H "Payment-Credential: $CRED" \
-d '{"model":"qwen3.6-35b-a3b","messages":[{"role":"user","content":"hello"}]}'Before it verifies the payment, the node checks the payer's identity: a suspended or revoked DID is refused, and a delegated agent's payment must fit its delegation scope (amount, daily cap, protocol and chain).
In application code you do not build credentials by hand: signChallengeCredential(challenge, signer, payerDid, maxAmount) from the web module of tenzro-sdk signs the credential with the paying account's hardware signer, and client.payment.verifyPayment({ credential }) checks it against the challenge without settling. payingFetch with a HybridPayer does the whole round trip for any paid URL; see x402 and Build an AI payment agent.
4. Read the receipt
On success the node settles the payment, serves the request and adds a PAYMENT-RESPONSE header: base64-encoded JSON describing the settlement.
curl -si ... | grep -i '^payment-response' | cut -d' ' -f2 | base64 -d | jq .Every settled payment is a consensus record, retrievable by its reference or by the payment id it settled under, which is useful for reconciliation:
curl -s https://rpc.tenzro.com \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tenzro_getPayment","params":{"pay_ref":"<pay-ref>"}}'The record names the payer, payee, asset, amount and settlement fee. tenzro payment history --payer 0x<address> lists your payments in chain order.
5. Handle failures
| Response | Meaning | What to do |
|---|---|---|
402 with a new challenge | The challenge expired or no credential was sent | Pay the new challenge |
400 invalid_credential | The header could not be decoded or parsed | Check the base64 and the JSON fields |
401 verification_failed | The signature, amount or payer did not verify, or the payer's identity or delegation scope refused it | Sign with the key bound to payer_address, pay the exact amount, or raise the agent's limits |
Before paying a new challenge after a network error, check whether the earlier payment landed with tenzro payment history --payer 0x<address>, so you do not pay twice.
6. Sell your own resource over x402
Any paid route you host can charge over x402: answer an unpaid request with 402 Payment Required and the payment requirements in the PAYMENT-REQUIRED header, naming the scheme, network, asset, payee and amount. PublisherGate from the web module of tenzro-sdk does this for a list of paid routes and serves each request only once a facilitator has settled it; see x402.
To issue a single challenge for a resource from the command line:
tenzro payment challenge /tenzro/forecasts --amount 100 --asset USDC --protocol x402 \
--recipient 0x<your-address> --rpc https://rpc.tenzro.comtenzro_settlementNetworks shows the rails a payment can settle on and the smallest worthwhile payment on each.
Next steps
- Sessions and streamed billing: Payments with MPP.
- Stablecoins end to end: Pay for inference in stablecoins.
- Protocol reference: x402.