402xAgent · Pricing · Docs · Blog

Docs

402xAgent checks an x402 endpoint right before your agent pays: pay, skip or recheck.

Quick start

Free check, no signup: GET https://api.402xagent.com/v1/products/endpoint-spot-check?url=<endpoint>&mode=dry-run

After you pay: payment status

Your paid call timed out, or came back 5xx or an empty 402. Did the money move? POST https://api.402xagent.com/v1/products/payment-status with JSON {"payment_payload":"<base64 PAYMENT-SIGNATURE you sent>","seller_url":"<url you paid>"} (or tx_hash, or from + nonce + valid_before). Base USDC (EIP-3009 exact) only for now.

Worked example: a paid call that timed out but settled (402rates-ping, Oct 16, 2026)

A real case from our own client. Facts only; times are Pacific (UTC-7).

Answer: status landed (reason settled_onchain, 634 confirmations), amount and payTo match the seller's 402 terms, two RPC sources (Alchemy and Infura) agreed, safe_to_resign false. So: don't sign a new payment; the money moved, and the next step is to ask the seller for the response or a refund, using the tx as evidence. Signed receipt: https://402xagent.com/v1/receipts/ps-65a4fdffae01d2b6.

Expiry check (opt-in)

Add &check_expiry=1 to endpoint-spot-check (optional &skew_s= 0 to 300, default 30). If the check would say pay but every Base USDC option in the live 402 is already expired, or expires within skew_s (extra.validBefore, expiresAt, expiry or deadline; or maxTimeoutSeconds of 5 or less), the verdict is skip with reason challenge_expired, and the answer and receipt carry an expiry_check block. Without the parameter nothing changes. In x402-spotcheck 1.4.0: { checkExpiry: true }.

Hosted mode (integrator keys)

For routers and wallets that run checks for many agents. Opt in by asking us for a key, then send Authorization: Bearer 402x_live_<id>_<secret> (or x-402xagent-key) on the check or on payment status. Optionally add x-402xagent-end-user: <your opaque agent id>; we store only a salted hash of it.

Receipt signatures

New receipts (/v1/receipts/<id>) carry kid and signature: ed25519 (base64url, 64 bytes) over the RFC 8785 (JCS) canonical JSON of the receipt with only signature removed. Public keys: /.well-known/402xagent-keys.json (match kid). Older receipts are unsigned. A signature proves 402xAgent issued the receipt; it does not change the verdict.

Offline, with x402-spotcheck 1.1.0+: await verifyReceipt(receipt, { publicKey }). Or with Node 20+ and no dependencies:

import { createPublicKey, verify } from "node:crypto";
const jcs = (v) => v === null || typeof v !== "object" ? JSON.stringify(v)
  : Array.isArray(v) ? "[" + v.map(jcs).join(",") + "]"
  : "{" + Object.keys(v).filter((k) => v[k] !== undefined).sort().map((k) => JSON.stringify(k) + ":" + jcs(v[k])).join(",") + "}";
const receipt = await (await fetch("https://402xagent.com/v1/receipts/sc-...")).json();
const { keys } = await (await fetch("https://api.402xagent.com/.well-known/402xagent-keys.json")).json(); // or a saved copy
const jwk = keys.find((k) => k.kid === receipt.kid);
const { signature, ...signed } = receipt;
const ok = verify(null, Buffer.from(jcs(signed)), createPublicKey({ key: { kty: "OKP", crv: "Ed25519", x: jwk.x }, format: "jwk" }), Buffer.from(signature, "base64url"));
console.log(ok ? "valid" : "INVALID");