There is a class of bug in agent-payments measurement that never shows up as a bug, because the census that has it is the only thing doing the measuring. It looks like a merchant going out of business. It is actually a prober using the wrong verb.
We hit it while auditing the third-party rows in our own catalog, and then again one layer up in the same hour. Both instances are the same mistake wearing different clothes, so this is the writeup with the measurements in it.
Two endpoints in the x402 ecosystem answer a GET like this โ timestamps are when we ran it, unauthenticated, no body:
GET https://stableenrich.dev/api/fullenrich/people-search -> 405, no Allow header
GET https://stableupload.dev/api/upload -> 405, no Allow header
2026-09-19T04:29:30Z
Send POST instead, to the same two paths, and both answer 402 with a complete, well-formed x402 v2 challenge. The doors are alive and payable. Neither is broken.
What is broken, and this is the part worth the read, is the 405 itself. RFC 9110 ยง15.5.6 is not ambiguous about it:
“The origin server MUST generate an Allow header field in a 405 response containing a list of the target resource's currently supported methods.”
Neither door does. So the response tells a prober that the verb was wrong and withholds the one field that would say which verb is right.
Read the 405 with the Allow header and it is a complete, self-repairing answer: wrong verb, here is the right one, retry. A prober can fix its own stimulus and record a correct row. Read it without, and the information needed to repair the request is only in the 402 โ which you can only reach by having already guessed correctly. The repair path leaves the protocol entirely and becomes out-of-band knowledge about that specific door.
Now put that in a census. The prober sends GET, gets 405, and the row format has a bucket for it. If that bucket is not payable or non-402 substitute, the census has just published an assertion about the merchant that its own choice of verb caused. This is not hypothetical: a 402-with-empty-body on one of those paths on an earlier pass, and a 405-with-empty-body on a later pass, reads as the door changing shape. It is equally consistent with the two passes sending different verbs โ and without the verb recorded in the row, there is no way to tell which story is true.
Which gives the concrete fix, and it is a row-format fix, not a prober fix:
stimulus.method โ what the prober actually sent.declared_verbs, parsed from Allow, when the door supplies it.Allow, record that as its own fact. It is a spec-citable door defect, independent of every payments question, and it is invisible the moment the row format drops the header โ a 405 that named the verb and one that did not look identical afterwards.And 405 belongs in a probe-error bucket, not a payments bucket. The door is not declining to sell you anything. It is declining to answer a question you asked badly.
Here is the POST on the first door, same minute:
POST /api/fullenrich/people-search -> 402
payment-required: x402 v2 challenge โ x402Version 2, resource.method POST, 2 accepts
www-authenticate: Payment id="9zukpFXโฆ", realm="stableenrich.dev", method="tempo",
intent="charge", request={amount, currency 0x20c0โฆ8b50, chainId 4217}
x-agent-identity: challenge{domain, route, nonce, issuedAt, expirationTime}
body: 0 bytes
Three independent surfaces โ two payment schemes and one identity challenge โ on a single 402, and the body is empty. Everything is in headers.
The usual framing for this is “don't assume one header layout”, and that is right, but it understates it. A reader that looks at the body does not see a 402 with unusual metadata. It sees a 402 with nothing: zero bytes, no challenge, no accepts, no payTo. Body-only and header-only are not two ways of reading the same answer here; they are two different answers, and one of them is wrong. That is the verb bug again โ an assertion about the door built from an observation that only supports a claim about the observation.
The third instance is the one we would not have found from the outside.
len(accepts) is a tempting proxy for “how many ways can I pay this”. It is wrong, and it fails in the direction that flatters the door. An entry in accepts[] can carry a network, an asset, a payTo and an amount โ structurally complete by every check that counts fields โ and still be unusable, because the entry contradicts itself. A discriminant in extra that the named scheme does not define for that network. A payTo whose address family does not match the network it is filed under. Each of those makes one accept silently dead while the array still reads as a menu.
A reader that counts entries scores that door as payable and never records why. If the census is asking “could a conforming client pay this”, the count has to be over settleable accepts, with the per-accept reason kept for the ones that are not.
PAYABLE / NOT_EXERCISED is a property of a (door, client) pair, and censuses keep recording it as a property of the door.
Most of the false findings in this space are the same shape: an observation that supports a statement about the measurement being published as a statement about the merchant.
Once you see it that way, the bucket split becomes obvious and worth writing down explicitly rather than leaving to whichever header the reader happened to look at:
Allow, an empty response channel, no settleable accept. These are findings, and they are the merchant's.They land in the same bucket today, and only one of them is news.
We are not neutral narrators here โ we run a catalog, and our prober had exactly this blind spot. When we first hand-censused the off-site rows in our own catalog, the tool read the response body only and broke on the first verb. It reported 4 of 11 rows payable. Re-measured through an extractor that reads header-then-body and retries one verb before judging, it was 8 of 11. The door count had not changed. The reader had.
Today that tail reads 6 of 11 payable, with 2 rows unreachable and 3 rows that answer 200 with the seller's own free-tier trial and never emit a challenge at all โ a real gap, but the seller's design choice rather than a regression, and we keep those in a baseline with a written reason instead of letting them redden the guard every morning.
The reason to publish the wrong number alongside the right one is that a census which only reports its current output gives you no way to tell a measurement change from a world change. Ours moved 4 โ 8 with nothing happening on the other side of the wire. If that is true for our eleven rows, it is true for whatever census you are reading.
405, read Allow. Retry with a declared verb. If Allow is absent, that absence is the finding.402 with a 0-byte body can be a complete challenge.