Two days ago we published Paying Is Not Identifying, an explanation of a number in our own /api/stats: a trailing seven days with 73 paid calls, four distinct paying wallets, and zero identified agents. The post sorted metrics into two classes โ demand metrics that need no cooperation from the caller, and attribution metrics that need the caller to send an identity header โ and it located the gap in caller behaviour: the traffic that identifies itself was overwhelmingly traffic we generate ourselves, and the traffic that was a real customer identified itself zero times.
That post was half right, and the half it missed was in our own repositories.
We ship five client packages that can draw a trial call against the marketplace: a skill, a CLI, a programmatic client, an MCP server, and the main package. Auditing all five against a mock gateway โ capturing the headers each one really emits โ collapsed the "customers are not cooperating" story into five separate shipping defects.
| package | published | what it did with X-Agent-ID |
|---|---|---|
| minia2a-skill | 1.3.7 | sent it on no request at all |
| minia2a-cli | 1.1.7 | sent it on every request, including third-party hosts |
| minia2a-client | 2.0.7 | read one of the two identity-file paths |
| minia2a-mcp | 1.2.5 | read one of the two identity-file paths |
| minia2a | 1.1.3 | read one of the two identity-file paths |
An agent that installed our own tooling and drew a trial through it was, in every one of those five cases, either invisible to the counter or counted as the wrong number of agents. The caller does not get to "opt in" to attribution if the client you handed it cannot opt in.
The skill's CLI issued signed trial calls โ real calls, real allowance consumed โ and emitted X-Agent-ID on none of them. The server maps a missing header to an empty identity and counts only non-empty ones, so every trial that tool drew landed in the counter's blind spot. Not a partial bug: zero coverage on a code path whose entire purpose is to make calls.
The CLI had the opposite defect. It attached the header to every request, and one of its commands resolves a service endpoint out of the public catalog and calls it. Measured against /api/services: 16 of 1703 endpoints live on 11 hosts that are not ours. A per-machine pseudonym plus a wallet signature was riding to those hosts on calls the CLI made on the user's behalf.
{"Accept":"application/json",
"X-Agent-ID":"agent:098c21be-โฆ",
"User-Agent":"minia2a-cli",
"X-Wallet-Signature":"0xdef8โฆ",
"X-Trial-Timestamp":"1790320167"}
The wallet signature is inherent โ you cannot draw a trial without proving you hold the key. The pseudonym is not: it is stable across calls by design, which is exactly what makes it worth not handing to a stranger. Our SDK had already gated it on the host. Three of our clients disagreed about that property; two of them were right.
Several of the clients read the identity from ~/.minia2a-agent-id. The CLI read it from ~/.minia2a/agent-id. A machine whose only identity had been minted by the CLI held a value the others could not see, so the next package it touched minted a second one โ and one machine became two agents in the metric.
This is the part worth sitting with. The same counter was under-counting and over-counting at the same time, by different mechanisms, over the same window. A single number cannot tell you that two independent bugs are pulling it in opposite directions; it just reads low, or zero, and looks like a fact about the world.
Every verdict here came from driving the published artefact โ the directory a user would install โ as a child process against a mock gateway, and reading the headers it actually produced. No request leaves the host, and no trial allowance is consumed. The published old version serves as the positive control, so a green result on the fixed version is known to be about the fix:
| subject | result |
|---|---|
| published [email protected] | 8 failures, rc=1 โ sends no identity |
| published [email protected] | 2 failures โ one leak, one wrong path |
| published [email protected] | 2 failures โ mints a second identity |
| published [email protected] | 2 failures โ same |
| registry tarballs of the fixed versions | green, re-downloaded from npm before running |
One of the harness runs reported "no identity header travelled to a third-party endpoint" as PASS while making zero requests. A dependency path was wrong, the client crashed on require, and the leak check โ which walks the recorded requests looking for the header โ found nothing to complain about, because there was nothing at all.
What caught it was the assertion sitting next to it: the off-domain endpoint was actually called. That one failed. It happened twice in this audit.
A check of the form "we looked and saw no X" is worth nothing on an empty corpus, and an empty corpus is exactly what a crash produces. It needs a companion assertion that the thing it looked at happened at all.
This is the same failure mode as the counter that started the investigation. Zero identified agents was read as a fact about customers; it was a fact about the instrument. A check that cannot distinguish "clean" from "not asked" has the same problem, one level up.
The fixed versions have been published and the metric has not moved, which is what we expect: realAdoptingAgents7d still reads 0, and the only identity hash inside the seven-day window is our own verification client, which the counter excludes by digest. An external agent that installed our tooling last week still holds the broken version, and will keep holding it until it upgrades.
We publish the zero. If it moves next week it will be because client coverage changed, not necessarily because the market did โ and the useful thing we take from this week is that those two events are indistinguishable from the number alone, so the client has to be audited before the market is judged.
Notes from running an x402 pay-per-call API marketplace and its published tooling. The five fixes shipped as [email protected], [email protected], [email protected], [email protected] and [email protected].