x402 Preflight — a complete x402 service you can fork
Live: https://x402-preflight.broke2builtai.com · $0.001 per call · USDC on Base (eip155:8453) · x402 v2 · no dependencies
One Cloudflare Worker, about 200 lines, that does everything a listable x402 service has to do: answers 402 with a spec-shaped PAYMENT-REQUIRED header, publishes /.well-known/x402, verifies and settles through a facilitator, and never charges for work it did not deliver.
It is also useful on its own. POST /v1/preflight checks someone else’s x402 endpoint before an agent pays it: does it answer 402, can the terms be read, is the price in Base USDC, and does payTo match the wallet you expected (Bankr Cloud aware)? Those are the same checks this directory’s free health check runs, and scripts/health-check.mjs runs them locally for nothing. The paid route is for agents that cannot run a script.
Try it without a wallet
# Any unpaid call answers 402 with the price. The body does not matter yet.
curl -si -X POST https://x402-preflight.broke2builtai.com/v1/preflight \
-H 'content-type: application/json' -d '{}' | head -5
# Decode the terms from the header
curl -s -D - -o /dev/null -X POST https://x402-preflight.broke2builtai.com/v1/preflight \
| tr -d '\r' | awk 'tolower($1)=="payment-required:"{print $2}' | base64 -d
{
"x402Version": 2,
"error": "PAYMENT-SIGNATURE header is required",
"resource": { "url": "https://x402-preflight.broke2builtai.com/v1/preflight", "mimeType": "application/json", "description": "..." },
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"amount": "1000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x7a3E312Ec6e20a9F62fE2405938EB9060312E334",
"maxTimeoutSeconds": 300,
"extra": { "name": "USD Coin", "version": "2" }
}]
}
Call it with payment
Use any x402 v2 client that sends a PAYMENT-SIGNATURE header. Request body:
{ "url": "https://penniless-json-repair.sjaman.workers.dev/repair/json",
"wallet": "0x3D98800c64C345950E1eAaa076D88C12d1BF5F37" }
method (GET or POST, default POST) and body are optional. Omit body and the target gets {}, which is what tests whether it answers 402 before validating. The response is a report:
{
"verdict": "PASS",
"httpStatus": 402,
"x402Version": 2,
"price": { "atomic": "1000", "usd": 0.001 },
"payTo": "0x3D98800c64C345950E1eAaa076D88C12d1BF5F37",
"checks": [
{ "id": "answers-402", "pass": true, "detail": "HTTP 402 to POST" },
{ "id": "readable-terms", "pass": true, "detail": "1 offer(s), read from the header" },
{ "id": "v2-header", "pass": true, "detail": "PAYMENT-REQUIRED header present" },
{ "id": "base-usdc", "pass": true, "detail": "eip155:8453 / 0x8335..." },
{ "id": "payto-matches", "pass": true, "detail": "payTo 0x3D98..." },
{ "id": "discovery-document", "pass": true, "detail": "GET /.well-known/x402 -> 200" }
]
}
verdict is gated on answers-402, readable-terms and payto-matches. The rest is advice.
The four rules
Each one is marked RULE 1–RULE 4 in src/index.js. Each exists because a real submission to this directory broke it.
- Price first. Answer 402 before you parse or validate anything. Two queued services answered
400to an empty body, so a discovering agent saw an error instead of a price. We had to hand-craft probe bodies to check them at all. - Check the payment is for your price. A facilitator verifies the signature and the balance. It does not know what you charge. Compare
accepted.amount,asset,networkandpayToagainst your own terms before calling/verify. - Do the work before you settle. If the input is bad or the work fails, return the error and never call
/settle. Failure is then free for the buyer. - Settle, then deliver. If settlement fails, the buyer gets a 402, not your output.
Two more that are not in the code:
- Publish
/.well-known/x402. Directories and agents look there first. - Keep one durable origin. One queued service moved hosts three times in five days (two quick tunnels, then a Netlify origin that ran out of credits). Every move meant a failed check and a re-check.
Fork it
- Copy this folder.
- In
wrangler.toml, setPAY_TOto your wallet andPRICE_ATOMICto your price (USDC has 6 decimals, so1000= $0.001). Delete theroutesline unless you have a domain on your own Cloudflare account. - Replace
preflight()with your own work. KeephandlePaidRouteas it is. - Deploy:
npx wrangler deploy - Check it exactly the way this directory will:
node scripts/health-check.mjs --url https://<your-worker>.workers.dev/v1/preflight --wallet <your PAY_TO> - When that prints
PASS, submit it. See CONTRIBUTING.md.
The facilitator is PayAI, which needs no API key. Its /supported endpoint lists x402Version 2 / exact / eip155:8453. Any v2 facilitator with the same /verify and /settle shape works.
Tests
node test/preflight.test.mjs
Live against real endpoints. Every PASS case has a control that must FAIL: the right service with the wrong wallet, and a Bankr Cloud route checked against a different seller. A checker that cannot fail is not checking anything.
What has and has not been exercised
Measured 2026-09-15 against the deployed Worker:
- Unpaid
POST {}, unpaidGET, and an unpaid non-JSON body each answer 402 with the v2 header. - A garbage
PAYMENT-SIGNATUREanswers 402. - An under-priced payload answers 402 at rule 2, before the facilitator is called.
- A forged signature reaches PayAI’s
/verify, is rejected there, and nothing settles.
Not exercised: a real settled payment. No paid call has been made to this Worker yet. The settle path follows the x402 v2 HTTP transport spec, but until a transaction hash is recorded here, treat it as untested.
This service is listed in directory/queue.json on the same terms as every community submission: HEALTH_CHECKED and not verified. It gets no badge it did not pay a first-party test for.