Charge for an API call
without an account on either side.
Wrap a route, name a price. The caller pays per request in USDG and the money lands in your wallet — no signup, no subscription, no processor in between.
import { withX402Payment } from '@/lib/x402/middleware';
export const POST = withX402Payment(handler, "0.01");- Protocol fee
- 0%
- Settles in
- USDG
- Accounts required
- none
How a paid request works
Four steps, all of them in lib/x402/middleware.ts. No facilitator required, no account anywhere in the loop.
- 01
The request arrives without payment
Your handler is wrapped once. Payless intercepts before your code runs.
GET /api/data/weather?city=Istanbul - 02
Payless answers 402
The response carries the price, your address, the chain ID and the tokens you accept.
HTTP/1.1 402 Payment Required { "amount": "0.01", "currency": "USDG", "recipient": "0x…", "network": "4663" } - 03
The caller pays on chain
A real USDG transfer on Robinhood Chain. The caller retries with its transaction hash — not a promise to pay, but a receipt.
X-Payment: {"from":"0x…","amount":"0.01", "token":"USDG","chainId":"4663", "transactionHash":"0x13c8…"} - 04
Payless reads the receipt
It confirms the transfer landed, paid the right address, cleared the price, and has not been spent before. Only then does your handler run.
HTTP/1.1 200 OK x-payment-chain: robinhood { "city": "Istanbul", "temperature": 22 }
The transaction hash is also the replay key: one transfer buys exactly one response. Reuse it and the request is rejected. What is still open is a shared store for that ledger, so replay protection holds across serverless instances — see the chain docs.
One wrapper on the handler you already wrote
Your route keeps its signature. The price is the second argument.
import { withX402Payment } from '@/lib/x402/middleware';
async function handler(req: NextRequest) {
// Your API logic here
const data = await processRequest(req);
return NextResponse.json({ data });
}
// Add payment requirement - that's it!
export const POST = withX402Payment(handler, "0.01");// 1. Pay on Robinhood Chain
const hash = await usdg.transfer(recipient, amount);
// 2. Call again with the receipt
const response = await fetch('/api/your-endpoint', {
method: 'POST',
headers: {
'X-Payment': JSON.stringify({
transactionHash: hash, // proof, not a promise
amount, token: 'USDG', chainId: '4663'
})
},
body: JSON.stringify({ data })
});Selling data about the assets the chain carries
Reading a tokenised equity on Robinhood Chain is permissionless. Moving one is not. So these endpoints sell data about them and settle in USDG — no securities held, no upstream key, and every answer checkable against the explorer.
/api/rwa/tokens$0.02 USDGEvery Robinhood stock token, supply read live/api/rwa/token$0.01 USDGOne tokenised equity, by ticker/api/rwa/holdings$0.02 USDGAn address’s tokenised equity position/api/chain/receipt$0.02 USDGDid this transaction actually pay me?/api/chain/balance$0.01 USDGETH and token balances for any address/api/chain/token$0.01 USDGERC-20 metadata, read live from chain 4663/api/data/crypto$0.015 USDGSpot prices, no API key on your side/api/tools/qrcode$0.005 USDGHalf a cent a QR code