<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: minia2a</title>
    <description>The latest articles on DEV Community by minia2a (@minia2a).</description>
    <link>https://dev.to/minia2a</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4067568%2Fe5525b84-ef78-4bb5-b31a-efb9e3dfd654.png</url>
      <title>DEV Community: minia2a</title>
      <link>https://dev.to/minia2a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/minia2a"/>
    <language>en</language>
    <item>
      <title>The Most-Used Agent API in October 2026 Costs Two Cents</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Sun, 11 Oct 2026 18:14:23 +0000</pubDate>
      <link>https://dev.to/minia2a/the-most-used-agent-api-in-october-2026-costs-two-cents-5e4b</link>
      <guid>https://dev.to/minia2a/the-most-used-agent-api-in-october-2026-costs-two-cents-5e4b</guid>
      <description>&lt;h1&gt;
  
  
  The Most-Used Agent API in October 2026 Costs Two Cents
&lt;/h1&gt;

&lt;p&gt;2026-10-11 · minia2a&lt;/p&gt;

&lt;p&gt;Every few weeks someone publishes a transaction count for x402 and the number looks enormous — millions of payments, a quarter of a ledger's activity, "agentic commerce is here." Then you look at the dollar value and it is a rounding error.&lt;/p&gt;

&lt;p&gt;A study presented at TOKEN2049 Singapore this month ("The State of AI Agent Payments 2026", BeInCrypto) did the useful thing and put the two numbers next to each other. Over a 35-day sample of x402 traffic on &lt;strong&gt;Base and Solana&lt;/strong&gt; (late July–August 2026):&lt;/p&gt;

&lt;p&gt;6.4M&lt;/p&gt;

&lt;p&gt;x402 transactions — settling &lt;strong&gt;$119,947&lt;/strong&gt; in total, with &lt;strong&gt;90.8% under one cent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And then the most interesting line in the whole report: the &lt;strong&gt;single most-requested paid resource&lt;/strong&gt; was a &lt;em&gt;token safety check&lt;/em&gt; — priced at two cents — called &lt;strong&gt;63,319 times&lt;/strong&gt;. Not an LLM inference call, not a data feed, not a trading signal. A cheap, deterministic, per-call safety check, bought over and over.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why that one line matters more than the transaction count
&lt;/h2&gt;

&lt;p&gt;It tells you where the demand actually is. Agents that hold or move tokens need to answer "is this contract safe to touch?" before they act. That question is: frequent, small, repetitive, machine-verifiable, and worth exactly a few cents per answer. That is the shape of a real M2M microtransaction — not a $40 model call, but thousands of $0.02 checks.&lt;/p&gt;

&lt;p&gt;It also tells you the market is &lt;em&gt;early&lt;/em&gt;, and honestly so. The same report flags concentration: on a single day, 145 addresses made 1,283,926 payments to just three recipients. The XRP Ledger picture is starker still — 13.4 million agentic x402 payments processed, with total settled value reported &lt;strong&gt;under $5,000&lt;/strong&gt;. High counts, tiny value, few actors. Anyone selling "agent payments at scale" should be measured against that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The economics the card networks can't reach
&lt;/h2&gt;

&lt;p&gt;A Coinbase Institute paper published the same week ("Machine-to-machine payments in the AiFi era", Oct 7, 2026) makes the structural argument plainly: a flat card fee destroys sub-cent payment. A $0.30 interchange fee is a &lt;strong&gt;30,000% overhead&lt;/strong&gt; on a $0.001 API query. There is no tweak to the card rails that fixes that; you need a native rail where the fee is on-chain gas, approaching zero. The paper demonstrated a 0.01 USDC transaction on Base settling in about two seconds with network cost under $0.001, via x402 + EIP-3009.&lt;/p&gt;

&lt;p&gt;That is the entire reason a two-cent check can be a business. The cost of moving two cents has to be less than two cents. On card rails it isn't; on a stablecoin rail it is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The takeaway for API builders:&lt;/strong&gt; price for the shape of agent demand, not for a human subscription. The most-purchased endpoint in the market today is a two-cent, no-account, per-call check. If your API can answer a small deterministic question, it is already the right product for this market.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where minia2a sits on this
&lt;/h2&gt;

&lt;p&gt;We run a pay-per-call market for x402 endpoints, so the demand-centre fact is directly testable against us. Our /x402/token-security endpoint is a token safety check, and it is priced at exactly the market's number — two cents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://minia2a.uk/x402/token-security | python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool
  accepts[0].amount    &lt;span class="o"&gt;=&lt;/span&gt; 20000                                  &lt;span class="c"&gt;# microUSDC = $0.02&lt;/span&gt;
  accepts[0].asset     &lt;span class="o"&gt;=&lt;/span&gt; 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913  &lt;span class="c"&gt;# USDC&lt;/span&gt;
  accepts[0].network   &lt;span class="o"&gt;=&lt;/span&gt; eip155:8453                             &lt;span class="c"&gt;# Base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No account, no API key — the endpoint answers a plain 402 Payment Required with the price, and a signed wallet gets five free trial calls before it pays anything. That is the full recipe for the demand the report measured: a machine that needs one small answer, one small payment, no human in the loop.&lt;/p&gt;

&lt;p&gt;We are not going to pretend the volume is large. It isn't — for us or for anyone in this market yet. What is real is the &lt;em&gt;direction&lt;/em&gt;: the paid resource agents actually buy is a cheap per-call safety check, and the settlement rail they actually use is Base. We serve both. The transaction graphs everywhere are flat because the market is young, not because the shape is wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try the two-cent check yourself.&lt;/strong&gt; &lt;br&gt;
Five free trial calls per signed wallet. No registration.&lt;/p&gt;

&lt;p&gt;Sources: BeInCrypto, "The State of AI Agent Payments 2026" (launched TOKEN2049 Singapore, Oct 7–8, 2026); Coinbase Institute, "Machine-to-machine payments in the AiFi era" (Oct 7, 2026); XRP Ledger agentic-payment figures as reported by PrimeXBT / AInvest / tokenpost. The minia2a 402 figures above were read live from &lt;a href="https://minia2a.uk/x402/token-security" rel="noopener noreferrer"&gt;https://minia2a.uk/x402/token-security&lt;/a&gt; on 2026-10-11.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://minia2a.uk/blog/most-used-agent-api-two-cents" rel="noopener noreferrer"&gt;minia2a.uk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>web3</category>
      <category>cryptocurrency</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Empty Example: a discovery layer that hands your agent a call it cannot make</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Wed, 07 Oct 2026 04:02:02 +0000</pubDate>
      <link>https://dev.to/minia2a/the-empty-example-a-discovery-layer-that-hands-your-agent-a-call-it-cannot-make-4jcl</link>
      <guid>https://dev.to/minia2a/the-empty-example-a-discovery-layer-that-hands-your-agent-a-call-it-cannot-make-4jcl</guid>
      <description>&lt;h1&gt;
  
  
  The Empty Example: a discovery layer that hands your agent a call it cannot make
&lt;/h1&gt;

&lt;p&gt;October 7, 2026&lt;/p&gt;

&lt;p&gt;When an x402 resource wants to be called, it advertises itself in a 402 response. For a machine caller the price is not the part that matters most — the part that matters is the &lt;strong&gt;input example&lt;/strong&gt;: the shape of the body it is supposed to send. When that example is {"from": "", "to": "", "value": ""} — correct field names, empty values — the agent has everything it needs to construct a request, and no reason to suspect the request will be rejected. It copies the example. It sends it. It gets a 400.&lt;/p&gt;

&lt;p&gt;This is a trap we spent a week of paid-call rejections discovering on our own endpoints, and it is worth writing down because its shape is subtle: an empty template does not &lt;em&gt;look&lt;/em&gt; broken, and a crawler has no way to tell it from a real one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the example comes from
&lt;/h2&gt;

&lt;p&gt;A discovery layer — a bazaar index, a /.well-known/ manifest, a registry — reads the metadata a resource publishes about its own inputs. In the x402 bazaar extension that field is extensions.bazaar.info.input.body: a sample request body the caller (and the crawler that indexes the seller) is meant to read.&lt;/p&gt;

&lt;p&gt;The producer almost never hand-writes these. It builds them through a fallback chain: a curated map first, then a machine-generated map of examples that were actually exercised, and — when neither has an entry — a schema-derived fallback. It is the last branch that hurts. With nothing else to go on, it emits one key per declared field, each with an empty string value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every name is correct. Every value is a blank. The shape is valid JSON, the fields are the real fields, and the example will be rejected by the handler's own validators — from required, value required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;{field:""}&lt;/code&gt; is worse than &lt;code&gt;{}&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The two payloads are a few bytes apart and mean opposite things:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Example body&lt;/th&gt;
&lt;th&gt;What it tells an agent&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;{}&lt;/td&gt;
&lt;td&gt;This resource takes no input.&lt;/td&gt;
&lt;td&gt;Agent sends nothing, call succeeds.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{"a": "", "b": ""}&lt;/td&gt;
&lt;td&gt;This resource takes fields &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Agent fills or copies them, handler validates, call &lt;strong&gt;fails&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A consumer cannot tell a &lt;em&gt;template&lt;/em&gt; from an &lt;em&gt;example&lt;/em&gt;. Both are objects with keys; only one is safe to send. This is the same failure class as a 200 response whose body says ok:false: the outer layer reports health and the inner layer reports the opposite, so the honest signal is the one the reader was told to ignore.&lt;/p&gt;

&lt;p&gt;2&lt;/p&gt;

&lt;p&gt;Of the two ways to advertise "no required input", only one is reachable by accident. An empty map is a deliberate statement. A blank template is what a generator writes when it has nothing to say — and it looks like a promise.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it cost us
&lt;/h2&gt;

&lt;p&gt;We measured rejections on our own paid endpoints. In the window ending 2026-09-19, &lt;strong&gt;16 of 69&lt;/strong&gt; paid calls were rejected. In the window ending 2026-09-28, &lt;strong&gt;32 of 90&lt;/strong&gt;. The rejection messages were the handlers' own validators — text required, domain required, expr required — failing on bodies that followed the published example. Roughly a third of paid attempts in that period failed on input &lt;em&gt;shape&lt;/em&gt;, not on payment, and the caller had done exactly what the discovery layer told it to do.&lt;/p&gt;

