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 1: 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, subscriptions or API keys, 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.xyz/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, 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: true
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 node verifies

bash
curl -s https://rpc.tenzro.xyz \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tenzro_listX402Schemes","params":[]}'
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 credential carries both signatures and the payer's ML-DSA-65 public key. 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 signature bytes..." ],
  "pq_signature": [ "...ML-DSA-65 signature bytes..." ],
  "pq_public_key": [ "...ML-DSA-65 public key bytes..." ],
  "extra": { "scheme": "tenzro-hybrid" }
}

Base64-encode the JSON and retry the same request with it in the Authorization header:

bash
CRED=$(base64 -w0 < credential.json)

curl -si https://rpc.tenzro.xyz/v1/chat/completions \
  -H 'content-type: application/json' \
  -H "Authorization: x402 $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. The @tenzro/ai SDK does steps 1 to 3 for every call when you pass a signer and payment: { protocol: "x402", maxPrice }; see Build an AI payment agent.

4. Read the receipt

On success the node settles the payment, serves the request and adds an X-PAYMENT-RESPONSE header: base64-encoded JSON describing the settlement.

bash
curl -si ... | grep -i '^x-payment-response' | cut -d' ' -f2 | base64 -d | jq .

Receipts are also retrievable by id, which is useful for reconciliation:

bash
curl -s https://rpc.tenzro.xyz \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tenzro_getPaymentReceipt","params":[{"receipt_id":"<receipt-id>"}]}'

A receipt records the protocol, challenge and credential ids, amount, asset, chain, the settlement transaction and the time, plus the chain of principals behind the payer: for an agent, the human or organisation that controls it.

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

Retrying a request after a network error must not pay twice. Derive a stable idempotency id for the offer and reuse it on every retry:

bash
tenzro x402 payment-id --payer-did did:tenzro:machine:<controller>:<agent-id> \
  --requirement-file challenge.json --rpc https://rpc.tenzro.xyz
# pay_<hex>

6. Sell your own resource over x402

Any paid route you host on Tenzro can charge over x402. To let buyers and agents find it, publish a listing in the network's discovery catalog. Publishing is an owner call signed by the seller's account.

bash
tenzro x402 register-resource \
  --seller-did did:tenzro:human:<your-id> \
  --resource https://api.example.org/v1/weather \
  --scheme tenzro-hybrid \
  --network <caip2> \
  --asset USDC \
  --pay-to 0x<your-wallet> \
  --max-amount-required 2000 \
  --description "Hourly weather by coordinates" \
  --tags weather,data \
  --rpc https://rpc.tenzro.xyz

Buyers browse listings with the CLI or the web API:

bash
tenzro x402 discover-resources --asset USDC --tags weather --rpc https://rpc.tenzro.xyz

curl -s 'https://api.tenzro.xyz/discovery/resources?asset=USDC&tags=weather&limit=20'

Next steps