Sato Hub
Open method

Method: reading an x402 facilitator's matrix

Version 2, as of 2026-10-13.

Licence: CC BY 4.0. Reuse it, adapt it, build on it, with attribution to Sato Hub (satohub.ai). The daily history this method produces is not covered by this licence.

An x402 client fails when it picks the wrong scheme, network or protocol version for a facilitator. The facilitator's own /supported answer states which it serves. This method records that answer every day, says plainly when a day's answer is missing, and publishes the result as a dated scorecard on the x402 facilitators page. It describes what a facilitator lists. It does not grade it.

1. Definitions

  • Facilitator. A service that verifies and settles x402 payments for sellers. The target list is a register of facilitators seeded from the Facilitators category of the public x402 ecosystem partners data (self-listed by pull request). A listing there is coverage, not endorsement.
  • Matrix cell. One facilitator x chain x scheme, with the x402 versions and the asset descriptions its /supported answer states for that pair. A chain is stored as a CAIP-2 id; a v1 network name we cannot map to one keeps its raw token and no CAIP-2 id. A name is never guessed.
  • Fact. A cell, or a facilitator's state, with its value, the day it was seen (as_of), its source URL, its method, stale_after, a protocol-version pin, and a carried flag.
  • Method: observed vs declared. Observed is read from the facilitator's own /supported answer. Declared is a partner listing that the partner wrote itself (by pull request).
  • State. ok (answered with a matrix), auth_required (the host answered and asked for credentials: a key wall), not_served (the host answers but serves no /supported), unreachable (transport failure), or null (never confirmed). Null is unknown.
  • Not read. On the scorecard: the facilitator showed no matrix on the day (auth_required, unreachable, never confirmed, or no usable answer). Whatever is listed for it is its last matrix, labelled carried.
  • Carried. A fact from an earlier day, kept because the facilitator did not show its matrix today.
  • Held reading (pending). The first odd reading (a not_served answer, or an empty list from a facilitator that had a matrix) is held for one reading: the previous matrix is kept and the scorecard says "one reading held: previous matrix kept", with the cells marked carried.
  • Stale. A fact is stale once the scorecard day is later than its stale_after, which is the day it was last seen plus 3 days (STALE_AFTER_DAYS).
  • Answers 402. A seller's listed endpoint, when requested without payment, returns HTTP 402 with a payment payload we can parse. A separate, seller-side check; section 2, step 8.
  • Incentive flag. Whether a dated program in the incentive calendar that names the chain (and x402, or no specific protocol) was running on the scorecard day. A program with no protocol is shown as "not x402-specific".

2. Method