&lt;p&gt;That is the cost of the trap in the only unit that matters: a payment-ready agent was turned away, and nothing in the response pointed at the example as the cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule we adopted
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Never publish a blank template as an example.&lt;/strong&gt; If a resource takes no input, publish {} — never {"a": ""}. The two must not be produced by the same code path, because they are not the same statement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An example is only an example if it ran.&lt;/strong&gt; Our generator's acceptance test is to send the candidate body through the real handler and require ok:true with non-empty content. A body that parses is not a body that works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Curate from the handler's own valid-value domain.&lt;/strong&gt; The unit m2 is in the handler's conversion table; sqm is a plausible-looking synonym that is not. The whole difference between an example and a 400 is that the value came from the code that validates it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-generation must not be destructive.&lt;/strong&gt; An upstream that is transiently unreachable is not evidence that a service's example is wrong. Drop it and the trap re-opens for the callers that endpoint already had.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to check your own discovery layer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;List every resource whose published example is non-empty but whose values are all empty strings. That is the trap population, and it is counted separately from the {} resources — only the second set is a defect.&lt;/li&gt;
&lt;li&gt;Run each example body against the live handler. A good example returns 2xx with content; anything else means the example is a liability, not documentation.&lt;/li&gt;
&lt;li&gt;Check the generator, not just the data. If the fallback branch is reachable, the trap re-appears for every new service that does not get a curated entry.&lt;/li&gt;
&lt;li&gt;Watch the rejection rate on paid calls and read the reasons. A rejection that names a required field is the discovery layer talking, not the caller.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What we are and are not claiming
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;These are our numbers, on our endpoints.&lt;/strong&gt; We did not survey other x402 producers and do not know how common the empty-template fallback is elsewhere. The rejection counts above are ours and measured.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixing it is per-service data, not a switch.&lt;/strong&gt; The fallback is convenient &lt;em&gt;because&lt;/em&gt; it generalizes; the only way to retire an endpoint out of it is to give that endpoint a real, exercised example. That is why it is a rolling cleanup rather than a one-line change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The lesson generalizes past x402.&lt;/strong&gt; Any registry that publishes example payloads — OpenAPI example blocks, GraphQL playground defaults, an MCP tool's sample arguments — can ship a blank template where it means to say "no input". The test is the same in every one: does the published example actually run?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rejection counts (16/69 and 32/90 paid calls) are read from our own delivery records on the dates shown; the valid-value-domain example (m2 vs sqm) is from the handler's own unit table. No figure here is estimated and none is drawn from a third party.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/blog"&gt;← More posts&lt;/a&gt; · &lt;a href="https://minia2a.uk/" rel="noopener noreferrer"&gt;minia2a.uk&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://minia2a.uk/blog/empty-input-example-discovery-trap-october-2026" rel="noopener noreferrer"&gt;minia2a.uk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>x402</category>
      <category>api</category>
      <category>agents</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What an x402 Rail Announcement Actually Certifies</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Mon, 05 Oct 2026 12:50:26 +0000</pubDate>
      <link>https://dev.to/minia2a/what-an-x402-rail-announcement-actually-certifies-2dbe</link>
      <guid>https://dev.to/minia2a/what-an-x402-rail-announcement-actually-certifies-2dbe</guid>
      <description>&lt;p&gt;Three x402 announcements landed inside two weeks: a chain joined the official SDK, a network crossed a ten-million-payment milestone, and a major CDN opened a beta that puts a 402 paywall in front of any domain. Each one is real. None of them, by itself, tells you whether you can get paid on that rail &lt;em&gt;today&lt;/em&gt; — because each is certifying a different thing, at a different stage of the same journey.&lt;/p&gt;

&lt;p&gt;We are a marketplace, not a rail, so we read these announcements the way a buyer does: not "is this good news for x402?" but "if I build on this, does a settlement actually clear?" That question has five separable parts, and a press release usually answers only the first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three announcements, three different claims
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The chain that joined the SDK
&lt;/h3&gt;

&lt;p&gt;A major proof-of-stake chain joined the official x402 standard SDK in September, with an installable package (&lt;code&gt;@x402/cardano&lt;/code&gt;), TypeScript first and Python planned. The specification had merged in June; the implementation commit landed about twelve days before the public announcement. The facilitator passed 166 unit tests and processed a real end-to-end transaction — &lt;strong&gt;on a pre-production network&lt;/strong&gt;. As of the announcement it had not settled on mainnet.&lt;/p&gt;

&lt;p&gt;That is not a knock on the work — shipping a facilitator that passes a real transaction on a testnet is exactly the right first step. It is a statement about what the announcement certifies: &lt;strong&gt;the code is joined, not the money is flowing.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The network that crossed a milestone
&lt;/h3&gt;

&lt;p&gt;A payments facilitator reported that one network crossed ten million x402 transactions (and, in a conference talk two days later, eleven million), driven by AI agents buying compute and data. The same reporting is careful about what the number is: it is &lt;strong&gt;that facilitator's counter&lt;/strong&gt;, not the network's, and it measures &lt;em&gt;messages&lt;/em&gt;, not settled value, not distinct buyers, not unique sellers. Separately, the protocol's originator has reported over a hundred million x402 payments across its two home chains — a different counter again.&lt;/p&gt;

&lt;p&gt;Payment counts are the easiest x402 metric to produce and the hardest to interpret. A hundred million agent-signed messages and a hundred million settled, distinct-buyer purchases look identical in a headline.&lt;/p&gt;

&lt;h3&gt;
  
  
  The CDN that put a paywall on every domain
&lt;/h3&gt;

&lt;p&gt;A major CDN opened a closed beta of a monetization gateway that lets a domain owner charge agents per request. It uses HTTP 402 and x402 authorization, and it settles &lt;strong&gt;USDC on Base&lt;/strong&gt; — the same chain and asset our marketplace settles in. Because it is a beta, it is gated: U.S.-based sellers only, a credit card on file, an account older than 60 days, a proxied zone older than 30 days, and requests priced from $0.001 to $100.&lt;/p&gt;

&lt;p&gt;This is the announcement with the most immediate substance — a real gateway, real early sellers, real settlement — and the one with the most caveats attached: geography, account age, and a per-request price ceiling that does not fit every catalog entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five questions a rail announcement does not answer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Merged, or deployed?&lt;/strong&gt; An SDK package existing is not an implementation serving traffic. Ask: does the package install, is it versioned, and who is running it in production?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Testnet, or mainnet?&lt;/strong&gt; A facilitator that settles on a testnet has proven the code path. Ask the one question that separates the two: &lt;em&gt;has a real mainnet transaction settled, and can I see it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Whose counter is the headline number?&lt;/strong&gt; "Ten million payments" is almost always one operator's view. Ask what it counts — messages or value, all buyers or one, all sellers or one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. What gates apply to me?&lt;/strong&gt; Geography, account age, KYC, a funding instrument, a price ceiling. These decide whether a rail exists for &lt;em&gt;your&lt;/em&gt; address, independent of how good it is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Does the resource accept it?&lt;/strong&gt; The rail being live somewhere does not mean the endpoint you want to pay accepts it. That is decided at payment time, and only one artifact decides it: the 402.&lt;/p&gt;

&lt;h2&gt;
  
  
  The only claim that binds at payment time is the challenge
&lt;/h2&gt;

&lt;p&gt;Every other layer — press release, SDK, facilitator, gateway — is upstream of the one document that actually constrains a payment: the &lt;code&gt;402&lt;/code&gt; challenge the resource returns when an unpaid caller asks for it. That document lists the rails the resource will settle on, and if the rail you read about is not in it, no amount of "x402 support" elsewhere will help you pay.&lt;/p&gt;

&lt;p&gt;You can read it without paying anything. Request the resource, and decode the payment terms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# an unpaid request returns the machine-readable terms&lt;/span&gt;
curl &lt;span class="nt"&gt;-sDi&lt;/span&gt; https://minia2a.uk/x402/time | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\r'&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'^PAYMENT-REQUIRED'&lt;/span&gt;

&lt;span class="c"&gt;# base64-decode the header and read accepts[]:&lt;/span&gt;
&lt;span class="c"&gt;#   scheme             exact | upto&lt;/span&gt;
&lt;span class="c"&gt;#   network            eip155:8453        &amp;lt;-- the rail that will settle&lt;/span&gt;
&lt;span class="c"&gt;#   asset              the token address&lt;/span&gt;
&lt;span class="c"&gt;#   payTo              where value lands&lt;/span&gt;
&lt;span class="c"&gt;#   maxAmountRequired  the price&lt;/span&gt;
&lt;span class="c"&gt;#   extra              EIP-712 domain the wallet signs against&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;network&lt;/code&gt; field is the ground truth. A rail is "supported" for you on a given endpoint if and only if it appears in that endpoint's &lt;code&gt;accepts[]&lt;/code&gt;. Everything above it — the SDK you installed, the milestone you read, the gateway in beta — is a claim about the ecosystem, not a promise about this resource.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A buyer's shortcut:&lt;/strong&gt; before trusting any rail claim, read one 402 from the endpoint you actually intend to call. If you want to read more than one, most x402 catalogs offer a free trial allowance so you can enumerate challenges without spending — on our own we allow five signed trial calls per wallet, no registration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where this leaves a marketplace
&lt;/h2&gt;

&lt;p&gt;Our honest position, stated the same way we would want a rail operator to state theirs: minia2a settles &lt;strong&gt;USDC on Base only&lt;/strong&gt; today. We list more than a thousand endpoints, and every one of them declares that single rail in its &lt;code&gt;accepts[]&lt;/code&gt;. When a second rail is genuinely live on mainnet — not merged, not testnet, not one operator's counter, but settling through a facilitator we can actually reach — we will add it and say so, and the 402 is where you will be able to verify it rather than take our word.&lt;/p&gt;

&lt;p&gt;An SDK merge, a milestone, and a gateway beta are three real and encouraging steps for the protocol. But "a rail exists in the ecosystem" and "I can pay this endpoint on it" are different sentences, and the distance between them is exactly the checklist above. Read the challenge; it is the only part of the stack that cannot overstate itself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sources for the three announcements: the Cardano Foundation's SDK-integration announcement (September 21, 2026; package &lt;code&gt;@x402/cardano&lt;/code&gt;; testnet-only settlement at announcement) as reported by CoinDesk and CoinMarketCap community coverage; XRP Ledger payment-count figures as reported by t54 at XRP Seoul 2026 (October 3, 2026) with the same reporting noting the counts are facilitator-scoped; and the Cloudflare Monetization Gateway closed-beta coverage (September 30–October 1, 2026) via Search Engine Journal. Protocol-originator aggregate figures are as publicly reported.&lt;br&gt;
minia2a publishes its own live numbers at &lt;a href="https://minia2a.uk/api/stats" rel="noopener noreferrer"&gt;/api/stats&lt;/a&gt;; the settlement-chain claim above is enforced in our own guard corpus against those numbers.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://minia2a.uk/blog/x402-rail-announcement-checklist-october-2026" rel="noopener noreferrer"&gt;minia2a.uk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>x402</category>
      <category>ai</category>
      <category>webdev</category>
      <category>payments</category>
    </item>
    <item>
      <title>The 429 Your Agent Client Can't Read: designing rate limits for machine callers</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Mon, 05 Oct 2026 03:55:30 +0000</pubDate>
      <link>https://dev.to/minia2a/the-429-your-agent-client-cant-read-designing-rate-limits-for-machine-callers-3925</link>
      <guid>https://dev.to/minia2a/the-429-your-agent-client-cant-read-designing-rate-limits-for-machine-callers-3925</guid>
      <description>&lt;h1&gt;
  
  
  The 429 Your Agent Client Can't Read
&lt;/h1&gt;

&lt;p&gt;A rate-limit response has two readers: a human debugging it in a terminal, and a retry loop parsing it in a program. Most APIs write it for the first reader and forget the second — and the second is the one that decides whether the next second brings one request or ten thousand.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one field a retry loop actually reads
&lt;/h2&gt;

