Documentation
Build on Rail402
Publish paid APIs, build autonomous agents, and integrate x402 micropayments on Base. Everything an agent needs to discover, pay for, and call an API — settled in USDC.
Introduction
What is Rail402
Rail402 is an agent-native marketplace for paid APIs. Providers publish REST endpoints with per-call USDC pricing. AI agents discover those endpoints through a structured registry, pay per request on Base via the x402 protocol, and receive a response — all autonomously, with no API keys, accounts, or subscriptions.
Payment as authentication
The core idea: the payment is the authentication. Instead of issuing credentials, a provider returns a standard HTTP 402. The agent pays in stablecoin, the proof is verified on-chain, and the data is returned. No session state, no key rotation, no human in the loop.
Because the transaction hash is the credential, the model composes cleanly with agent runtimes, MCP servers, and multi-agent pipelines — settings where per-call economic settlement is the only access-control model that scales to software acting on its own behalf.
Who it's for
| You are… | Start here |
|---|---|
| A provider with an API to monetize | Jump to For providers |
| Building an agent that needs live data | Jump to For agents |
| Using Claude, Codex, Antigravity or Cursor | Set up the MCP server |
| Integrating the discovery registry | Read Agent discovery |
How x402 payments work
Request lifecycle
The x402 flow is a two-step HTTP pattern built on standard status codes:
- Agent calls
POST /api/services/[id]/callwith the request payload. - Rail402 returns
HTTP 402with a payment object: recipient wallet, USDC amount, network, and a 10-minute expiry. - Agent submits a USDC transfer on Base (~2s finality) and captures the transaction hash.
- Agent retries with an
x-payment-proofheader containing thetxHashandpayerWallet. - Rail402 verifies the on-chain transfer and returns
HTTP 200with the provider response.
Input is validated against the service's inputSchema before the challenge is issued — a malformed request returns 400and never reaches the provider. The SDK's fetchWithPayment performs steps 2–5 for you in a single call.
The payment object
The 402 body carries everything needed to settle. The same fields appear at the top level (for x402 clients) and under a payment key (for convenience).
| Field | Example | Meaning |
|---|---|---|
| type | x402_payment_required | Discriminator that marks an x402 challenge. |
| amountAtomic | 50000 | Exact USDC to transfer, in atomic units (6 decimals). Send this value. |
| amount | 0.05 | Human-readable USDC amount (display only). |
| payTo | 0x2DDa…191a | Provider wallet — the USDC recipient. |
| network | base | Settlement network. base = mainnet. |
| chainId | 8453 | Base mainnet (8453) or Base Sepolia (84532). |
| currency | USDC | Settlement token. |
| expiresAt | ISO 8601 | Challenge expiry — 10 minutes from issuance. |
amountAtomicexactly. Underpayment is rejected, and the value is already denominated in USDC's 6-decimal base units — no conversion required.Proof & verification
After paying, the agent retries with an x-payment-proof header. The proof is a small JSON object:
1{
2 "txHash": "0x91c9…6055", // required — the USDC transfer hash
3 "payerWallet": "0x8A05…0C08" // recommended — the paying account
4}The gateway verifies the transaction on-chain before proxying the call. It checks that the:
- transaction succeeded and is a USDC transfer on the expected network;
- token contract matches the canonical USDC address for that network;
- recipient equals the service's
payTowallet; - amount is at least the registered price (underpayment is rejected);
txHashhas not been used for this service before (replay protection).
Expiry & replay rules
txHash is single-use — reusing one returns 409 Conflict. Pay a fresh transaction per call.Replay protection is what distinguishes an x402 settlement from a plain USDC transfer: the gateway indexes every proof, so a transaction can unlock a given service exactly once.
SDKs & tooling
Everything is open source and published. The SDK handles the x402 handshake and on-chain verification so you never implement it by hand.
Make any REST endpoint x402-payable, and pay for x402 endpoints as an agent. Express, Next.js & FastAPI adapters.
npm install @rail402/x402 viemThe Python distribution: FastAPI middleware and on-chain verification.
pip install rail402-x402[fastapi]Give Claude, Codex, Antigravity, Cursor & Base MCP direct access to the marketplace: list, inspect, pay for, and call any API.
npx -y @rail402/mcp@latestValidate an agent-services.json discovery document against the Rail402 spec.
npx @rail402/validate-spec ./agent-services.jsonBrowse the source on github.com/Rail402 — see Resources for the full repo map.
For providers
There are two ways to monetize an API. Most providers use the marketplace; advanced providers can also accept x402 directly on their own infrastructure.
Option A — Publish on the marketplace
Expose a plain HTTPS endpoint that accepts a JSON body and returns JSON. You do notadd any payment code — Rail402's gateway handles the 402 challenge, on-chain verification, and replay protection, then proxies the request to your endpoint. USDC settles directly to your wallet.
- Build a public HTTPS endpoint (POST, JSON in/out, internet-reachable).
- Define JSON Schemas for input and output — agents read these to call you.
- Connect your wallet — it becomes the payout address.
- Submit on the Publish page. New services enter
pending_review; once approved they go live in the marketplace.
npx @rail402/validate-spec ./agent-services.jsonOption B — Accept x402 on your own server
Want to charge for an endpoint outside the marketplace? Wrap it with @rail402/x402. One wrapper adds the full 402 → verify → respond flow.
1import { withX402 } from "@rail402/x402";
2
3app.post(
4 "/api/risk",
5 withX402(
6 async (req, res) => {
7 // payment already verified; req.x402.payerWallet is available
8 res.json(await yourBusinessLogic(req.body));
9 },
10 { price: "0.05", wallet: process.env.PROVIDER_WALLET, network: "base" },
11 ),
12);1// app/api/risk/route.ts
2import { withX402Payment } from "@rail402/x402";
3
4export const POST = withX402Payment(
5 async (req, { x402 }) => Response.json({ score: 0.92, paidBy: x402.payerWallet }),
6 { price: "0.05", wallet: process.env.PROVIDER_WALLET!, network: "base" },
7);1from rail402_x402 import X402Middleware
2
3X402Middleware(app, price="0.05", wallet=PROVIDER_WALLET, network="base", protected_paths=["/api/risk"])Deploy-ready templates: Rail402/provider-starter.
For agents & consumers
Three ways to call a paid API, from highest-level to lowest.
Option A — SDK (recommended)
fetchWithPayment takes a viem wallet and transparently settles the 402, then retries — the whole handshake in one call.
1import { fetchWithPayment } from "@rail402/x402";
2import { createWalletClient, http } from "viem";
3import { privateKeyToAccount } from "viem/accounts";
4import { base } from "viem/chains";
5
6const wallet = createWalletClient({
7 account: privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as `0x${string}`),
8 chain: base,
9 transport: http(),
10});
11
12const res = await fetchWithPayment(
13 "https://www.rail402.app/api/services/{id}/call",
14 { method: "POST", body: JSON.stringify({ input: { address: "0x…" } }) },
15 wallet,
16);
17console.log(await res.json());Option B — MCP (Claude, Codex, Antigravity, Cursor & Base MCP)
Add @rail402/mcp and your model can browse, price-check, pay for, and call any marketplace API itself. Tools: list_services, get_service_details, call_service, check_payment_status.
The config below is the standard MCP format — drop it into your client's config file (claude_desktop_config.json, Codex's MCP settings, or Antigravity's mcp_config.json).
1{
2 "mcpServers": {
3 "rail402": {
4 "command": "npx",
5 "args": ["-y", "@rail402/mcp@latest"],
6 "env": {
7 "RAIL402_API_BASE": "https://www.rail402.app",
8 "RAIL402_NETWORK": "base",
9 "RAIL402_PRIVATE_KEY": "0xYourAgentWalletKey"
10 }
11 }
12 }
13}RAIL402_PRIVATE_KEY is required only for paid call_servicecalls; discovery works read-only without it. Use a dedicated agent wallet funded with only what you're willing to let the agent spend.
Option C — Raw HTTP
No SDK? Implement the two-step flow directly:
1// 1. First call — expect 402
2const challenge = await fetch(callUrl, {
3 method: "POST",
4 headers: { "Content-Type": "application/json" },
5 body: JSON.stringify({ input }),
6});
7const { payment } = await challenge.json(); // { payTo, amountAtomic, network, ... }
8
9// 2. Transfer USDC(payment.payTo, payment.amountAtomic) on Base -> txHash
10
11// 3. Retry with proof
12const result = await fetch(callUrl, {
13 method: "POST",
14 headers: {
15 "Content-Type": "application/json",
16 "x-payment-proof": JSON.stringify({ txHash, payerWallet }),
17 },
18 body: JSON.stringify({ input }),
19});Agent discovery
Registry endpoints
Rail402 publishes a machine-readable registry at three canonical endpoints:
| Endpoint | Format | Best for |
|---|---|---|
| /api/agent/services | JSON | Programmatic discovery, pageable, CORS-open |
| /.well-known/agent-services.json | JSON | Standard well-known location |
| /llms.txt | Plain text | LLM context windows |
The document format is an open, versioned standard. Read the spec and the full x402 flow at Rail402/agent-services-spec, and validate your own with @rail402/validate-spec.
Service schema
Each entry is self-describing — an agent can select, price-check, and invoke a service in a single discovery pass. The key fields:
| Field | Description |
|---|---|
| id / slug | Stable identifier and human-readable handle. Either works in the call URL. |
| price | { amount, currency } — per-call cost in USDC. |
| network | Settlement network (base). |
| capabilities / tags | Keywords for capability-based routing and search. |
| inputSchema / outputSchema | JSON Schemas — build a valid request and parse the response. |
| endpoint | The call URL to POST against. |
| reliability | { verified, successRate, averageLatencyMs, totalCalls } — live operational stats. |
| playgroundUrl | Try the service with a simulated payment, no wallet required. |
1{
2 "id": "cmpjq32i80002qaimfb2fezm4",
3 "slug": "wallet-risk",
4 "name": "Wallet Risk Score API",
5 "capabilities": ["risk", "wallet", "security"],
6 "price": { "amount": "0.05", "currency": "USDC" },
7 "network": "base",
8 "endpoint": "https://www.rail402.app/api/services/{id}/call",
9 "inputSchema": { /* JSON Schema */ },
10 "outputSchema": { /* JSON Schema */ },
11 "reliability": {
12 "verified": true,
13 "successRate": 99.5,
14 "averageLatencyMs": 132,
15 "totalCalls": 2059
16 },
17 "playgroundUrl": "https://www.rail402.app/playground/wallet-risk"
18}API reference
Status codes
| Status | Meaning | Action |
|---|---|---|
| 400 | Input failed schema validation. | Fix the request body to match inputSchema. |
| 402 | Payment required. Includes the payment object. | Pay on Base, retry with x-payment-proof. |
| 403 | Service exists but is not published. | Wait for the service to go live. |
| 404 | No service matches that id or slug. | Re-check the id from the discovery registry. |
| 409 | txHash already used (replay rejected). | Submit a new on-chain transaction. |
| 429 | Rate limited (10 req / 10s / IP). | Wait for X-RateLimit-Reset, then retry. |
| 502 | Provider endpoint unreachable or timed out. | Try again later. |
| 500 | Internal Rail402 error. | Retry once; contact support if it persists. |
Rate limits
All call endpoints: 10 requests per 10 seconds per IP. On a 429, response headers tell you when to retry:
1X-RateLimit-Remaining: 0
2X-RateLimit-Reset: 1717056123000Resources
Repositories
| Repo | What it is |
|---|---|
| Rail402/x402-sdk | @rail402/x402 — provider middleware + agent client (TS & Python) |
| Rail402/provider-starter | Deploy-ready Express / Next.js / FastAPI paid-API templates |
| Rail402/agent-services-spec | The discovery standard + @rail402/validate-spec CLI |
| Rail402/rail402-mcp | @rail402/mcp — MCP server for Claude, Codex, Antigravity, Cursor & Base MCP |
| Rail402/examples | LangChain, Claude, OpenAI & Base MCP reference agents |
| Rail402/awesome-rail402 | Curated ecosystem index |
Drop-in agent prompt
Paste this into any LLM system prompt to make it a Rail402-capable agent:
1You are an AI assistant that can call paid APIs on Rail402.
21. Discover services at https://www.rail402.app/api/agent/services.
32. Read inputSchema and build a valid request body.
43. Call the service's call endpoint. If HTTP 402, read the payment object.
54. Transfer USDC to payment.payTo on Base. Capture the txHash.
65. Retry with x-payment-proof: {"txHash":"0x...","payerWallet":"0x..."}.
76. Return the provider response to the user.
8
9Never expose private keys. Validate output before irreversible actions.
10Payment requirements expire after 10 min - never reuse a txHash.