Reference code: the Sato Hub app (private). Names of the functions and constants that implement each step are given in backticks.

  1. Ask once per facilitator per day. GET <base_url>/supported (or the facilitator's own path), with a SatoHub- user agent, a timeout, and no payment. A body over 1 MB is a failed reading. Every URL comes from a third party's pull request, so before each request and each redirect hop the host must resolve to public addresses only (assertPublicHttpUrl); a refused host is a failed reading made with zero requests.
  2. Parse into cells (buildCells, normalizeNetwork). Each kind in the answer becomes a cell. Every stored string is a token, a host, an https URL, an enum or a date; a kind whose network, scheme or feature name is not a token is skipped and counted. Free text (a partner name, an EIP-712 asset name) is stored capped and stripped of markup and is returned only as labelled third-party text.
  3. Classify the answer. A usable matrix is ok. A 401 or 403 is auth_required. A 404, 405, 410 or an HTML 200 is not_served. A timeout, DNS or TLS error, 5xx, 429 or malformed body is a transport failure.
  4. Carry forward, with limits (resolveFacilitator, buildSupportedRows, OUTAGE_LIMIT_DAYS). On a transport failure or a key wall the last open facilitator row and its cells are copied into today (carried). carried_since records the first day of the outage and is cleared by the first day the matrix shows again. The carry ends, and the cells close, after 14 days of transport failure (the state becomes unreachable) or 30 days behind a key wall (the state stays auth_required, because the host did answer).
  5. Two-strike rule for odd readings. A not_served answer, or an empty kinds list from a facilitator that had open cells, is held for one reading (pending): the previous state and cells stay, and they close only on the second consecutive reading.
  6. Close history only on a complete read. The source read is complete when the whole target list was processed and at least one facilitator answered. A day that would close more than 30% of a sizeable open set closes nothing and says so in the manifest note (MASS_CLOSE_SHARE).
  7. Record whether it answered. A daily metric supported_reachable is 1 when the day's outcome was ok and 0 otherwise, per facilitator. The latest day with the value 1, looked back 90 days, is the input the reader needs to tell a fresh fact from a carried one.
  8. The 402 check, seller side (answersFor, parsePayment). For catalogued endpoints we request the URL without payment and count a 402 whose payment payload parses (a v2 PAYMENT-REQUIRED header, or v1 accepts[] in the body). Sampling is per chain (120 resources, SAMPLE_PER_NETWORK) with at most 3 per host (PER_HOST_CAP); templated URLs that cannot be requested as listed are counted, not probed; testnets are excluded. A 200, 404, 5xx or timeout is "listed but not answering 402", not a finding about the seller. The result is a merchant-level reading, not a facilitator one.
  9. Build the fact and the scorecard day (freshnessOf, cellFacts). Last answered on or after the scorecard day means as_of is that day and carried is false; an earlier day means as_of is that day and carried is true; no recorded answer means carried and already stale, with the last-seen day shown as unknown. A held reading (step 5) marks its cells carried. The scorecard groups cells by chain, lists schemes, the union of stated versions (a scheme that stated none is named, not counted as "none"), asset ids and fee-sponsorship counts, and orders facilitators by id. There is no rank or score.
  10. Set the incentive flag (activeIncentives). The scorecard day, the chain, and protocol x402. A chain with no key in the calendar, a testnet, or a namespace-only id that is not an exact known mainnet is unknown, never "none". The calendar says a program was running, not that it moved any number.

3. Limits

  • /supported is the facilitator's own claim. A listed chain and scheme do not show that it settles there, at what fee, with what uptime, or for whom. The cell is a record of what it said and when.
  • A key wall hides the matrix. Where a facilitator answers only with credentials (Coinbase's hosted facilitator is the best-known case), its matrix is not read; the carried cells are the last ones we saw before the wall, and may be out of date. Public documentation of such a facilitator is a separate source and is not merged into the observed cells.
  • Carried is not confirmed. For up to 14 days (30 behind a key wall) a carried matrix is shown, labelled, with its last-seen day when one is on record. A facilitator may have changed in that time.
  • A daily snapshot misses short outages and changes inside a day.
  • Unmapped networks stay raw. A chain the table cannot map to a CAIP-2 id is listed under its raw token with no incentive match; the incentive flag there is unknown.
  • Version and asset fields are as stated. A missing version is unknown. Asset descriptions are type-checked fields only; announced asset names are third-party text.
  • Who pays through a facilitator is not in this data. The settlement transaction's sender identifies the facilitator, but payments are published keyed by payout address. Outside-payer counts and top-payer shares (see the method "The x402 outside-payer filter") therefore appear on the scorecard as unknown. The share of settlements whose payers were funded by the operator has no data field or run method yet; it also appears as unknown, with that reason.
  • Partner listings are coverage, not endorsement. Being listed, or absent, says nothing about quality or safety.
  • Describes, does not grade. Nothing on the scorecard is a safety, quality or value judgement of a facilitator, and nothing in it is an instruction to use or avoid one.

Cite as

Sato Hub, "Method: reading an x402 facilitator's matrix", version 2, 2026-10-13, CC BY 4.0, satohub.ai.