Dev.to WebDev πŸ›  Dev πŸ‘ 0 πŸ“– 5 min read

Your 402 input example is a contract. Ours shipped a placeholder.

This content reflects minia2a as of its publication date and is kept for the record. Current model: x402 pay-per-call in USDC on Base only, 5 free trial calls per signed wallet, no credits and no top-up rail.

This content reflects minia2a as of its publication date and is kept for the record. Current model: x402 pay-per-call in USDC on Base only, 5 free trial calls per signed wallet, no credits and no top-up rail.

Your 402 input example is a contract. Ours shipped a placeholder.

November 12, 2026 Β· measured at 23:05Z

A 402 response does two jobs. It says this costs money, and it tells the caller how to call it. The second job lives in extensions.bazaar.info.input.body β€” an example body. Agents that read a 402 to learn the shape will copy that example. So the example is not decoration. It is the request the caller will send.

Ours was a placeholder. Here is a live one, unpaid, fetched while writing this post:

POST /x402/publish-1793636031257-base-receipt-verify  {}
HTTP/1.1 402

extensions.bazaar.info.input.body:
{
  "amount_micro": "<PLACEHOLDER: amount_micro>",
  "from":         "<PLACEHOLDER: from>",
  "nonce":        "<PLACEHOLDER: nonce>",
  "pay_to":       "<PLACEHOLDER: pay_to>",
  "tx":           "<PLACEHOLDER: tx>"
}

Every value is a sentinel string. Copy it literally and you send five non-empty strings to an endpoint that verifies a payment receipt. That is not a syntactically valid receipt, and it is not an empty body either β€” it is a request that gets past the door.

The measurement

We sample a rotating window of first-party endpoints, POST an empty body with no payment header, and classify the advertised example. This is today's window:

class count meaning
REAL 98 at least one value is a real value
PLACEHOLDER 28 no real value, at least one <PLACEHOLDER: …>
EMPTY_BODY 66 {} β€” the surface names no field
ALL_EMPTY 0 the pre-October shape
PARTIAL 0 mix of real and sentinel values

Window: 192 of 1,883 first-party ids, rotated by UTC day so the same ids are not sampled forever. 28/192 = 15%. All 28 are published third-party listings (x402-publish-*, thirdParty: true, $0.01–$0.02 per call). None of them is a hand-written handler.

Then we swept all of them. The same evening, every one of the 1,883 first-party ids: 42 PLACEHOLDER, 236 EMPTY_BODY, 1,605 REAL, 0 ALL_EMPTY, 0 PARTIAL, 0 unmeasured. So the catalog-wide rate is 42/1,883 = 2%, not 15% β€” the rotation walks the sorted id list and is not a random draw, so a window can over- or under-weight a cluster of listings that were submitted together. One more number makes the trend: the same full sweep on 2026-10-21 measured 5 placeholders out of 1,707. The class grew because listings keep arriving without a verified example, not because old ones changed.

The sentinel itself was an improvement. Until October the fallback emitted {field: ""}, and the reasoning for the change is written in the gateway: an empty string β€œlooks like a usable example”, while a sentinel is visibly not one. That is honest to a human reader. The problem is the next reader.

Why the door does not stop it

The gateway decides whether a caller supplied input in handler_x402.go (requestHasInput, ~line 44): any non-blank string counts. A sentinel is a non-blank string. So a copy-paste buyer is dispatched, not refused at the door, and what happens next is each handler's own business. Both outcomes exist in the tree:

  • truthiness guards pass the sentinel through β€” x402-ai-regex.js:10 is if (!desc), and a sentinel is truthy; the request proceeds to the upstream with a nonsense value;
  • ni() in lib/ops raises a caller error, so those ids answer 400 instead of delivering.

On a paid path, the first shape is the expensive one. β€œInput present + ok:false” is recorded as a failed delivery, and on this platform three failures from distinct wallets take a service off the shelf β€” with no automatic return.

What it actually cost, measured

On the one endpoint where a real buyer is active, we read the delivery ledger rather than assume:

  • 2026-11-11: two paid calls failed upstream with Some of the required values for successive user emulation are missing. . Not valid domain hcaptcha.com. That text names the caller's own input, but the row was booked as cause=service.
  • 2026-11-12: 10 paid calls, 7 delivered, 3 failed. Refunds were issued at 16:45:11Z and 16:45:19Z, $0.10 each, status sent.

Those two failures were not sentinels β€” they were a caller sending a target the upstream refuses. They still belong in this post, because they are the same defect class: a caller's mistake was being charged to the service's strike count. We only saw it because we read the rows.

The fix on our side

Three changes, all in the handler, none of them in the schema an agent reads:

  1. Any input field whose value matches ^<PLACEHOLDER:[^>]*>$ is refused as a caller error (HTTP 400, refund, no delivery-failure row). The sentinel is our marker, not the caller's parameter.
  2. A supplied url must parse as an absolute http(s) URL with a hostname; a bare domain is a caller error.
  3. Unsorted captcha type spellings are normalised before the lookup, so recaptcha-v2 is the recaptcha_v2 bucket rather than an β€œunsupported type” that used to cost the service a strike.

This is latent, not a postmortem. Zero paid calls and zero delivery rows reached any of the 28 placeholder listings in the last 14 days. The trap was measured before it fired. The pattern that did fire β€” caller input booked as service failure β€” is in the ledger twice, on 2026-11-11.

If you publish a 402

An example body is a promise that this request works. If you control your own 402 and have verified one that returned ok:true, ship that β€” it is the single most useful thing a calling agent can read. If you have none, ship {}. An empty map says β€œI am not telling you the fields”; a placeholder says β€œhere are the fields” and then teaches a request that cannot succeed.

On our own publish path there is a constraint worth naming, because it explains why the sentinel exists at all: the publish API takes no input example. The registry stores field names (read from the description's Input: {…} line), not values. So for a listing that only declares fields, the platform has nothing real to put in those slots β€” and until the publish path can accept a verified example, the honest fallback is a value that is visibly not one.

The distinction matters more on x402 than on a normal API, because on a normal API a wrong example gets a 400 before money moves. Here the example is what a buyer pays on.

minia2a sells pay-per-call agent endpoints in USDC on Base. Every 402 carries the challenge, the price, and the input example in the same document. We measure ours daily β€” including the ones that are wrong.

Originally published at minia2a.uk.

πŸ“° Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.