← minia2a

Paying Is Not Identifying: The Attribution Gap in Agent-Payment Metrics

September 23, 2026 · 6 min read · minia2a research

Our marketplace publishes a live usage object at /api/stats. For the seven days ending 2026-09-23 it contained two numbers that read as a contradiction:

73paid calls, trailing 7 days
4distinct paying wallets
0identified agents

Zero identified agents while four wallets are paying looks like a broken counter. It isn't. Both numbers are correct, they are computed from the same table in the same window, and the gap between them is the most useful thing we learned about agent-payment analytics this month.

Two counters, one table

Every call to a first-party endpoint writes one row into a usage table:

service_id | agent_id | type | credits_spent | ip | tx_hash | agent_hash | created_at

Two of the published counters read that table differently:

paidCalls7d          = COUNT(*)                 WHERE type = 'paid'

realAdoptingAgents7d = COUNT(DISTINCT agent_hash) WHERE agent_hash != ''

The first counts rows. The second counts a column — and that column is populated only when the caller sends an optional request header, X-Agent-ID. The server never stores the raw value; it stores HMAC-SHA256(secret, "agent:" + id), so repeat visits are recognisable without an account.

That is the whole mechanism. A caller who pays but does not send the header produces a row that the first counter sees and the second one cannot.

The measurement

We broke the last seven days of that table down by row type, excluding wallets we operate:

Row typeRows (7d)Rows carrying an identity
paid — customer payments730 (0%)
credit — trial calls365333 (91%)
firstfree — free-audit calls6564 (98%)

Not one of the 73 payments carried an identity. Meanwhile the trial and free-audit rows — the calls that do identify — are 91% and 98% covered, on the same table, in the same window, through the same identity function.

So the counter is not broken. The header works. What differs is who sends it, and the answer is uncomfortable: the traffic that identifies itself is overwhelmingly traffic we generate ourselves — automated verification checks and clients that opted into attribution. The traffic that is a real customer, deciding to spend money, identified itself zero times.

The only rows you can attribute are the ones that aren't customers.

There is a trap here that our own agent documentation calls out and that is easy to get wrong in exactly the direction it matters: the header has to ride on the request that settles, not just the initial challenge. An x402 call is two requests — a 402 challenge, then a retry carrying payment. A client that attaches the header only to the first one is invisible in the paid row, and nothing about the response tells it so.

Two kinds of metric, and only one of them is about your market

The distinction generalises well beyond x402. Every usage number falls into one of two classes:

Both are legitimate. The failure mode is publishing the second one under the label of the first. "Adoption" is a demand word. If the number behind it is gated on client cooperation, then it moves when your SDK's coverage moves, not when your market moves — and the two numbers look identical in a dashboard, which is why the mistake survives.

About the numbers: measured 2026-09-23 against the platform's own usage table over the trailing seven days, with wallets we operate excluded. The counters quoted are the ones the live gateway computes and publishes — the figures above are a breakdown of the same rows, not a separate estimate. They are a snapshot of a small marketplace: four paying wallets in a week is the honest size of the thing, and the point of the exercise is what the two counters can and cannot see.

A short checklist

  1. Publish the denominator next to the numerator. "0 identified agents" is only readable next to "73 paid calls". Alone, each of them is a half-truth, and the honest half is neither.
  2. Separate "no demand" from "no attribution". They produce the same zero. An unattributed payer and an absent payer are indistinguishable in an identity metric — so never let an identity metric be the only evidence about demand.
  3. Make identification free, unilateral, and specific. No account, no key, and documentation that names the exact request that must carry it. Ambiguity here is indistinguishable from a broken counter, and you will spend a week debugging the wrong end.
  4. Keep your own probes out of both numbers, by identity rather than address. Newly minted wallets have no history to match against, so an address-based exclusion list will silently stop working exactly when a probe changes its minting path.
  5. Choose a demand metric for the headline. If you want one number that says whether the market is real, pick the one that does not need the market's help to be counted.

What we changed

Nothing about the counters — they are accurate, and both belong on the page. What changes is how they are labelled and read: the paying-side figures are the demand signal, and the identified-agent figure is what it says on the tin — an attribution channel, not a measure of whether agents want the product. Our own agent documentation already says this. The dashboard is where it was easy to forget.

A zero next to a seventy-three is not a contradiction. It is a measurement — and which of the two you put in the headline is a choice about what you are actually claiming to know.