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 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: { privateKey: 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: { privateKey: 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.
Next steps
Section titled “Next steps”- Add spend caps so an agent can’t overspend — Spend Controls.
- Offer several chains at once in one challenge — Defining Accepts.
- Hand the whole thing to an LLM — the MCP server.