Point your server at it.
Use https://anyroute.tech/facilitator as the facilitator address, with network eip155:4663 and asset USDG. It speaks x402 v1 and v2 and answers GET /supported, POST /verify and POST /settle. Verify before you do the work, settle after.
// Your server received a buyer's x402 payment and decoded it to paymentPayload.
const FACILITATOR = "https://anyroute.tech/facilitator";
const paymentRequirements = {
scheme: "exact",
network: "eip155:4663",
amount: "10000", // 0.01 USDG (6 decimals)
asset: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
payTo: "0xYourAddress",
maxTimeoutSeconds: 60,
extra: { name: "Global Dollar", version: "1" },
};
const post = (path, body) =>
fetch(FACILITATOR + path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) }).then((r) => r.json());
const check = await post("/verify", { paymentPayload, paymentRequirements });
if (!check.isValid) return reply402(check.invalidReason);
const answer = await doTheWork();
const settled = await post("/settle", { paymentPayload, paymentRequirements });
if (!settled.success) return reply402(settled.errorReason);
// settled.transaction: the Robinhood Chain transaction. settled.receipt.url: its signed receipt.What it checks before it relays.
- The signature. The payer’s EIP-712 signature over USDG’s own domain, from a plain wallet or a smart wallet.
- The recipient and amount. The authorization pays your payTo at least the amount you asked for.
- The time window. validBefore must outlive the relay by at least 6 seconds.
- The nonce, once. An authorization settles at most once, on chain and in the facilitator’s own record, so a replay is refused.
- The balance. The payer holds enough USDG.
What it costs.
No fee during the launch waiver: you receive the full amount. The smallest payment it settles is 0.01 USDG, because each settle costs gas. To accept smaller payments, prepay a gas float in USDG; each small settle is debited at its measured gas plus a buffer. When the relay key runs below its gas floor the facilitator refuses with facilitator_unavailable instead of queueing your payment.
Get listed.
Sign a listing with your payTo key: the paid URL, a price hint, an output schema and tags. Listed sellers appear at GET /facilitator/discovery/resources in the same item shape x402 discovery clients read. Listings are screened against the public OFAC SDN list. Payers are not, because the facilitator never holds the funds it relays.
import { privateKeyToAccount } from "viem/accounts";
const seller = privateKeyToAccount(process.env.PAY_TO_KEY); // the key behind your payTo address
const listing = {
payTo: seller.address,
resource: "https://api.example.com/quote",
priceHint: 10000n,
outputSchema: JSON.stringify({ type: "object", properties: { price: { type: "number" } } }),
tags: ["quotes"],
listed: true,
issuedAt: BigInt(Math.floor(Date.now() / 1000)),
};
const { policy } = await (await fetch("https://anyroute.tech/facilitator/supported")).json();
const signature = await seller.signTypedData({ ...policy.listing, message: listing });
await fetch("https://anyroute.tech/facilitator/sellers", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ ...listing, priceHint: "10000", issuedAt: Number(listing.issuedAt), signature }),
});Every settle is signed.
Each settle gets a receipt signed with the router’s receipt key: the transaction, amount, payTo and network, and no payer. Receipts are rooted every hour with the router’s other receipts. Read one at GET /facilitator/receipts/{id}, and check its signature and anchor proof with POST /api/v1/receipts/verify.
What it does not do.
- It cannot make a seller deliver. It relays payments; it does not judge the work.
- A listing is a hint. Your own 402 response stays the price and payTo a buyer should trust.
- The first payTo to list a URL owns that entry.
- It settles USDG on Robinhood Chain only.