On 12 August 2026 three of our paid endpoints carried a grade of
C on a public x402 trust directory, each with a machine-readable
recommendation attached: avoid. Downtime wasn't the cause. One
missing block in the 402 response body was — and it had failed every probe for
thirty days straight.
This is what the bug is, how to check whether your endpoint has it, and how the scores that punish it are actually computed. If you operate an x402 endpoint, the check at the bottom takes about thirty seconds.
An x402 402 Payment Required response carries its payment
requirements in two places: the PAYMENT-REQUIRED header (base64 JSON)
and the JSON body. Paying clients generally read the header. Indexers, validators
and trust directories generally read the body — it is cheaper to parse and it
doesn't depend on a header that not every implementation sets.
Our envelope was complete in the header. The body carried error,
x402Version, accepts[] and our legacy
payment_options — but no top-level resource object. So
every validator reading the body reached the same verdict:
missing-resource-info.
The measurement was unambiguous once we could see it: 793 probes over 30 days, zero valid envelopes. Payments worked the entire time. Agents paid, tools delivered, receipts settled on-chain. The endpoint was healthy and publicly labelled untrustworthy.
Public directory pages show you a grade. They don't show you which component produced it, which is why this sat unnoticed: a C looks like a reliability problem, so you go hunting through uptime and latency. We bought the paid report instead — half a cent per endpoint, settled over x402 — and it named the component immediately.
With four observations (two endpoints, before and after the fix) the model solves exactly:
score = 0.45 x technicalReliability
+ 0.30 x specCompliance
+ 0.25 x economicReputation
specCompliance = 30 + 70 x (validEnvelopes30d / scoredProbes30d)
grades: A >= 80 B >= 65 C >= 50 D >= 35
That formula is inferred from observation, not published — treat it as a working model rather than gospel, and correct us if your own numbers disagree. But the shape holds, and the shape is the useful part:
| component | ours, before | what it measures |
|---|---|---|
| technicalReliability | 86 | uptime, weighted with latency |
| specCompliance | 30 | share of probes with a valid envelope |
| economicReputation | 47 | settlement history, payer count, age |
An endpoint that was fundamentally healthy — 86 on reliability — sat at 59
overall because one component was pinned at its floor. Compliance is worth 21
points of grade. That is the difference between avoid and an A.
specCompliance is a ratio over the trailing 30 days, not
a current-state check. Fixing the bug does not restore the grade; it starts a
thirty-day clock. At roughly 26 probes a day into a 793-probe window, a
corrected envelope earns back about 0.7 points of score per day.
So the practical cost of shipping this bug is not the day you find it — it is the month of trailing average you spend climbing out. Every day you leave it unfixed is a day added to the recovery, and the public page caches on top of that. Check yours now rather than next quarter.
Compare what your 402 says in the body against what it says in the header. If
the header has resource and the body doesn't, you have this bug:
curl -sD /tmp/h -o /tmp/b https://your-endpoint.example/your/tool # what validators and directories read: jq 'keys' /tmp/b # what paying SDK clients read: grep -i '^payment-required:' /tmp/h | cut -d' ' -f2 | tr -d '\r' | base64 -d | jq 'keys'
Both lists should contain resource. While you're there, confirm
the body's accepts[] entry carries the standard field names
(payTo, maxAmountRequired, asset,
network) — a generic payer that can't find those will silently skip
your endpoint too, and that failure never appears in your logs as an error.
It is additive. Nothing is renamed, nothing is removed, and no existing client changes behaviour — you are giving body-readers what header-readers already had:
{
"x402Version": 2,
"error": "Payment required",
"accepts": [ ... ],
+ "resource": {
+ "url": "https://your-endpoint.example/your/tool",
+ "serviceName": "What you sell",
+ "description": "One clear sentence",
+ "mimeType": "application/json"
+ }
}
The more durable half of the fix is structural: build the envelope once and emit it to both places, then assert in a test that the body is a superset of the header. This was our third bug in that family — standard fields reaching only the header, then one tool serving two different envelopes on two paths, now this. All three have the same root shape: two copies of the truth, drifting quietly, with only one of them being watched.
Same paid probe, 85 minutes apart, one deploy in between:
| 18:33 UTC | 19:58 UTC | |
|---|---|---|
| valid envelopes (30d) | 0 / 793 | 1 / 793 |
| flags | envelope-noncompliant | cleared |
| recommendation | avoid | caution |
The public grade didn't move that day and won't for a while — trailing window, plus a 24-hour cache on the free page. That is expected, and it is worth writing down somewhere your future self will find it, because the natural instinct a week later is to conclude the fix didn't work and go re-diagnose a solved problem.
Trust scores are becoming the thing agents route on. An autonomous buyer
choosing between two endpoints that both return data will take the one that
isn't labelled avoid — and unlike a human, it will never read your
documentation to discover that the label was about a JSON field rather than your
service. Every report we bought also named a higher-scoring competitor as a
suggested alternative. That is what a compliance bug actually costs: not a bad
grade, a redirected buyer.
The general rule we'd offer: your 402 is your storefront. If you serve one version of it to SDKs and another to crawlers, you will eventually be graded on the one you weren't watching.
Credit where it's due: the directory's free page told us we had a problem, and its paid report told us what the problem was. Half a cent, settled over x402, to find a bug that thirty days of our own green dashboards had missed. Buying a probe of yourself is cheap; assuming your own surface is fine is not.
AgentPay (agentpay.tools) runs paid probes against x402 endpoints — not just liveness checks, but settle-and-verify runs that confirm a service actually delivers after it takes the money. Those results are public, with receipts: