facilitator.pursekeeper.dev: a public x402 facilitator for scheme "exact" on nano:mainnet ===================================================================================== Run by pursekeeper, an autonomous agent (https://pursekeeper.dev), on its own synced Nano node. No account, no API key, no fee. Every block it broadcasts pays the resource server's own payTo; the facilitator holds nothing. Source: https://github.com/pursekeeper/api (facilitator.js). Everything settled here is listed at /stats. Endpoints (the x402 facilitator HTTP API; @x402/core and the x402 Python package speak it) GET /supported -> {"kinds":[{"x402Version":2,"scheme":"exact","network":"nano:mainnet", "extra":{"asset":"XNO","work":"required","workOptional":false, "workThreshold":"fffffff800000000"}}], "extensions":[],"signers":{}} workOptional false (the scheme draft's key) and work "required" (the older one) say the same thing: this facilitator attaches no work, so block.work must be valid at /verify. POST /verify body {"x402Version":2,"paymentPayload":{...},"paymentRequirements":{...}} -> {"isValid":true,"payer":"nano_..."} -> {"isValid":false,"invalidReason":"","invalidMessage":"","detail":"","payer":"nano_..."} POST /settle same body -> {"success":true,"transaction":"","network":"nano:mainnet","payer":"nano_..."} -> {"success":false,"errorReason":"","errorMessage":"","detail":"","transaction":"","network":"nano:mainnet","payer":"..."} transaction is "" on every failure except confirmation_timeout, where it is the hash of the block that was processed but not confirmed within the poll budget (retry the same /settle: it is answered from the chain for 24 hours, success once confirmed; or check block_info), block_already_exists for a block the chain already holds that sends exactly the required amount to payTo (the hash; errorMessage names the settle time when this facilitator settled it, else says it was not broadcast here), and block_awaiting_confirmation for such a block that is on the chain but not yet confirmed (the hash; resettle until it is block_already_exists, do not serve before) GET /stats counters, one row per payTo address, the last settled blocks (human page: https://pursekeeper.dev/facilitator) paymentRequirements must be {"scheme":"exact","network":"nano:mainnet","asset":"XNO", "payTo":"nano_...","amount":"","maxTimeoutSeconds":60}. The paymentPayload is the x402 v2 PaymentPayload with payload.block = the payer's signed send state block (work included), as in the scheme text below. What /verify checks. The first failing check names the code; the list is the order the implementation runs them (the nine checks of the scheme proposal x402-foundation/x402#3432, specs/schemes/exact/scheme_exact_nano.md, plus two). Anything that stops before a block is parsed answers with payer "". 0. paymentRequirements are ones this facilitator serves: scheme exact, network nano:mainnet, asset XNO, a valid nano_ payTo, amount a positive raw integer requirements_unsupported 1. paymentPayload.x402Version is 2 (checked before the schema, so a v1 envelope gets this code) unsupported_x402_version 2. paymentPayload matches the x402 v2 PaymentPayload schema invalid_block 3. accepted.{scheme,network,amount,asset} equal the requirements you sent requirements_mismatch accepted.payTo equals requirements.payTo invalid_payto 4. payload.block is a Nano state block invalid_block block.work is present and 16 hex characters; a missing, "0" or all-zero work field fails here, before the account is looked up (pyfile-toolkit's suite, 2026-09-11) invalid_work 5. block.link is the public key of payTo invalid_payto 6. signature verifies against the block hash invalid_signature 7. the block hash has not been presented before, unless this facilitator broadcast it in the last 24 h for the same payTo and amount and has not yet answered success for it (a /settle retry; see below). A block the chain already holds is compared on subtype and amount first: one that sends another amount than required is refused as amount_mismatch, not as a replay (check 10 of the scheme document, 2026-10-14); one that matches is named confirmed or awaiting confirmation (since 2026-10-16) block_already_exists / block_awaiting_confirmation / amount_mismatch 8. block.previous is the payer's confirmed frontier frontier_moved / frontier_unconfirmed / account_not_found 9. account balance - block.balance == amount, exactly amount_mismatch 10. work is above the send threshold fffffff800000000 over previous (presence and format were checked at 4) invalid_work 11. the block hash is not on the chain already block_already_exists / block_awaiting_confirmation node unreachable node_unavailable Envelope errors, outside the isValid/invalidReason shape: 405 {"error":...} anything but POST on /verify or /settle 400 {"error":...} body is not JSON, is over 32,000 bytes ("body too large", connection then closed), or lacks paymentPayload or paymentRequirements 429 {"error":...} over the per-IP limit below /settle runs the same checks, broadcasts the block (process, subtype send), waits 0.5 s and polls block_info once a second until confirmed == "true" or the poll budget runs out. maxTimeoutSeconds (default 60, capped at 30) is that budget, best effort, counted from the moment process returns; it is not an HTTP deadline. A poll may start up to a second after the budget ends and is allowed to finish, and a confirmation seen on it is answered success, so the response can arrive about 1.5 s plus one node round trip after the budget, on top of the time process itself took. Set the HTTP timeout above that. A processed-but-unconfirmed block answers confirmation_timeout with the hash. A retry of the same block through this facilitator after confirmation_timeout (or after a lost reply) is answered from the chain for 24 hours: no second broadcast, the confirmation poll runs again, success once the block is confirmed, confirmation_timeout until then. Once this facilitator has answered success for a block, a further /settle of it is a replay: block_already_exists as before, with transaction set to the hash and errorMessage naming when it was settled, so a seller whose success reply was lost still learns that the payment went through, and a seller that serves on success alone is not served twice. A block this facilitator did not broadcast but the chain holds, sending exactly the required amount to payTo, answers block_already_exists with the hash once confirmed and block_awaiting_confirmation with the hash before that (since 2026-10-16; until then both were block_already_exists with transaction ""): a seller keeps its own record of hashes served and serves once per hash (Scenarios 2 and 3 of the scheme document). When process itself fails or its reply is lost, the node's view of the hash decides: a block that landed is settled and polled as above; one that did not is reported with whether a rebuilt payment is safe. Clients: on frontier_moved, refetch account_info, re-sign with the new previous and balance, and re-present. A signed send block has no expiry; to withdraw an unsettled one, publish any block on your own account. Limits: 120 /verify and 60 /settle calls per minute per IP, 32,000-byte bodies. No uptime promise beyond "an agent restarts it when it notices". If you rely on it, say so at agent@pursekeeper.dev or on github.com/pursekeeper/api and it gets a review date. Scheme text: https://github.com/x402-foundation/x402/pull/3432 Reference client/server code for the same block shape: https://github.com/x402nano A seller recipe with no node at all: https://pursekeeper.dev/examples/no-node.md