&lt;p&gt;When a service answers &lt;code&gt;429 Too Many Requests&lt;/code&gt;, the mechanism the caller is supposed to use to back off is the &lt;code&gt;Retry-After&lt;/code&gt; header. It is in the HTTP spec for exactly this (RFC 9110 §10.2.3), and the retry machinery in common clients already honors it without the application doing anything: Python's &lt;code&gt;urllib3&lt;/code&gt; and &lt;code&gt;requests&lt;/code&gt; &lt;code&gt;Retry&lt;/code&gt; classes respect it by default, and every serious HTTP gateway and service mesh has a knob for it.&lt;/p&gt;

&lt;p&gt;What none of that machinery does is parse &lt;em&gt;your&lt;/em&gt; JSON body. A generic client does not know that your error document carries a field named &lt;code&gt;retryAfter&lt;/code&gt;, or &lt;code&gt;retry_after&lt;/code&gt;, or &lt;code&gt;retryAfterSeconds&lt;/code&gt; — you invented the name, and only clients that read your docs will ever look inside. So a retry hint that exists &lt;strong&gt;only&lt;/strong&gt; in the body is addressed to a reader that, in practice, is not there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The asymmetry in one line:&lt;/strong&gt; a header is read by default; a body field is read only if the caller already knows its name. Putting your retry hint in the body is not wrong — it is optional to the one reader that acts on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we found in our own API
&lt;/h2&gt;

&lt;p&gt;We went looking at this because of a monitoring bug, not a client one. While reviewing why one of our own internal checks had gone quiet on a large sweep, we read our rate-limit response at the source. The anonymous gate on our catalog routes answers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;429&lt;/span&gt; &lt;span class="ne"&gt;Too Many Requests&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"rate limit exceeded"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"retryAfter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is &lt;code&gt;retryAfter&lt;/code&gt; — a &lt;strong&gt;body field, in seconds&lt;/strong&gt;. There is no &lt;code&gt;Retry-After&lt;/code&gt; header on the response. A caller that reads the JSON and knows our field name backs off exactly right; a caller using stock retry middleware sees a bare 429 with no timing and does whatever its default is, which is often "retry immediately, then again."&lt;/p&gt;

&lt;p&gt;We also noticed a second, quieter inconsistency: we &lt;em&gt;do&lt;/em&gt; send a &lt;code&gt;Retry-After&lt;/code&gt; header on our database-unavailable path, and the two responses are otherwise similar enough that an integrator would reasonably assume the same shape applies to both. One carries the standard signal, the other carries a private one.&lt;/p&gt;

&lt;p&gt;We found this while reading our own source, which is the cheap way. The expensive way to find it is to watch an agent client quietly turn a soft limit into a hard one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it costs you, not just your caller
&lt;/h2&gt;

&lt;p&gt;The bill for an unreadable retry hint comes back to the server. A client that cannot tell "wait 60 seconds" from "wait forever" usually picks one of two bad defaults: retry tightly until something changes, or give up and never come back. The first is a self-inflicted flood — the exact traffic your limiter exists to stop — and it inflates your own 429 counters until they stop distinguishing a real abuser from an ordinary client that simply could not read you. The second loses you the client silently.&lt;/p&gt;

&lt;p&gt;There is a second-order cost, too. When your monitoring and your product share an origin, a rate limit is also a fact your own checks have to interpret correctly. A sweep that hammers its own edge until it is throttled learns nothing about the endpoints and something misleading about the response codes — a 429 read as a defect is a phantom failure, and a 429 read as "no data" is a false all-clear. We have hit both shapes. The fix on the client side is to treat a throttle as a distinct outcome from a defect; the fix on the server side is to make the throttle impossible to misread.&lt;/p&gt;

&lt;h2&gt;
  
  
  A short checklist for agent-facing APIs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Send &lt;code&gt;Retry-After&lt;/code&gt;.&lt;/strong&gt; It is one line, it is in the spec, and every retry library honors it. Body fields are for humans and for callers who read your docs; the header is for the caller who did not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you also put the hint in the body, match the header.&lt;/strong&gt; Two sources of the same number that can disagree is worse than one. If they can only ever be equal, say so in the docs and in a test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not let 429 mean several different things.&lt;/strong&gt; "Slow down," "you already registered," and "daily quota spent" are three different actions — back off, stop, come back tomorrow. If they share a status code, distinguish them in a field &lt;em&gt;and&lt;/em&gt; in your docs, and keep the machine-readable one stable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Say what the limit is scoped to.&lt;/strong&gt; Per IP, per wallet, per key, per route group — the caller cannot infer it, and guessing wrong is how a correctly-written client still gets throttled by a limit it never read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not page on 429.&lt;/strong&gt; It is a request for patience, not an outage. A monitor that treats it as a failure will lie to you the first time your own traffic is healthy but fast.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is exotic. It is the difference between a limit that shapes traffic and a limit that a client simply cannot see.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We operate an x402 pay-per-call API marketplace and hit our own edge with the same clients agents do. The 429 shape quoted above is read from the deployed gateway source, not from memory. No numbers in this post are invented; where we describe our own behavior, it is the behavior of the running service.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://minia2a.uk/blog/x402-rate-limit-429-for-agents-october-2026" rel="noopener noreferrer"&gt;minia2a.uk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>http</category>
      <category>ai</category>
    </item>
    <item>
      <title>What Happens When Academics Stress-Test 15 x402 Payment Gateways: An Operator's Security Audit</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Sun, 04 Oct 2026 22:00:53 +0000</pubDate>
      <link>https://dev.to/minia2a/what-happens-when-academics-stress-test-15-x402-payment-gateways-an-operators-security-audit-4ne8</link>
      <guid>https://dev.to/minia2a/what-happens-when-academics-stress-test-15-x402-payment-gateways-an-operators-security-audit-4ne8</guid>
      <description>&lt;p&gt;Last week, researchers from EPFL, Zhejiang University, and USENIX Security 2026 dropped a paper that should make every x402 operator stop and audit their code: &lt;strong&gt;31 security vulnerabilities across 15 facilitators&lt;/strong&gt;, covering 99% of x402 transaction volume. Every single facilitator they tested violated at least one payment verification or settlement rule.&lt;/p&gt;

&lt;p&gt;We run a facilitator in production. When the paper landed, we had two choices: skim the abstract and move on, or take it seriously and audit our own code against every attack class they described. We chose the latter.&lt;/p&gt;

&lt;p&gt;Here's what we found, what we fixed, and a practical audit checklist for anyone operating an x402 payment gateway.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Paper: A Quick Summary
&lt;/h2&gt;

&lt;p&gt;The EPFL/Zhejiang team (arXiv:2607.19545) built an automated testing framework that systematically probes x402 payment gateways for violations of the protocol's three core guarantees:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Payment verification&lt;/strong&gt; — the facilitator must cryptographically verify that payment was made before delivering the service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Settlement finality&lt;/strong&gt; — once verified, the settlement must either succeed atomically or fail without delivering the service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource protection&lt;/strong&gt; — the facilitator must not allow attackers to consume paid resources without paying (gas abuse, replay, double-spend)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;They classified attacks into four families:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attack Class&lt;/th&gt;
&lt;th&gt;What It Exploits&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Free Shopping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Settle failure → still serve. Invalid signatures accepted. Zero-amount payments pass.&lt;/td&gt;
&lt;td&gt;Attacker gets paid service for free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Asset Theft&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Facilitator wallet compromise. Refund-to-attacker. Settlement amount manipulation.&lt;/td&gt;
&lt;td&gt;Attacker drains facilitator funds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service Denial&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Replay attacks. Sig reuse. Malicious payloads causing facilitator crash.&lt;/td&gt;
&lt;td&gt;Legitimate agents can't pay or get service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gas Abuse&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Facilitator pays gas for malicious txns. Spam causing repeated on-chain ops.&lt;/td&gt;
&lt;td&gt;Facilitator bleeds gas fees&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The paper reports that Coinbase, PayAI, and Mogami had acknowledged six of the vulnerabilities by February 2026 — some already fixed, others still being addressed. But the broader finding — &lt;em&gt;every facilitator had at least one vulnerability&lt;/em&gt; — is the real headline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Audit: Methodology
&lt;/h2&gt;

&lt;p&gt;We run a production x402 facilitator handling payment for 326 services. The codebase spans four files that matter for payment security:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;proxy.js&lt;/strong&gt; (2,472 lines) — the main gateway: receives requests, checks trials/credits, forwards to services, injects payment headers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;x402.js&lt;/strong&gt; (1,163 lines) — the payment engine: signature parsing, facilitator verify/settle, on-chain verification, receipt generation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;receipts.js&lt;/strong&gt; — cryptographic receipt signing and verification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;payment.js&lt;/strong&gt; — payment model: amount calculation, network routing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our audit mapped each attack class from the paper to specific code paths, then traced every path manually to verify the control flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free Shopping: The Most Critical Check
&lt;/h3&gt;

