Quickstart
Introduction
Section titled “Introduction”This walks the whole loop: stand up a paid endpoint, then pay it from an agent. Both sides use
mainnet USDC on Base, but the only thing that changes for another chain is the chain value.
1. Gate a route (the seller)
Section titled “1. Gate a route (the seller)”requirePayment returns Express/Connect middleware. The route answers 402 Payment Required
until a payment for the right amount, asset, and recipient verifies on-chain — then it runs.
import express from 'express'import { requirePayment } from '@piprail/sdk'
const app = express()
app.get( '/report', requirePayment({ chain: 'base', token: 'USDC', amount: '0.10', // human units — 0.10 USDC ≈ 10 US cents (USDC is a $1 stablecoin) payTo: '0xYourWallet', // paid straight to you; PipRail never touches it }), (req, res) => { res.json({ report: 'the goods behind the paywall' }) },)
app.listen(3000)Not on Express? createPaymentGate gives you the same logic framework-free for Hono, Fastify,
Workers, Next, Bun, or Deno — see Accepting Payments.
2. Pay it (the buyer)
Section titled “2. Pay it (the buyer)”PipRailClient.fetch is a drop-in for fetch. When the server answers 402, it reads the
challenge, pays on-chain, and retries — all in one call.
import { PipRailClient } from '@piprail/sdk'
const client = new PipRailClient({ chain: 'base', wallet: { key: process.env.AGENT_KEY }, // a 0x-hex key, from the environment})
const res = await client.fetch('http://localhost:3000/report')const data = await res.json()// ^ { report: 'the goods behind the paywall' } — paid for and unlockedThat’s the entire round-trip: 402 → pay on-chain → verify locally → 200.
3. Look before you pay (recommended for agents)
Section titled “3. Look before you pay (recommended for agents)”An autonomous agent should learn the price and check it can actually settle before spending.
The read-only trio never moves funds; each returns null when the URL isn’t payment-gated (no
402), so null-guard the result before using it.
const url = 'https://api.example.com/report'
const quote = await client.quote(url) // the price, with the token's TRUE decimals// → { amountFormatted: '0.10', symbol: 'USDC', chain: 'base', withinPolicy: true, … }
const plan = await client.planPayment(url) // can I pay? balance + gas + recipient readiness// → { payable: true, best: { … }, options: [ … ], fundingHint: null, … }
if (plan?.payable) { await client.fetch(url)} else if (plan) { console.log(plan.fundingHint) // one-line, human-readable: what's missing}See quote() for the priced requirement,
estimateCost() for the gas, and
planPayment() for the full PaymentPlan — per-rail
blockers, a fundingHint, and best.
Try it against a live endpoint (no server needed)
Section titled “Try it against a live endpoint (no server needed)”Want to pay a real 402 before standing up your own server? PipRail runs a live one on Base mainnet — a $0.01 USDC 402 you can hit right now:
curl -i https://piprail.com/x402/demo # see a real 402 challenge + the accepts[]// Pay it from a client (needs ~$0.01 USDC + a little ETH for gas on Base):const client = new PipRailClient({ chain: 'base', wallet: { key: process.env.AGENT_KEY } })const res = await client.fetch('https://piprail.com/x402/demo') // 402 → pay → 200It’s a real, backendless endpoint — dual-rail (PipRail’s onchain-proof and a gasless standard
exact rail settled via the free PayAI facilitator), and it’s listed on x402scan and 402 Index.
Prefer to watch the round-trip in your browser first? Try the interactive demo at
piprail.com/demo.
One agent, every chain (multi-wallet)
Section titled “One agent, every chain (multi-wallet)”A PipRailClient is bound to one chain. But a merchant might demand Base today and Solana
tomorrow — or list both in the same 402. Instead of wiring up routing yourself, give a
MultiChainPayer one wallet per chain and it pays whichever chain the 402 asks for,
under one shared budget:
import { MultiChainPayer } from '@piprail/sdk'
const agent = MultiChainPayer.fromWallets({ wallets: { base: { key: process.env.EVM_KEY }, // one EVM key works on every EVM chain solana: { key: process.env.SOLANA_KEY }, // every chain takes the same field: { key } }, // ONE policy caps every chain — the agent can never exceed it, whatever chain it pays on. policy: { maxAmount: '1.00', maxTotal: '10.00', tokens: ['USDC'] },})
// Same fetch/get/post/quote/planPayment as a single client — just across all your chains.const res = await agent.get('https://api.example.com/report')// ^ settles on the first funded chain (in the order you listed) that can afford itIt surveys every funded chain when it hits the 402, then settles on the first one you listed that can pay — no oracle, no backend, no manual routing. The agent toolkit and the MCP server wrap it unchanged, so an LLM with a multi-chain bundle uses the exact same tools. Full reference: Multi-chain buying.
Next steps
Section titled “Next steps”- Add spend caps so an agent can’t overspend — Spend Controls.
- Pay a 402 on whichever chain it asks for — Multi-chain buying.
- Offer several chains at once in one challenge (seller side) — Defining Accepts.
- Hand the whole thing to an LLM — the MCP server.