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

  1. Request. POST /v1/<id> with your JSON input. Input is validated first, so a bad request costs nothing.
  2. 402 quote. The response is 402 Payment Required. The body (and the base64 PAYMENT-REQUIRED header) lists accepts: one quote per network with amount (6 decimals), asset, payTo and the token's EIP-712 domain in extra.
  3. Sign. Sign an EIP-3009 transferWithAuthorization for exactly amount to payTo. Signing costs no gas. Send it base64-encoded in PAYMENT-SIGNATURE (the legacy X-PAYMENT header also works).
  4. Deliver. The facilitator verifies the signature, the resource runs, and only then the transfer settles on-chain. The response carries the result, a receipt and a base64 PAYMENT-RESPONSE header with the transaction hash.

Networks & assets

NetworkCAIP-2AssetContractEIP-712 domain
Robinhood Chaineip155:4663USDG0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168"Global Dollar", "1"
Baseeip155:8453USDC0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"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/resourcesFull catalog: id, price, input fields, returns, networks. Free.
GET /v1/resources/:idOne resource. Free.
POST /v1/:idRun 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/statsLive 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_invalidSignature, amount, balance or network did not check out.
402 nonce_usedThat authorization was already used. Sign a new one.
422 fetch_failedThe link you gave could not be read. Not charged.
429 model_busyModel at capacity. Retry. Not charged.
502 *_failed / *_unavailableUpstream 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_walletAddress, balances on both chains, caps, today's spend.
wafermap_resourcesThe Wafermap catalog with prices and input fields.
wafermap_callRun a resource and pay it, within the caps.
wafermap_discoverSearch third-party x402 services.
wafermap_payCall any x402 URL and pay it, within the caps.
wafermap_swap_quoteBest KyberSwap route for a trade.
wafermap_swapExecute a swap. Off until you run wafermap limits --mcp-swaps on; then capped per swap.
wafermap_historyRecent 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

ResourceEndpointPrice