Docs
Build with
Wafermap.
Every Wafermap resource is an HTTP endpoint that charges per call with x402 v2. There are no accounts and no API keys: the payment is the authentication.
- Base URL:
https://wafermap.xyz - Catalog:
GET /v1/resources(free, JSON) - Call:
POST /v1/<resource-id>with a JSON body - Pay: USDG on Robinhood Chain (eip155:4663) or USDC on Base (eip155:8453)
Quickstart
The shortest path is the SDK or the CLI. Below is the whole protocol by hand: Node 18+ or any modern runtime, with viem. Use a fresh wallet that holds only what your agent should spend.
// npm i viem import { privateKeyToAccount } from "viem/accounts"; const account = privateKeyToAccount(process.env.AGENT_KEY); const CHAIN_ID = { "eip155:4663": 4663, "eip155:8453": 8453 }; async function call(id, body, network = "eip155:4663") { const url = `https://wafermap.xyz/v1/${id}`; const init = { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) }; let res = await fetch(url, init); if (res.status !== 402) return res.json(); const quote = await res.json(); const req = quote.accepts.find((r) => r.network === network); const nonce = "0x" + crypto.getRandomValues(new Uint8Array(32)).reduce((s, b) => s + b.toString(16).padStart(2, "0"), ""); const validBefore = BigInt(Math.floor(Date.now() / 1000) + 120); const signature = await account.signTypedData({ domain: { name: req.extra.name, version: req.extra.version, chainId: CHAIN_ID[network], verifyingContract: req.asset }, types: { TransferWithAuthorization: [ { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" }, { name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" } ] }, primaryType: "TransferWithAuthorization", message: { from: account.address, to: req.payTo, value: BigInt(req.amount), validAfter: 0n, validBefore, nonce }, }); const payment = { x402Version: 2, resource: quote.resource, accepted: req, payload: { signature, authorization: { from: account.address, to: req.payTo, value: req.amount, validAfter: "0", validBefore: String(validBefore), nonce } } }; init.headers["PAYMENT-SIGNATURE"] = btoa(JSON.stringify(payment)); res = await fetch(url, init); return res.json(); // { resource, result, receipt: { amountUsd, tx, network } } } const out = await call("market-brief", { network: "robinhood" }); console.log(out.result.brief, out.receipt.tx);
How x402 works
- Request.
POST /v1/<id>with your JSON input. Input is validated first, so a bad request costs nothing. - 402 quote. The response is
402 Payment Required. The body (and the base64PAYMENT-REQUIREDheader) listsaccepts: one quote per network withamount(6 decimals),asset,payToand the token's EIP-712 domain inextra. - Sign. Sign an EIP-3009
transferWithAuthorizationfor exactlyamounttopayTo. Signing costs no gas. Send it base64-encoded inPAYMENT-SIGNATURE(the legacyX-PAYMENTheader also works). - Deliver. The facilitator verifies the signature, the resource runs, and only then the transfer settles on-chain. The response carries the result, a
receiptand a base64PAYMENT-RESPONSEheader with the transaction hash.
Networks & assets
| Network | CAIP-2 | Asset | Contract | EIP-712 domain |
|---|---|---|---|---|
| Robinhood Chain | eip155:4663 | USDG | 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 | "Global Dollar", "1" |
| Base | eip155:8453 | USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | "USD Coin", "2" |
Both assets use 6 decimals, so a $0.005 call is amount: "5000" on either network. If you name no network in your payment, Robinhood Chain is assumed.
Endpoints
| GET /v1/resources | Full catalog: id, price, input fields, returns, networks. Free. |
| GET /v1/resources/:id | One resource. Free. |
| POST /v1/:id | Run a resource. Answers 402 until paid. |
| GET /v1/payments?payer=0x… | Settled payments for one payer (public on-chain data). Free. |
| GET /api/discover?q=… | Search third-party x402 services in the open x402 Bazaar index. Payable ones (USDG on Robinhood Chain, USDC on Base) first. Free. |
| GET /api/tokens?network=… | Swappable tokens: ETH, WETH, the stablecoin and, on Robinhood Chain, every stock token. Free. |
| GET /api/stats | Live totals: resources, paid calls, volume. Free. |
Errors & refunds
Errors look like {"error":{"code","message"}}. Wafermap settles a payment only after the resource succeeded, so there is nothing to refund: if the model, the chain or the page you asked for fails, the signed authorization is released and never executed.
| 400 missing_field / invalid_* | Your input. Not charged. |
| 402 verify_invalid | Signature, amount, balance or network did not check out. |
| 402 nonce_used | That authorization was already used. Sign a new one. |
| 422 fetch_failed | The link you gave could not be read. Not charged. |
| 429 model_busy | Model at capacity. Retry. Not charged. |
| 502 *_failed / *_unavailable | Upstream failure. Not charged. |
Agent wallet
The Wafermap Wallet generates a key in your browser and stores it in local storage. The app signs payments with it inside your per-payment and daily caps, and swaps inside a per-swap cap. Wafermap's servers never receive the key. Export it to use the same wallet from code, and keep only a small balance on it.
CLI
The wafermap command keeps the agent key on your own machine, encrypted with your password (scrypt + AES-256-GCM in ~/.wafermap/keystore.json), and enforces the same caps before it signs anything.
# Node 20+ npm i -g https://wafermap.xyz/cli/wafermap.tgz wafermap init # new encrypted wallet; prints the address to fund wafermap limits --per-payment 0.1 --per-day 1 --per-swap 25 wafermap balance wafermap call prediction-markets query="fed rates" wafermap call stock-signal symbol=TSLA wafermap call flux-2-dev prompt="a glossy silicon wafer" --out wafer.png wafermap discover weather # third-party x402 services wafermap pay https://x402.agentindex.world/weather city=Jakarta wafermap swap 0.01 ETH USDG # KyberSwap route, needs a little ETH for gas wafermap history
Every payment and swap asks for confirmation unless you add --yes; the caps apply either way. --network base pays or trades on Base. Set WAFERMAP_PASSWORD for scripts.
MCP server
Remote: https://wafermap.xyz/mcp (Streamable HTTP, no install). Browse the catalog, read resource schemas, discover third-party x402 services and list swappable tokens for free; wafermap_call runs a paid resource when params._meta["x402/payment"] carries a signed x402 payment, and otherwise returns the payment requirements. Listed in the official MCP Registry as xyz.wafermap/wafermap.
Local, with a wallet: wafermap mcp runs the same tools plus automatic payments and swaps from your encrypted local wallet, inside your caps, for Claude Desktop, Claude Code, Cursor and other MCP clients.
{
"mcpServers": {
"wafermap": {
"command": "wafermap",
"args": ["mcp"],
"env": { "WAFERMAP_PASSWORD": "your wallet password" }
}
}
}
| wafermap_wallet | Address, balances on both chains, caps, today's spend. |
| wafermap_resources | The Wafermap catalog with prices and input fields. |
| wafermap_call | Run a resource and pay it, within the caps. |
| wafermap_discover | Search third-party x402 services. |
| wafermap_pay | Call any x402 URL and pay it, within the caps. |
| wafermap_swap_quote | Best KyberSwap route for a trade. |
| wafermap_swap | Execute a swap. Off until you run wafermap limits --mcp-swaps on; then capped per swap. |
| wafermap_history | Recent payments and swaps. |
SDK
// npm i https://wafermap.xyz/cli/wafermap.tgz import { Wafermap } from "wafermap"; const wm = new Wafermap({ privateKey: process.env.AGENT_KEY, limits: { perPayment: 0.05, perDay: 2 } }); const { result, receipt } = await wm.call("stock-signal", { symbol: "TSLA" }); const [svc] = await wm.discover("token price"); const paid = await wm.pay(svc.url, { method: svc.method, input: svc.input, network: "base" }); const quote = await wm.swapQuote("ETH", "USDG", "0.01"); await wm.swap(quote);
Pass approve: (a) => boolean to confirm each payment or swap, and ledger to persist spending for the daily cap. x402fetch(url, init, { signer }) pays any x402 v1 or v2 endpoint directly.
Discovery
Beyond its own catalog, Wafermap searches the open x402 Bazaar index (run by Coinbase CDP, tens of thousands of services) and pays any service that accepts the exact scheme in USDG on Robinhood Chain or USDC on Base. The Wafermap App does this on its own when no Wafermap resource fits: it finds a service, fills its input, and shows you the price before paying.
Third-party services are run by their owners, not by Wafermap. Their answers, prices and refund rules are theirs. In the browser, calls go through a narrow relay (POST /api/relay) that only forwards to services returned by discovery and never sees your key; the CLI, SDK and MCP server call them directly.
Swaps
Swaps route through the KyberSwap aggregator on Robinhood Chain and Base. The quote and the transaction are built on your device and sent from your wallet: an exact-amount approval when needed, then the swap with 1% maximum slippage by default. Swaps need a little ETH for gas, always ask before sending in the app, and are capped per swap. Wafermap takes no fee on swaps.
Resource catalog
| Resource | Endpoint | Price |
|---|