&lt;p&gt;This is the big one. Free Shopping means "attacker gets paid service without paying." The paper found it in the majority of facilitators. The root cause is almost always the same pattern: &lt;strong&gt;settle failure after verify success, but the service is still delivered&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's the correct control flow for a paid request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Parse&lt;/span&gt; &lt;span class="nx"&gt;payment&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;HTTP&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Call&lt;/span&gt; &lt;span class="nx"&gt;facilitator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;If&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;402&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;don&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t serve)
4. Call facilitator.settle(verifiedPayment)
5. If !settle.success → return 402 (don&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="nx"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="nx"&gt;THIS&lt;/span&gt; &lt;span class="nx"&gt;IS&lt;/span&gt; &lt;span class="nx"&gt;THE&lt;/span&gt; &lt;span class="nx"&gt;CRITICAL&lt;/span&gt; &lt;span class="nx"&gt;CHECK&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Generate&lt;/span&gt; &lt;span class="nx"&gt;receipt&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Forward&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Return&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;receipt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 5 is where most facilitators fail. They verify successfully (step 3), then if settlement fails (step 5), they either:&lt;br&gt;
 (a) serve the content anyway — "the payment was verified, settlement will probably go through"&lt;br&gt;
 (b) log an error and continue — "we'll reconcile later"&lt;br&gt;
 (c) return a partial response — "here's a degraded version"&lt;/p&gt;

&lt;p&gt;All three are Free Shopping vulnerabilities.&lt;/p&gt;

&lt;p&gt;Our main proxy path (proxy.js → x402.js) passed this check: the &lt;code&gt;processPayment()&lt;/code&gt; function properly returns null on settlement failure, and the caller returns 402 without forwarding the request. &lt;strong&gt;The primary payment path is secure.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What We Found: One Real Vulnerability
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Vulnerability Found&lt;/strong&gt; — x402.js &lt;code&gt;processPayment()&lt;/code&gt; function: when settlement failed, two code paths (gas and api-review endpoints) still delivered the service response instead of returning 402. The settlement call returned an error, but the function continued execution and returned the paid service data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's what the vulnerable code pattern looked like (simplified):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// VULNERABLE PATTERN (simplified)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;priceCents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;networkId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Step 1: Verify signature — this works correctly&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;verifyResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;facilitatorVerify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;priceCents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;networkId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;verifyResult&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;verifyResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ✅ Correct: no verify → no service&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 2: Settle — but settlement failure was NOT handled&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;settleResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;facilitatorSettle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;verifyResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ❌ BUG: settleResult could be null or failed, but code continues&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 3: Return paid service data&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generateReceipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;verifyResult&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchServiceData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;verifyResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceEndpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="c1"&gt;// ❌ Service delivered even when settlement failed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix was straightforward: check the settlement result and bail out if it failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// FIXED&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;priceCents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;networkId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;verifyResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;facilitatorVerify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;priceCents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;networkId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;verifyResult&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;verifyResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;settleResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;facilitatorSettle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;verifyResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;settleResult&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;settleResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[x402] SETTLE FAILED — returning 402, no service delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ✅ Fixed: settle failure → no service&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generateReceipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;settleResult&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchServiceData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;verifyResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceEndpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was a real bug. In the narrow window between verify and settle, if the facilitator returned a settlement error (network blip, facilitator-side issue, gas spike), the service would still be delivered. The attacker would get a paid API call for free.&lt;/p&gt;

&lt;p&gt;We deployed the fix with zero-downtime reload, verified all four affected endpoints returned correct 402 responses, and the backup files are timestamped and stored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit Results: Attack-by-Attack
&lt;/h2&gt;

&lt;p&gt;Here's how our facilitator scored against every attack class in the paper:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attack Class&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free Shopping — settle failure&lt;/td&gt;
&lt;td&gt;🔴 Found &amp;amp; Fixed&lt;/td&gt;
&lt;td&gt;Fixed in processPayment(); settle failure now returns null → 402&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free Shopping — invalid sig&lt;/td&gt;
&lt;td&gt;🟢 Safe&lt;/td&gt;
&lt;td&gt;facilitatorVerify() properly validates all signatures before returning isValid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free Shopping — zero amount&lt;/td&gt;
&lt;td&gt;🟢 Safe&lt;/td&gt;
&lt;td&gt;Minimum 1 cent enforced; schema.exact validation catches zero amounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asset Theft — wallet keys&lt;/td&gt;
&lt;td&gt;🟢 Safe&lt;/td&gt;
&lt;td&gt;Platform wallet is env-var only; no private key in code or logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asset Theft — refund redirect&lt;/td&gt;
&lt;td&gt;🟡 Low Risk&lt;/td&gt;
&lt;td&gt;Refunds go to payment source address (on-chain tx); facilitator wallet not used for refunds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service Denial — sig replay&lt;/td&gt;
&lt;td&gt;🟢 Safe&lt;/td&gt;
&lt;td&gt;sigUsed() / lockSig() dedup; each signature usable exactly once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service Denial — tx replay&lt;/td&gt;
&lt;td&gt;🟢 Safe&lt;/td&gt;
&lt;td&gt;txUsed() / lockTx() dedup per transaction hash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas Abuse — spam txns&lt;/td&gt;
&lt;td&gt;🟡 Mitigated&lt;/td&gt;
&lt;td&gt;5-min pending tx timeout; invalid txns cleaned without on-chain cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gas Abuse — malicious payload&lt;/td&gt;
&lt;td&gt;🟢 Safe&lt;/td&gt;
&lt;td&gt;Amount validated client-side before any on-chain operation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What Every x402 Operator Should Check
&lt;/h2&gt;

&lt;p&gt;Based on this audit, here's a practical checklist for anyone running an x402 payment gateway:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Trace your settle failure path
&lt;/h3&gt;

&lt;p&gt;This is the #1 vulnerability. Find every code path that calls settle, and verify that &lt;strong&gt;settle failure means no service delivery, no exceptions&lt;/strong&gt;. Don't trust error handlers that "log and continue." Don't trust try/catch blocks that don't re-throw. The only acceptable behavior on settle failure is returning 402.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Signature deduplication is not optional
&lt;/h3&gt;

&lt;p&gt;If your facilitator doesn't track used signatures (or nonces) and reject replays, an attacker can pay once and call the same paid endpoint infinitely. This is trivial to implement (a Set or Map of used sigs with TTL expiry) but catastrophic to miss.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Zero-amount transactions must be rejected
&lt;/h3&gt;

&lt;p&gt;The x402 challenge format includes &lt;code&gt;maxAmountRequired&lt;/code&gt;. Verify that your settlement code rejects zero-amount payments even if the facilitator says they're valid. A facilitator bug that accepts zero-amount settlements becomes a Free Shopping vulnerability downstream.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Receipts are not optional — they're accountability
&lt;/h3&gt;

&lt;p&gt;The paper notes that facilitators without receipt generation can't provide dispute resolution. If an agent claims they paid but didn't get service, the facilitator has no evidence to resolve the dispute. Cryptographically signed receipts (linking payment txHash → service endpoint → response hash) are the foundation of accountability in agent payments.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Test your 402 responses in production
&lt;/h3&gt;

&lt;p&gt;Set up a monitor that calls your endpoints with invalid signatures and verifies you get a proper 402 response (not a 200 with trial data, not a 500, not a timeout). The EPFL team built automated testers for this — you should too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters Beyond Security
&lt;/h2&gt;

&lt;p&gt;The EPFL paper is a milestone for the x402 ecosystem. It's the first independent, academic security audit of the protocol in production — and it shows that the protocol design is sound (the attack surface is implementation errors, not protocol flaws).&lt;/p&gt;

&lt;p&gt;But there's a bigger story here: &lt;strong&gt;security audits create trust, and trust is what's missing from agent-to-agent payments.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Right now, the biggest barrier to M2M payment adoption isn't technology. The payment rails work — Cloudflare, Coinbase, Stripe, and 40+ x402 Foundation members have proven that. The barrier is that agents (and their developers) don't trust the payment infrastructure. They don't know if their money is safe. They don't know if they'll get what they paid for. They don't know if there's recourse when something goes wrong.&lt;/p&gt;

&lt;p&gt;Independent security audits — and operators who publicly share their results — are part of building that trust. Every facilitator that passes an audit and publishes the results makes the whole ecosystem stronger.&lt;/p&gt;

&lt;p&gt;We're doing our part. If you run an x402 facilitator, we encourage you to do the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Wang, Yang, Chen, Ji, Payer — "When HTTP 402 Meets the Blockchain: Risks on Emerging x402 Payments", USENIX Security 2026 (arXiv:2607.19545)&lt;/li&gt;
&lt;li&gt;USENIX Security '26 presentation — &lt;a href="https://www.usenix.org/conference/usenixsecurity26/presentation/wang-qinying" rel="noopener noreferrer"&gt;usenix.org/conference/usenixsecurity26/presentation/wang-qinying&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;x402 Protocol Specification — &lt;a href="https://x402.org" rel="noopener noreferrer"&gt;x402.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cloudflare Wallets documentation — &lt;a href="https://developers.cloudflare.com/wallets" rel="noopener noreferrer"&gt;developers.cloudflare.com/wallets&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; We found and fixed the vulnerability described in this post on August 9, 2026, the same day the EPFL paper came to our attention. No user funds were at risk — the vulnerability was in the service-delivery path, not the fund-custody path. The affected endpoints (gas, api-review) had low paid transaction volume. We're publishing these results to help the x402 ecosystem mature.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://minia2a.uk/blog/x402-facilitator-security-audit-epfl-2026" rel="noopener noreferrer"&gt;minia2a.uk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>x402</category>
      <category>security</category>
      <category>ai</category>
      <category>payments</category>
    </item>
    <item>
      <title>Your x402 Endpoint Isn't in the Bazaar Yet — and It's Probably Not Your Metadata</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Sun, 04 Oct 2026 17:37:51 +0000</pubDate>
      <link>https://dev.to/minia2a/your-x402-endpoint-isnt-in-the-bazaar-yet-and-its-probably-not-your-metadata-mgl</link>
      <guid>https://dev.to/minia2a/your-x402-endpoint-isnt-in-the-bazaar-yet-and-its-probably-not-your-metadata-mgl</guid>
      <description>&lt;h1&gt;
  
  
  Your x402 Endpoint Isn't in the Bazaar Yet — and It's Probably Not Your Metadata
&lt;/h1&gt;

&lt;p&gt;Every few weeks someone in the x402 channels asks the same thing: &lt;em&gt;"I deployed a valid endpoint, why isn't it in the Bazaar?"&lt;/em&gt; The usual first guess is metadata — a missing field, a bad &lt;code&gt;extensions.bazaar&lt;/code&gt; block, a truncated description. Sometimes that's right. But the larger answer is structural, and it's written plainly in the CDP docs: the Bazaar has &lt;strong&gt;no registration step at all&lt;/strong&gt;. A resource gets indexed when a payment &lt;strong&gt;settles through the CDP Facilitator&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Discoverability is metadata; indexing is settlement. Two different gates. Most sellers only ever test the first one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gate one: is your endpoint &lt;em&gt;discoverable&lt;/em&gt;?
&lt;/h2&gt;

&lt;p&gt;This part you control directly, and you can check it before you ever take a payment. CDP exposes a free, unauthenticated validation endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://api.cdp.coinbase.com/platform/v2/x402/validate
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns &lt;code&gt;bazaarExtension.discoverable&lt;/code&gt; plus a list of named &lt;code&gt;preflight[]&lt;/code&gt; checks, each with a &lt;code&gt;severity&lt;/code&gt;. The rule that matters: a failure with &lt;code&gt;severity == "required"&lt;/code&gt; means the resource will never be indexed; &lt;code&gt;advisory&lt;/code&gt; is a warning, not a blocker.&lt;/p&gt;

&lt;p&gt;We ran a stratified sample of &lt;strong&gt;64 live endpoints&lt;/strong&gt; from our own catalog (across price and category) through it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;64 / 64 &lt;code&gt;discoverable = true&lt;/code&gt;, 0 required preflight failures.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a negative control we fed it one endpoint we already knew was retired (&lt;code&gt;x402-language-detect&lt;/code&gt;): &lt;code&gt;discoverable = false&lt;/code&gt;, 21 required failures. The validator is not a rubber stamp — it flips on a genuinely broken resource.&lt;/p&gt;

&lt;p&gt;So our metadata is &lt;em&gt;fine&lt;/em&gt;. And that is exactly the trap. Passing gate one proves nothing about gate two, and gate two decides whether anyone can find you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gate two: has your endpoint &lt;em&gt;settled&lt;/em&gt;?
&lt;/h2&gt;

&lt;p&gt;From the CDP seller docs, verbatim:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Every validated endpoint is eligible for indexing in the CDP Bazaar after a successful settled payment." — with the setup item: "Complete a successful paid call through the CDP Facilitator."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not after registration. Not after validation. After a &lt;strong&gt;settled payment through the CDP Facilitator&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the fact that explains the most common confusion. If your 402 is answered and settled by &lt;strong&gt;some other facilitator&lt;/strong&gt; — or by your own gateway, as ours is — then no CDP settlement ever occurs, and by the docs' own rule your resource is eligible forever and indexed never. It is not a metadata problem. It is a rail condition.&lt;/p&gt;

&lt;p&gt;On the settlement request, two fields must be present or the route is treated as an ordinary 402 and skipped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;paymentPayload.extensions.bazaar&lt;/code&gt; — the metadata block the index stores.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;paymentPayload.resource&lt;/code&gt; — which route it belongs to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The docs put it flatly: &lt;em&gt;"Only routes that declare Bazaar metadata are indexed."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What gets you removed
&lt;/h2&gt;

&lt;p&gt;Being indexed is not permanent. Three documented exits, none of which announces itself:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;30 days with no settlement&lt;/td&gt;
&lt;td&gt;Removed from both the catalog and search results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stops returning &lt;code&gt;402 Payment Required&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Eventually removed from the index entirely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sustained consecutive probe failures&lt;/td&gt;
&lt;td&gt;First down-ranked, then auto-delisted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The middle row is the one to internalize. An endpoint that starts returning &lt;code&gt;200&lt;/code&gt; — because you added an unauthenticated demo path, or a refactor bypassed the payment middleware, or a CDN cached a success response — gets &lt;strong&gt;delisted for being too available&lt;/strong&gt;. The index is a directory of things that charge money; when a thing stops charging, it stops being a useful row.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two edge cases that quietly sink good endpoints
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. TypeScript indexes you by default; Python does not.&lt;/strong&gt; On the CDP SDK's TypeScript building blocks, discovery is automatic — &lt;em&gt;"You do not need to add a discovery setting."&lt;/em&gt; On Python it is explicitly opt-in: you register the Bazaar resource-server extension and declare metadata per route yourself. Same protocol, same 402 shape, opposite default. A Python seller can ship a textbook-correct endpoint and never appear, purely because the language differs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. High-cardinality path segments get collapsed.&lt;/strong&gt; The Bazaar normalizes any path segment that consists &lt;em&gt;entirely&lt;/em&gt; of a high-cardinality identifier — a UUID, a wallet address, a tx hash. If your product is &lt;code&gt;/token/&amp;lt;address&amp;gt;&lt;/code&gt; or &lt;code&gt;/tx/&amp;lt;hash&amp;gt;&lt;/code&gt;, every one of those URLs collapses into a single index entry. Usually the right call for the index, but it means a parameterized API looks like one resource, not thousands. If you need distinct entries, the escape hatch is a prefix or suffix that is not an identifier.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a seller should actually do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validate before you deploy&lt;/strong&gt;, not after. One unauthenticated call names the exact required fields you're missing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirm your settlement rail.&lt;/strong&gt; If being in the CDP Bazaar matters, the payment must settle through the CDP Facilitator. Verify it — nothing in your 402 tells you which facilitator will clear it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send the two fields on the settlement payload&lt;/strong&gt; — &lt;code&gt;extensions.bazaar&lt;/code&gt; and &lt;code&gt;resource&lt;/code&gt;. Static metadata alone is not enough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep answering 402.&lt;/strong&gt; A convenient unauthenticated path is a delisting vector. If you want a free tier, gate it behind the same signed-trial check so the unauthenticated answer stays a 402.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't read a small index count as a metadata failure.&lt;/strong&gt; For most of the catalog it's settlement volume, not schema quality. Check both gates before hunting a bug that isn't there.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Sources: CDP x402 seller docs, "Get discovered" (&lt;code&gt;docs.cdp.coinbase.com/x402/seller/get-discovered&lt;/code&gt;), read 2026-10-04. Validation sample: &lt;code&gt;POST api.cdp.coinbase.com/platform/v2/x402/validate&lt;/code&gt; over 64 live endpoints from our catalog, all &lt;code&gt;discoverable = true&lt;/code&gt; with zero &lt;code&gt;severity: "required"&lt;/code&gt; failures; negative control on a retired endpoint returned &lt;code&gt;discoverable = false&lt;/code&gt;. Quotes attributed to the CDP docs are verbatim.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>x402</category>
      <category>ai</category>
      <category>payments</category>
      <category>api</category>
    </item>
    <item>
      <title>48 Networks, One Winner: What 24,419 x402 Resources Actually Settle On</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Sun, 04 Oct 2026 14:25:04 +0000</pubDate>
      <link>https://dev.to/minia2a/48-networks-one-winner-what-24419-x402-resources-actually-settle-on-5ecp</link>
      <guid>https://dev.to/minia2a/48-networks-one-winner-what-24419-x402-resources-actually-settle-on-5ecp</guid>
      <description>&lt;h1&gt;
  
  
  48 Networks, One Winner: What 24,419 x402 Resources Actually Settle On
&lt;/h1&gt;

&lt;p&gt;"Which chain does this x402 service settle on?" is a question the catalog can answer and the homepage copy usually can't. So we paged the entire CDP x402 discovery index — the largest public x402 catalog — and counted what its resources actually advertise as payment options. The result is cleaner than the marketing: &lt;strong&gt;48 distinct networks appear, and Base appears in 97.9% of resources.&lt;/strong&gt; The interesting part is not that one chain dominates. It is how much of the catalog is one publisher, and how fast it grew without diversifying.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was measured
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;code&gt;https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources&lt;/code&gt; — the CDP x402 discovery index, read-only, no auth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Method:&lt;/strong&gt; paginate the endpoint, combine every page, count the &lt;code&gt;accepts[]&lt;/code&gt; array on each resource. Each entry in &lt;code&gt;accepts[]&lt;/code&gt; is one payment option: a network, an asset, an amount, a payee.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frame:&lt;/strong&gt; &lt;strong&gt;24,419 resources&lt;/strong&gt; on &lt;strong&gt;2026-10-03&lt;/strong&gt;. The same index read &lt;strong&gt;19,126&lt;/strong&gt; resources on 2026-09-29 — up 27.7% in four days.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;A resource is a &lt;em&gt;listing&lt;/em&gt;, and an &lt;code&gt;accepts[]&lt;/code&gt; entry is an &lt;em&gt;advertised&lt;/em&gt; option. Neither is evidence that anyone paid. This is a map of what the catalog says is payable, not of what settled.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Finding 1 — Base is present in 97.9% of resources
&lt;/h2&gt;

&lt;p&gt;Grouping by whether a resource's advertised networks include Base (&lt;code&gt;eip155:8453&lt;/code&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Resources&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Base only&lt;/td&gt;
&lt;td&gt;14,515&lt;/td&gt;
&lt;td&gt;59.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Base + at least one other network&lt;/td&gt;
&lt;td&gt;9,398&lt;/td&gt;
&lt;td&gt;38.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No Base at all&lt;/td&gt;
&lt;td&gt;506&lt;/td&gt;
&lt;td&gt;2.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read together with the asset column below, this says something practical for a buyer agent: if it holds USDC on Base, it can pay &lt;strong&gt;97.9%&lt;/strong&gt; of the advertised catalog. If it holds USDC on any other single chain, it can pay a small fraction of it. The catalog's diversity lives almost entirely in the multi-rail tail, not in the base case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 2 — 48 networks, and the tail is long and thin
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Network (CAIP-2)&lt;/th&gt;
&lt;th&gt;Resources advertising it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eip155:8453&lt;/code&gt; (Base)&lt;/td&gt;
&lt;td&gt;23,913&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;6,638&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eip155:137&lt;/code&gt; (Polygon)&lt;/td&gt;
&lt;td&gt;3,408&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eip155:42161&lt;/code&gt; (Arbitrum)&lt;/td&gt;
&lt;td&gt;2,665&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eip155:84532&lt;/code&gt; (Base Sepolia)&lt;/td&gt;
&lt;td&gt;2,366&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eip155:143&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;910&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eip155:10&lt;/code&gt; (Optimism)&lt;/td&gt;
&lt;td&gt;659&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eip155:43114&lt;/code&gt; (Avalanche)&lt;/td&gt;
&lt;td&gt;655&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;xrpl:0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;641&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eip155:42220&lt;/code&gt; (Celo)&lt;/td&gt;
&lt;td&gt;603&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stellar:pubnet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;599&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;337&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 36 networks below the visible cut are real but small. Note that Base Sepolia — a testnet — carries more than a quarter as many listings as Solana mainnet. A discovery index counts what people list, not what people pay, and testnets list cheaply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 3 — the asset is USDC, and the scheme is &lt;code&gt;exact&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Across every &lt;code&gt;accepts[]&lt;/code&gt; entry in the catalog, the asset is overwhelmingly USD Coin. The five most-quoted assets, by number of entries:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Asset (contract)&lt;/th&gt;
&lt;th&gt;Accept entries&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&lt;/code&gt; (USDC, Base)&lt;/td&gt;
&lt;td&gt;25,186&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&lt;/code&gt; (USDC, Solana)&lt;/td&gt;
&lt;td&gt;6,893&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359&lt;/code&gt; (USDC, Polygon)&lt;/td&gt;
&lt;td&gt;3,430&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0xaf88d065e77c8cC2239327C5EDb3A432268e5831&lt;/code&gt; (USDC, Arbitrum)&lt;/td&gt;
&lt;td&gt;2,711&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0x036CbD53842c5426634e7929541eC2318f3dCF7e&lt;/code&gt; (USDC, Base Sepolia)&lt;/td&gt;
&lt;td&gt;2,371&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The settlement scheme is just as concentrated. Of all accept entries, &lt;code&gt;exact&lt;/code&gt; accounts for &lt;strong&gt;48,257&lt;/strong&gt;; everything else is a long tail — &lt;code&gt;upto&lt;/code&gt; (782), &lt;code&gt;batch-settlement&lt;/code&gt; (52), and a dozen experimental schemes in the single and double digits. A catalog of 24,419 resources is, in practice, a catalog of one asset and one scheme with a decorative fringe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 4 — 2,054 publishers, and the top of the curve is steep
&lt;/h2&gt;

&lt;p&gt;Counting by resource host:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Distinct hosts in the catalog&lt;/td&gt;
&lt;td&gt;2,054&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosts with ≥100 resources&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Share of all resources held by the top 5 hosts&lt;/td&gt;
&lt;td&gt;28.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One host alone lists 3,420 resources. That single fact changes how the whole catalog should be read: a 27.7%-in-four-days jump in the resource count is not 5,000 new teams arriving. It is a handful of bulk publishers pushing rows. The index is inflating faster than it is diversifying, and the two numbers above — 24,419 resources, 2,054 publishers — are the pair to quote together, never the first alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where our own service sits.&lt;/strong&gt; We are one small row on this map: a Base-only, USDC-only, &lt;code&gt;exact&lt;/code&gt;-scheme seller. That is the median of the catalog by rail, not the tail. It is also, deliberately, a single rail — we do not advertise Solana, XRPL, Celo, Stellar, or Algorand, and the 2.1% of resources that take no Base are, for us, unreachable buyers in the other direction. Cross-rail paying is the open problem this data makes visible: the catalog lists a buyer's chain, but a 402 is answered in &lt;em&gt;one&lt;/em&gt; rail, and nothing in the listing reconciles the two.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a builder should take from this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quote the publisher count, not just the resource count.&lt;/strong&gt; "24,419 resources" and "2,054 publishers" are the same catalog; only one of them is a market size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base is the safe default rail, by a wide margin.&lt;/strong&gt; 97.9% of advertised resources accept it. If you ship one settlement rail, ship that one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testnets inflate listings.&lt;/strong&gt; Base Sepolia out-lists XRPL and Stellar mainnets in this index. Filter testnets out before you draw any conclusion about mainnet coverage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Multi-chain" in a listing usually means "one chain plus a fallback."&lt;/strong&gt; 38.5% of resources are multi-network, but the currency is nearly always USDC and the scheme nearly always &lt;code&gt;exact&lt;/code&gt; — the variety is in the chain, not in the payment shape.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Source: &lt;code&gt;api.cdp.coinbase.com/platform/v2/x402/discovery/resources&lt;/code&gt;, full pagination read on 2026-10-03 (24,419 resources). Compared against the same index read on 2026-09-29 (19,126 resources). All figures are counts over the resources' own advertised &lt;code&gt;accepts[]&lt;/code&gt; arrays; nothing here is a settlement figure. Re-derivable from the public endpoint.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://minia2a.uk/blog/x402-catalog-rails-october-2026" rel="noopener noreferrer"&gt;minia2a.uk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>web3</category>
      <category>api</category>
      <category>crypto</category>
    </item>
    <item>
      <title>An Index Is Not a Market: What the x402 Storefront Ships, and What It Leaves Out</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Sat, 03 Oct 2026 13:42:09 +0000</pubDate>
      <link>https://dev.to/minia2a/an-index-is-not-a-market-what-the-x402-storefront-ships-and-what-it-leaves-out-1457</link>
      <guid>https://dev.to/minia2a/an-index-is-not-a-market-what-the-x402-storefront-ships-and-what-it-leaves-out-1457</guid>
      <description>&lt;p&gt;This week the x402 ecosystem got a storefront. Agentic.market — a directory of x402-enabled services, operated by Coinbase and presented as an "app store for agents" — now headlines &lt;strong&gt;2,980 services&lt;/strong&gt;. We are in it. So instead of arguing about it from the outside, we queried its public catalog and checked our own row, field by field, against our live &lt;code&gt;402&lt;/code&gt;. What that exercise shows is a clean line between two things that both call themselves a marketplace: an &lt;strong&gt;index&lt;/strong&gt;, and a &lt;strong&gt;market&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is, pinned to the source
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Agentic.market describes itself as "a directory of x402-enabled services that agents can call with pay-per-request pricing in USDC," with "no registration, no API keys, no rate limits."&lt;/li&gt;
&lt;li&gt;Its footer is explicit about ownership: x402 is owned by the Linux Foundation, and the marketplace itself is &lt;strong&gt;operated by Coinbase&lt;/strong&gt; and is &lt;strong&gt;not an official x402 Foundation product&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;There is a read-only public catalog: &lt;code&gt;GET https://api.agentic.market/v1/services&lt;/code&gt; and &lt;code&gt;.../v1/services/search?q={query}&lt;/code&gt;, both documented in &lt;code&gt;https://agentic.market/llms.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The network figures — roughly &lt;strong&gt;69,000 active agents&lt;/strong&gt;, &lt;strong&gt;165M+ cumulative transactions&lt;/strong&gt;, &lt;strong&gt;$50M volume&lt;/strong&gt;, ~85% settling on Base — are &lt;em&gt;self-reported by Coinbase&lt;/em&gt; (CDP engineering lead Erik Reppel) and carried by several outlets. We did not independently verify them, and neither should you.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note the actor: the storefront is the protocol steward's own commercial product, built on a standard it governs through a foundation. That is fine to build — but it means the storefront's incentives and the standard's neutrality are not the same thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Our own row, measured
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GET /v1/services/search?q=minia2a&lt;/code&gt; returns exactly one record — &lt;code&gt;minia2a-uk&lt;/code&gt;, domain &lt;code&gt;minia2a.uk&lt;/code&gt;, network &lt;code&gt;eip155:8453&lt;/code&gt; — carrying 20 endpoints. We probed all 20 against our live challenges on 2026-10-03:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;check&lt;/th&gt;
&lt;th&gt;result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;endpoints indexed&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;answer &lt;code&gt;402&lt;/code&gt; with a payment challenge&lt;/td&gt;
&lt;td&gt;20 / 20 — no dead links&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;indexed price equals our live 402 amount&lt;/td&gt;
&lt;td&gt;18 / 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;indexed price differs (their snapshot is stale)&lt;/td&gt;
&lt;td&gt;2 (&lt;code&gt;loan&lt;/code&gt;, &lt;code&gt;hash&lt;/code&gt;: index says 0.01 USDC, live is 0.5)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;service category in the index&lt;/td&gt;
&lt;td&gt;empty string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;service description in the index&lt;/td&gt;
&lt;td&gt;empty; endpoints show auto-generated &lt;code&gt;"minia2a service: x402-&amp;lt;id&amp;gt;"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;submission path to fix any of the above&lt;/td&gt;
&lt;td&gt;none documented on the site or in &lt;code&gt;llms.txt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things are true at once, and it is worth keeping them apart. First, rail-level interoperability is real: every endpoint answers their crawler with the standard challenge, so a wallet configured for their storefront can mechanically pay ours. That is the protocol working as intended.&lt;/p&gt;

&lt;p&gt;Second, the row is &lt;em&gt;thin&lt;/em&gt;. No category, no description, two prices frozen at a value we stopped charging, and duplicate entries: 9 of the 20 endpoints are indexed under a doubled-prefix spelling of our path (the &lt;code&gt;x402-&lt;/code&gt; id prefix applied a second time), 2 of them under both that and the canonical path. Both spellings resolve — the gateway tolerates one doubled prefix — so they are duplicates, not breakage, but each keeps its own counters and nothing merges them. The two stale prices are not our error: our catalog and our live challenge agree with each other for both endpoints. Their figure is a snapshot from the last observation, and it ages at its own pace. We cannot edit it, because an index has no provider-facing edit surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an index does, and what a market does
&lt;/h2&gt;

&lt;p&gt;This is not a criticism of indexing. Indexing is a hard job — crawl a federation of facilitators, normalize wildly different endpoints into one schema, at scale. 2,980 services is real work. But an index and a market answer different questions, and conflating them is how buyers get hurt.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;question a user actually has&lt;/th&gt;
&lt;th&gt;index&lt;/th&gt;
&lt;th&gt;curation / market&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Does a service with this capability exist?&lt;/td&gt;
&lt;td&gt;yes, best effort&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is this price the price I will be charged right now?&lt;/td&gt;
&lt;td&gt;only as fresh as the last crawl&lt;/td&gt;
&lt;td&gt;checked against the live challenge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can I try it before paying?&lt;/td&gt;
&lt;td&gt;no concept of a trial&lt;/td&gt;
&lt;td&gt;trial path is a first-class rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who answers when a row is wrong?&lt;/td&gt;
&lt;td&gt;nobody; the row is auto-derived&lt;/td&gt;
&lt;td&gt;a named operator, with the row under a check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is there a category I can trust?&lt;/td&gt;
&lt;td&gt;only if the crawler inferred one&lt;/td&gt;
&lt;td&gt;set by the listing's own metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The difference is not engineering quality. It is &lt;em&gt;who answers for the row&lt;/em&gt;. An auto-derived listing can only ever be as good as its crawl, and no crawl knows whether a price moved, whether a seller is reachable, or whether two entries are the same service behind a path alias. Those are exactly the questions a buyer has.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If you build on top of any index:&lt;/strong&gt; treat the row as a lead, not a contract. Fetch the live &lt;code&gt;402&lt;/code&gt; before you commit a payment decision. The challenge is authoritative; a directory entry is a memory of one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What this changes for us
&lt;/h2&gt;

&lt;p&gt;Not much about the rail — we settled on the same one and it works. What it sharpens is why a curated, agent-first storefront exists next to a very large index: not to be bigger, but to be &lt;em&gt;answerable&lt;/em&gt;. Every price we publish is the price our own live challenge returns; if it drifts, a check fires rather than a user discovering it. Trials are a rule, not a row attribute. When a service goes stale, the remedy is on our side of the table.&lt;/p&gt;

&lt;p&gt;The honest summary of the week is not "an index launched and threatens us." It is that the layer everyone is racing to own — discovery — has at least two shapes, and only one of them can be held to a number. We would rather be small and answerable than large and unaccountable.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Measurement note: the Agentic.market figures were read live on 2026-10-03 from &lt;code&gt;api.agentic.market&lt;/code&gt; (our row) and from public reporting for the network-wide numbers, which are marked self-reported. Our own prices were compared against our live &lt;code&gt;402&lt;/code&gt; challenges and our catalog, which agree. No end-to-end settlement through a third-party facilitator was tested for this post, and we make no claim that we did.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>x402</category>
      <category>aiagents</category>
      <category>payments</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A 402 Can Be Structurally Perfect and Still Unpayable</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Sat, 03 Oct 2026 08:21:57 +0000</pubDate>
      <link>https://dev.to/minia2a/a-402-can-be-structurally-perfect-and-still-unpayable-1plb</link>
      <guid>https://dev.to/minia2a/a-402-can-be-structurally-perfect-and-still-unpayable-1plb</guid>
      <description>&lt;p&gt;If you are a buyer, you inspect the &lt;code&gt;402&lt;/code&gt; before you pay it: the scheme is &lt;code&gt;exact&lt;/code&gt;, the network is the chain you hold, the amount is what the listing said, the &lt;code&gt;payTo&lt;/code&gt; is an address somebody set up. All four can be right and the challenge still be unpayable — because none of those four is what your client actually signs against. That lives in a fifth place, and the official SDK reads it with a default that never raises.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you check, and what the client signs
&lt;/h2&gt;

&lt;p&gt;An x402 payment challenge is an array of &lt;code&gt;accepts&lt;/code&gt; entries. Four fields describe &lt;em&gt;who gets paid and how much&lt;/em&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;field&lt;/th&gt;
&lt;th&gt;answers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scheme&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;how the transfer is authorised (&lt;code&gt;exact&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;network&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;which chain settles it (&lt;code&gt;eip155:8453&lt;/code&gt; — Base)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;amount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;how much, in the asset's base units&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;payTo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;which address receives it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What the buyer's wallet &lt;em&gt;signs&lt;/em&gt; is an EIP-712 typed message, and its domain is not spelled out in those four fields. On the EVM rail it comes from the entry's &lt;code&gt;extra&lt;/code&gt; object: &lt;code&gt;extra.name&lt;/code&gt; and &lt;code&gt;extra.version&lt;/code&gt; are the token's EIP-712 domain name and version. The signature is only valid if those match what the token contract on that chain actually declares. Pay USDC on Base with &lt;code&gt;extra.name: "USDC"&lt;/code&gt; instead of &lt;code&gt;"USD Coin"&lt;/code&gt; and every signature recovers to an address nobody controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this failure is silent
&lt;/h2&gt;

&lt;p&gt;The reason it is worth writing down is that on a first-party route, the same operator both mints the challenge and verifies the signature — and both read &lt;em&gt;the same literal&lt;/em&gt;. So the two never disagree with each other. A wrong domain does not surface as a mismatch between two products that can be diffed; it surfaces only as payments that recover to nobody, i.e. every payment failing, with no error anywhere naming the cause. On a rail that mints its own challenges, that is the half the buyer cannot verify from the outside and the seller cannot verify from the inside either.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The shape:&lt;/strong&gt; a challenge with a valid &lt;code&gt;scheme&lt;/code&gt;, a supported &lt;code&gt;network&lt;/code&gt;, the advertised &lt;code&gt;amount&lt;/code&gt; and a real &lt;code&gt;payTo&lt;/code&gt; — and an &lt;code&gt;extra&lt;/code&gt; whose domain is wrong. Every field a buyer can see passes; the payment cannot be signed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The fifth field, and the SDK's silent default
&lt;/h2&gt;

&lt;p&gt;There is a second value in &lt;code&gt;extra&lt;/code&gt; that decides &lt;em&gt;how&lt;/em&gt; the transfer is authorised: &lt;code&gt;assetTransferMethod&lt;/code&gt;. We read the installed client to be sure of what it does with it rather than taking it on faith. In &lt;code&gt;@x402/evm&lt;/code&gt; 2.25.0, the exact scheme resolves it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// @x402/evm 2.25.0, exact scheme — "Routes to EIP-3009 or Permit2&lt;/span&gt;
&lt;span class="c1"&gt;// based on requirements.extra.assetTransferMethod."&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assetTransferMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;paymentRequirements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;assetTransferMethod&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eip3009&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;assetTransferMethod&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;permit2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createPermit2Payload&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createEIP3009Payload&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;   &lt;span class="c1"&gt;// any other value lands here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read that carefully. The only value that branches away from the default is the exact string &lt;code&gt;"permit2"&lt;/code&gt;. A missing key, an empty string, or a value the client has never heard of all take the same path: EIP-3009. No exception, no warning. So for a buyer, the safe assertion is not &lt;em&gt;"assetTransferMethod equals a known-good value"&lt;/em&gt; — it is &lt;em&gt;"the key is absent, or equals one the client supports"&lt;/em&gt;, because equality with a value you invented is exactly the shape that falls through.&lt;/p&gt;

&lt;p&gt;One nuance that is easy to over-generalise from: the &lt;strong&gt;batch-settlement&lt;/strong&gt; scheme in the same package does &lt;em&gt;not&lt;/em&gt; fall through — it raises on a value that is neither &lt;code&gt;eip3009&lt;/code&gt; nor &lt;code&gt;permit2&lt;/code&gt;. The silent default is a property of the &lt;code&gt;exact&lt;/code&gt; path, which is the path most listings — including ours — use. Same field, two different failure behaviours depending on the scheme, which is itself a reason to check the scheme you are actually being offered.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we assert about our own challenges
&lt;/h2&gt;

&lt;p&gt;The rule we adopted is the one above: assert the absence of &lt;code&gt;assetTransferMethod&lt;/code&gt;, not equality with a guessed value. Here is our own quick-start route read straight off the deployed challenge:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'https://minia2a.uk/x402/time'&lt;/span&gt;
accepts[0].scheme &lt;span class="o"&gt;=&lt;/span&gt; exact
accepts[0].network &lt;span class="o"&gt;=&lt;/span&gt; eip155:8453
accepts[0].amount  &lt;span class="o"&gt;=&lt;/span&gt; 100000          &lt;span class="c"&gt;# == the advertised price, exactly&lt;/span&gt;
accepts[0].asset   &lt;span class="o"&gt;=&lt;/span&gt; 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
accepts[0].payTo   &lt;span class="o"&gt;=&lt;/span&gt; 0xAb62452b4b019bC4402BFfCca6C706d16d72A7Bf
accepts[0].extra   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"USD Coin"&lt;/span&gt;, &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"2"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;"assetTransferMethod"&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;extra &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One &lt;code&gt;accepts&lt;/code&gt; entry, none missing. The domain the wallet is asked to sign against is declared in the challenge; the branch the client takes is the EIP-3009 default, which is the branch USDC on Base supports. That is what we can show a buyer from the outside.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we do not verify daily
&lt;/h2&gt;

&lt;p&gt;One honest limit. The values in &lt;code&gt;extra&lt;/code&gt; are pinned against what the token contract's own &lt;code&gt;name()&lt;/code&gt; and &lt;code&gt;version()&lt;/code&gt; return on Base — but reading that from the chain is a third-party network call, so it does not run in our daily path. Our daily check compares the live challenge against a constant in the guard file, which means the challenge and the constant can agree with each other while both are wrong about the chain — precisely the failure mode this post describes. The chain read is a separate mode that runs when the pin or the asset changes, which is the only moment the answer can move. A buyer checking a stranger's route cannot run even that; the most they can read is the challenge itself, which is why the challenge should carry the domain explicitly and the client should say what it does with a value it does not recognise.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Measured 2026-10-03: the challenge read above is live from &lt;code&gt;https://minia2a.uk/x402/time&lt;/code&gt; (HTTP 402, GET only, no trial burned, nothing signed). The client behaviour is read from the installed &lt;code&gt;@x402/evm&lt;/code&gt; 2.25.0 distribution, not from documentation. This post describes a failure shape and the check that catches it; it is not a claim that any particular route is broken.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>x402</category>
      <category>web3</category>
      <category>api</category>
      <category>payments</category>
    </item>
    <item>
      <title>Cloudflare Put a 402 Paywall Behind Every Domain. The Rail Was Never the Hard Part.</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Fri, 02 Oct 2026 23:29:36 +0000</pubDate>
      <link>https://dev.to/minia2a/cloudflare-put-a-402-paywall-behind-every-domain-the-rail-was-never-the-hard-part-5g4j</link>
      <guid>https://dev.to/minia2a/cloudflare-put-a-402-paywall-behind-every-domain-the-rail-was-never-the-hard-part-5g4j</guid>
      <description>&lt;p&gt;On 2026-09-30 Cloudflare opened a closed beta of its &lt;strong&gt;Monetization Gateway&lt;/strong&gt;. It lets a domain owner charge agents per request over HTTP 402, settled in USDC on Base, through Coinbase's x402 Facilitator. We have settled on exactly that rail since we launched. So the interesting question is not whether the rail works — it plainly does — but what is left to build on top of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually shipped, verified
&lt;/h2&gt;

&lt;p&gt;People will summarize this launch in a hundred ways, so it is worth pinning the facts to the source rather than to the retellings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The interface &lt;em&gt;is&lt;/em&gt; the status code. Cloudflare's own framing: there is "no redirect to a checkout page and no separate payment API to call."&lt;/li&gt;
&lt;li&gt;Settlement is &lt;strong&gt;USDC on Base&lt;/strong&gt;, through &lt;strong&gt;Coinbase's x402 Facilitator&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Closed beta, &lt;strong&gt;U.S.-based sellers and buyers&lt;/strong&gt; only, more geographies promised.&lt;/li&gt;
&lt;li&gt;Named early customers: Cloudflare AI Gateway (pay per inference), Ceramic.ai (pay per web search), Stocktwits (pay for stock signals), API2PDF (pay per API access).&lt;/li&gt;
&lt;li&gt;Their published API2PDF example carries the challenge in a &lt;code&gt;payment-required&lt;/code&gt; response header.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We checked our own live challenge to compare. &lt;code&gt;curl -D - https://minia2a.uk/x402/mime-type&lt;/code&gt; answers 402 with the payment requirements in &lt;em&gt;both&lt;/em&gt; the JSON body and a &lt;code&gt;payment-required&lt;/code&gt; header, and the accept entry is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"scheme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"exact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"eip155:8453"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"asset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"payTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"0xAb62452b4b019bC4402BFfCca6C706d16d72A7Bf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"50000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"maxTimeoutSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"extra"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"USD Coin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same scheme (&lt;code&gt;exact&lt;/code&gt;), same network (Base, &lt;code&gt;eip155:8453&lt;/code&gt;), same asset (USDC), and the same EIP-712 &lt;code&gt;extra&lt;/code&gt; (&lt;code&gt;name&lt;/code&gt;/&lt;code&gt;version&lt;/code&gt;) a facilitator needs to build the transfer authorisation. That is the sentence worth stating plainly: &lt;strong&gt;this is not a competing rail.&lt;/strong&gt; A wallet that learned to pay a Cloudflare-metered endpoint is, mechanically, able to pay ours.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What we did not test.&lt;/strong&gt; We verified the &lt;em&gt;shape&lt;/em&gt; of our challenge against the shape that facilitator requires. We did not route a real payment end-to-end through Coinbase's infrastructure from our host — third-party egress here is restricted, and "shape-verified" is a claim we can stand behind where "settlement-verified" is not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The half that is genuinely new: the buyer
&lt;/h2&gt;

&lt;p&gt;Most x402 coverage is about sellers — who is allowed to charge. The more interesting half of this announcement is on the buy side: U.S. Cloudflare customers can pay per inference at request time by sending &lt;code&gt;PAYMENT-METHOD: x402&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A pay-per-call economy has two cold starts, not one. Everyone notices the seller side — nobody lists an endpoint until there are buyers to call it. The buyer side is quieter and harder: an agent will not integrate a payment SDK it does not already carry. A header that a platform already terminates is a buyer cold-start being solved by somebody else, for free. That is good for the whole space, this catalog included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four jobs a gateway does not do
&lt;/h2&gt;

&lt;p&gt;A gateway monetizes &lt;em&gt;one domain&lt;/em&gt;, on &lt;em&gt;first contact&lt;/em&gt;. A marketplace has to do four other things, and none of them is the rail.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Cross-seller discovery
&lt;/h3&gt;

&lt;p&gt;A gateway answers "how does this endpoint charge?" It cannot answer "which of 1,694 endpoints should I call for this task?" Discovery is a separate layer, and it is the layer that decides whether an agent finds &lt;em&gt;any&lt;/em&gt; seller at all rather than the one domain it already knew.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. First contact
&lt;/h3&gt;

&lt;p&gt;A gateway charges on the first request; that is its job. An agent evaluating an unknown endpoint needs the opposite — a free first look. We give 5 free trial calls per signed wallet, one shared allowance across the catalog, no registration, so testing an endpoint costs a signature and not a payment.&lt;/p&gt;

&lt;p&gt;And the footnote, because it is the same disease as the provenance problem below: most of the wallets in our own trial count are ours. Of 1,338 trial wallets in the last 7 days, 1,336 trace to our own address prefixes. The trial mechanism is real and it works; the trial &lt;em&gt;demand&lt;/em&gt; number is mostly us proving it works. We would rather publish the split than a flattering total.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Accountability when the delivery is bad
&lt;/h3&gt;

&lt;p&gt;A facilitator moves money. It does not adjudicate a 200 that returned garbage. Settlement and &lt;em&gt;redress&lt;/em&gt; are different layers, and the second is where trust actually accumulates. We run a refund path: 57 refunds, 0.875 USDC, against 226 settled on-chain payments. Small numbers — but they exist, and they are the layer a pure gateway has no structural reason to build.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Provenance of the numbers
&lt;/h3&gt;

&lt;p&gt;Volume in agent payments is easy to inflate. A seller buying from itself moves a headline and proves nothing; independent analysis of Base x402 traffic this week found the same shape at scale — most headline buyers funded by the sellers themselves. So we publish the split instead of the total: 226 settled on-chain payments / 6.227 USDC, &lt;strong&gt;6&lt;/strong&gt; paying wallets in 7 days against &lt;strong&gt;1,338&lt;/strong&gt; trial wallets. That gap &lt;em&gt;is&lt;/em&gt; the honest state of the market, and it is more useful to a builder than a bigger, fuzzier number.&lt;/p&gt;

&lt;h2&gt;
  
  
  The threat, stated without spin
&lt;/h2&gt;

&lt;p&gt;Cloudflare owns the edge, an agent-identity primitive, and now a wallet relationship. That is three of the four ingredients of a marketplace. What it does not have is a catalog, and today the gateway is a closed U.S. beta with no published fee.&lt;/p&gt;

&lt;p&gt;If it opens up and bolts a directory on top, the rail becomes the marketplace and the discovery layer gets squeezed. Our bet is that breadth and trust are the durable layer and the rail is not: rails commoditize, and the thing that told you &lt;em&gt;which&lt;/em&gt; endpoint to call, let you try it free, and refunded you when it lied does not. If we are wrong, this post is a useful record of when we were.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we are watching
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Whether the beta leaves the U.S., and at what fee — an unpublished fee is the whole question for sellers doing the math.&lt;/li&gt;
&lt;li&gt;Whether a &lt;em&gt;directory&lt;/em&gt; appears on top of the gateway. That is the actual competitive event, not the gateway itself.&lt;/li&gt;
&lt;li&gt;Whether the x402 spec's newer schemes — &lt;code&gt;upto&lt;/code&gt;, &lt;code&gt;batch-settlement&lt;/code&gt;, &lt;code&gt;auth-capture&lt;/code&gt; — become how agents actually buy metered capacity, because a paywall that can charge "up to X" is a different product from one that charges a fixed amount per request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will keep the challenge in the open. If you are building a client against Cloudflare's gateway and want to point it at an endpoint in our catalog, it is the same object — a 402 with an &lt;code&gt;exact&lt;/code&gt; accept on Base, in the body and in the header.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Sources: Cloudflare's Monetization Gateway closed-beta announcement, 2026-09-30 (blog.cloudflare.com/monetization-gateway-beta/). Live challenge read from &lt;code&gt;https://minia2a.uk/x402/mime-type&lt;/code&gt; on 2026-10-02. Counts from &lt;code&gt;https://minia2a.uk/api/stats&lt;/code&gt; on 2026-10-02; the trial-wallet first-party share and the on-chain/refund split are fields the platform publishes and discloses as first-party where applicable.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>ai</category>
      <category>webdev</category>
      <category>payments</category>
    </item>
    <item>
      <title>Two Kinds of Machine Consumer: the ones that read your 402, and the ones that replay</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Fri, 02 Oct 2026 18:56:59 +0000</pubDate>
      <link>https://dev.to/minia2a/two-kinds-of-machine-consumer-the-ones-that-read-your-402-and-the-ones-that-replay-448k</link>
      <guid>https://dev.to/minia2a/two-kinds-of-machine-consumer-the-ones-that-read-your-402-and-the-ones-that-replay-448k</guid>
      <description>&lt;p&gt;A few weeks ago we noticed that our 402 payment challenge was making a specific kind of caller produce malformed 404s: the challenge carried a discovery field whose value was a service id (&lt;code&gt;x402-time&lt;/code&gt;), and a consumer was appending that value to our published paths as if it were a URL segment. We &lt;a href="https://minia2a.uk/blog/your-404s-have-user-agents-september-2026" rel="noopener noreferrer"&gt;wrote that up&lt;/a&gt; and asked the obvious follow-up: if we stop emitting the field, do the 404s stop? We said we'd want to run that experiment before writing the mechanism down as settled. We ran it. This is the result, and it is more interesting than a yes or no.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we changed
&lt;/h2&gt;

&lt;p&gt;The fix was narrower than it first looked. The field was doing internal work — it keyed a lookup into our per-service input examples — so we couldn't simply delete it. The change was to take the value, use it, and drop it before the challenge is serialized. After that, the field is absent from what a caller actually receives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'https://minia2a.uk/x402/time?probe=1'&lt;/span&gt;
extensions.bazaar keys &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"discoverable"&lt;/span&gt;, &lt;span class="s2"&gt;"info"&lt;/span&gt;, &lt;span class="s2"&gt;"schema"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="s2"&gt;"routeTemplate"&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;keys &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the write side is clean: the challenge no longer carries the value that was being appended. If the mechanism we described was right, and the consumer was reading the field from the challenge, the 404s should now go to zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we measured
&lt;/h2&gt;

&lt;p&gt;We re-ran the measurement with the clock pinned to the deploy (&lt;code&gt;--since 2026-10-02T11:16:27Z&lt;/code&gt;, the moment the new binary came up — the rotated logs still hold the pre-deploy lines and would otherwise make a fixed thing look broken). The result, over the whole post-deploy window:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;154&lt;/strong&gt; of 271 distinct 404 paths still matched the "published path + its own service id" shape. Not zero.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The natural reading is "the fix didn't work." That reading is wrong, and the reason is worth the rest of this post. Before concluding anything, look at &lt;em&gt;who&lt;/em&gt; the 154 came from:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;property&lt;/th&gt;
&lt;th&gt;value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;user agents&lt;/td&gt;
&lt;td&gt;1 (&lt;code&gt;Nitrograph-HealthCheck/1.0&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;source IPs&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;time span of the burst&lt;/td&gt;
&lt;td&gt;12 seconds (12:35:58Z → 12:36:10Z)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;that IP's requests, across every rotated log&lt;/td&gt;
&lt;td&gt;1,078&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;…of which responses were 402 (the challenge)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;…of which responses were 404&lt;/td&gt;
&lt;td&gt;1,078&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last pair is the whole story. Across every log we retain, this caller has made 1,078 requests and received 1,078 404s. It has never once fetched a 402 from us — not before the change, not after. It is not reading our challenge and mis-parsing a field; it is replaying a list of URLs that it built at some earlier point and has not rebuilt since.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;You cannot fix a consumer that does not read you.&lt;/strong&gt; Removing the field from the challenge changed a document this caller never opens. The residual 154 are not evidence the mechanism was wrong — they are evidence that this particular caller is outside the mechanism's reach by construction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The distinction
&lt;/h2&gt;

&lt;p&gt;It is tempting to treat "machine consumers" as one audience, but on the evidence there are (at least) two:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;readers&lt;/th&gt;
&lt;th&gt;replayers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;behavior&lt;/td&gt;
&lt;td&gt;fetch the current challenge, then act on what they find&lt;/td&gt;
&lt;td&gt;act on a copy captured earlier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;your fix reaches them&lt;/td&gt;
&lt;td&gt;yes, on their next fetch&lt;/td&gt;
&lt;td&gt;only when their source refreshes — or never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;what you can measure about them&lt;/td&gt;
&lt;td&gt;challenge fetches in your own logs&lt;/td&gt;
&lt;td&gt;a flat wall of one status code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;what they cost you&lt;/td&gt;
&lt;td&gt;your real traffic&lt;/td&gt;
&lt;td&gt;your 404 rate and your log volume&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The useful part is that the second column is legible in data you already have. You do not need to detect a "bad bot." You need one question answered per source: &lt;em&gt;has this caller ever fetched the document I just changed?&lt;/em&gt; If its entire request history is one status code, nothing you serve will change its behavior, and it belongs in a different bucket from the callers your fix is actually for.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this changes in practice
&lt;/h2&gt;

&lt;p&gt;Three things we now do differently:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Measure the write side and the read side separately.&lt;/strong&gt; "The field is gone from the challenge" and "the malformed requests stopped" are different claims with different evidence. We confirmed the first directly (a probe). The second needs a consumer that both fetched a fresh challenge &lt;em&gt;and&lt;/em&gt; then mis-used it — and in this window no such request exists, so the read-side half stays &lt;em&gt;unconfirmed&lt;/em&gt;, not refuted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attribute residuals before explaining them.&lt;/strong&gt; A count of 154 invites a story. The story has to survive the per-source breakdown: one UA, one IP, 12 seconds. Group first, theorize second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not read a replayer's noise as your own regression.&lt;/strong&gt; A guard that fires on every replayed request will fire forever, and its redness stops carrying information. That is a defect in the guard, not in the system it watches.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the sibling of a rule we keep relearning: a check has to measure the thing it names. "The malformed 404 count" is not "our fix's effect" unless every counted line could, in principle, have been affected by the fix. When the population includes callers that never read you, the count is a mix — and a mix with an unreachable component in it can never reach zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest scoreboard
&lt;/h2&gt;

&lt;p&gt;For the record, because the earlier post asked for it explicitly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;claim&lt;/th&gt;
&lt;th&gt;status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;the challenge no longer carries the appended value&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;confirmed&lt;/strong&gt; (live probe)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;removing it stops the malformed 404s&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;untested&lt;/strong&gt; — no post-deploy caller both fetched a challenge and then appended an id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the sole surviving source is a consumer that never fetches the challenge&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;confirmed&lt;/strong&gt; (0 of 1,078 requests were 402)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We would rather leave the middle row "untested" than upgrade it to "works" because the number went the way we wanted. The number did not go the way we wanted — it stayed at 154 — and the reason it stayed is itself the finding.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Method: the shape predicate is "a 404 path equal to a published service path with that service's own id appended," with both oracles read from live data (&lt;code&gt;/api/services&lt;/code&gt; and the gateway). The per-source status mix is a &lt;code&gt;grep&lt;/code&gt; over every rotated gateway log for the single matching source. Both are read-only; the deploy timestamp is the gateway binary's mtime. Callers are identified by user agent and source address; no payload data is inspected.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>x402</category>
      <category>api</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Catalog Freshness: Why We Won't Self-Buy, and Why There's No "Verified" Timestamp</title>
      <dc:creator>minia2a</dc:creator>
      <pubDate>Fri, 02 Oct 2026 02:08:36 +0000</pubDate>
      <link>https://dev.to/minia2a/catalog-freshness-why-we-wont-self-buy-and-why-theres-no-verified-timestamp-5dgb</link>
      <guid>https://dev.to/minia2a/catalog-freshness-why-we-wont-self-buy-and-why-theres-no-verified-timestamp-5dgb</guid>
      <description>&lt;p&gt;A reader on dev.to asked two things about our catalog measurement: whether we force a self-buy when a price or payee changes so a third-party snapshot moves, and whether our listing page surfaces a "last verified against live 402" timestamp, or leaves that to the facilitator.&lt;/p&gt;

&lt;p&gt;Thanks — and the nastiest part is exactly the one you named: the rows that stay wrong are the ones nobody buys, and those carry the biggest gaps.&lt;/p&gt;

&lt;p&gt;On the self-buy habit: we looked at it and decided against it. A self-buy is a real settlement, so it writes a real row into our own books — we would be manufacturing payment activity in order to move someone else's snapshot. Our published figures are what our ledger actually holds, and we would rather leave a catalog row stale than make our own payment history say something that did not happen. So for us the snapshot moves only when someone genuinely buys.&lt;/p&gt;

&lt;p&gt;On the "last verified against live 402" timestamp: not on the listing page, and that is deliberate. What we do run is a check of our own 402 against our own service records — which answers "is our challenge internally consistent", not "does a third-party catalog still agree with us". Those are different questions, and a green internal check would be a misleading answer to the second one. So the catalog sweep runs as a measurement that prints the discrepancy, not as a guard that claims health.&lt;/p&gt;

&lt;p&gt;Which leaves the honest freshness signal the one you cannot fabricate: the last real settlement.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Context: a reply to a question in the comments on &lt;a href="https://minia2a.uk/blog/catalog-is-a-snapshot-september-2026" rel="noopener noreferrer"&gt;The Catalog Is a Snapshot&lt;/a&gt;. Method note: the catalog sweep is a measurement, not a health claim — it pulls a directory's rows for our own origin and compares each quoted amount against the live 402 the endpoint actually returns, printing the rows that disagree rather than folding them into a score.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ranking hints may come from a catalog; the amount and payee an agent signs should come only from the challenge it just received.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>x402</category>
      <category>agents</category>
      <category>api</category>
      <category>payments</category>
    </item>
  </channel>
</rss>
