Docs / x402
Guarding x402 machine-payments
x402 lets an agent pay for an API call over HTTP: request → 402 Payment Required → pay → retry. The catch: the agent decides to pay autonomously, against whatever the challenge asks. @countersign/x402 puts your policy between the challenge and the payment.
The guard
import { parseX402, withX402Guard, X402Denied } from "@countersign/x402";
const res = await fetch(paidApiUrl);
if (res.status === 402) {
// pin the asset — decoy options with spoofed names don't survive parsing
const charge = parseX402(await res.json(), { asset: "USDC" });
if (!charge) throw new Error("no acceptable payment option");
await withX402Guard(cs, agentId, charge, async (c) => {
// this callback runs ONLY if Countersign allowed the spend
return payX402(c);
}); // throws X402Denied on deny / needs_approval
}
What the parser defends against
- Cross-asset decoys. "Cheapest option" is compared on decimals-normalized value, not raw atomic units — a worthless token with a tiny atomic amount can't win the sort.
- Spoofed names. With an asset pin, only options genuinely matching the pinned asset survive; the charge is labeled with the trusted pin, and the asset contract rides along for verification.
- Malformed amounts. Negative, hex, or empty amounts are filtered before selection.
- Garbage decimals. An option carrying a non-numeric
extra.decimalsis dropped (it can't be priced honestly); the rest of the challenge survives.
One integration anti-pattern
Don't wrap the guard in a fallback: withX402Guard(…).catch(() => payAnyway()) silently converts every deny — and every network failure, which correctly rejects — into a payment. The guard is fail-closed; keep the integration fail-closed too. If you need a recovery path, branch on X402Denied explicitly and route to a human, never to an alternate pay call.
The policy backstop
Even past the parser, evaluate enforces the real rules: per-transaction and rolling daily caps, allow/denylists, and the asset gate — a payment in an asset your policy doesn't govern is denied by default, not waved through. A fleet freeze denies everything instantly. Every decision, either way, lands in the signed ledger.
In MCP clients the same guard is one tool call: countersign_guard_x402 — pass the 402 challenge, get allow/deny before paying.
Try it in 60 seconds — no account, no human
Self-serve key, an isolated sandbox tenant with a three-backend demo fleet, testnet only.
Get a free key → Watch the freeze GitHub