Skip to content
Tenzro
โ† All tutorials
Tutorial ยท Payments

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.

Intermediate20 min

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

1. Ask for a paid resource

Call a paid route with no credential. Here it is chat completions:

bash
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
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:

bash
curl -s https://rpc.tenzro.com \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tenzro_settlementNetworks","params":{}}' | jq .
SchemeHow the payment is authorised
tenzro-hybridA hybrid classical and ML-DSA-65 signature from the payer's account over the canonical payment message. Quantum-safe.
exact-eip3009An EIP-3009 transferWithAuthorization for stablecoins on EVM chains.
permit2A signed Permit2 transfer for ERC-20 tokens.
erc7710Redemption 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.

json
{
  "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.)

bash
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.

bash
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:

bash
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

ResponseMeaningWhat to do
402 with a new challengeThe challenge expired or no credential was sentPay the new challenge
400 invalid_credentialThe header could not be decoded or parsedCheck the base64 and the JSON fields
401 verification_failedThe signature, amount or payer did not verify, or the payer's identity or delegation scope refused itSign 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:

bash
tenzro payment challenge /tenzro/forecasts --amount 100 --asset USDC --protocol x402 \
  --recipient 0x<your-address> --rpc https://rpc.tenzro.com

tenzro_settlementNetworks shows the rails a payment can settle on and the smallest worthwhile payment on each.

Next steps

ExploreJoin the network