<?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: bykamo</title>
    <description>The latest articles on DEV Community by bykamo (@bykamo).</description>
    <link>https://dev.to/bykamo</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3980371%2F8d055a96-7c69-4fde-9089-1c84325863f4.png</url>
      <title>DEV Community: bykamo</title>
      <link>https://dev.to/bykamo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bykamo"/>
    <language>en</language>
    <item>
      <title>I Made My Website Charge AI Crawlers with HTTP 402. In 30 Days, 5,811 Came and 5 Paid.</title>
      <dc:creator>bykamo</dc:creator>
      <pubDate>Fri, 12 Jun 2026 06:48:03 +0000</pubDate>
      <link>https://dev.to/bykamo/i-made-my-website-charge-ai-crawlers-with-http-402-in-30-days-5811-came-and-5-paid-2112</link>
      <guid>https://dev.to/bykamo/i-made-my-website-charge-ai-crawlers-with-http-402-in-30-days-5811-came-and-5-paid-2112</guid>
      <description>&lt;p&gt;I run a content site, &lt;a href="https://do-and-coffee.com" rel="noopener noreferrer"&gt;do-and-coffee.com&lt;/a&gt;. Like everyone else, it gets scraped by AI crawlers. Instead of blocking them, I did something else: I put a paywall in front of the site that returns &lt;strong&gt;HTTP 402 Payment Required&lt;/strong&gt; to bots, with machine-readable payment instructions. If a crawler pays a cent in USDC, it gets the article. If it doesn't, it gets the 402 and nothing else.&lt;/p&gt;

&lt;p&gt;Then I let it run for 30 days and watched. Here's what actually happened — and it's not the number you'd put on a pitch deck.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A Cloudflare Worker sits in front of the site. AI crawlers get &lt;strong&gt;402 + x402 payment requirements&lt;/strong&gt;; humans and search bots pass through free.&lt;/li&gt;
&lt;li&gt;Payment is &lt;strong&gt;USDC on Base&lt;/strong&gt;, $0.01 per article, verified and settled through Coinbase's CDP facilitator.&lt;/li&gt;
&lt;li&gt;30-day result: &lt;strong&gt;5,811 crawler requests, 5 paid, 5,806 served a 402.&lt;/strong&gt; Revenue at $0.01/article ≈ &lt;strong&gt;$0.05&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The interesting part isn't the revenue. It's &lt;em&gt;who&lt;/em&gt; paid: &lt;strong&gt;GPTBot paid 4 times out of 48 requests; ClaudeBot paid once out of 651.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;do-and-coffee.com/blog/article/*  ─▶  x402 Worker (Cloudflare)
                                       │
   has X-PAYMENT-RESPONSE? ───────────┤─▶ yes ─▶ proxy origin (200)
   KV cache hit (payer:url)? ─────────┤─▶ yes ─▶ proxy origin (200)
   no X-PAYMENT? ─────────────────────┤─▶ 402 + payment requirements
   has X-PAYMENT? ────────────────────┘
        │
        ├─▶ CDP /verify   (is the signed payment valid?)
        ├─▶ CDP /settle   (waitUntil: confirmed — on-chain)
        └─▶ on success: KV.put(payer:url, receipt, ttl 24h) ─▶ proxy origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The worker speaks the &lt;strong&gt;x402&lt;/strong&gt; protocol: a 402 response carries an &lt;code&gt;accepts&lt;/code&gt; array describing exactly how to pay (scheme &lt;code&gt;exact&lt;/code&gt;, network &lt;code&gt;base&lt;/code&gt;, asset USDC, amount, &lt;code&gt;payTo&lt;/code&gt; wallet). A compliant agent reads that, signs a USDC payment, and retries with an &lt;code&gt;X-PAYMENT&lt;/code&gt; header. The worker verifies and settles it through Coinbase's facilitator, then proxies the real article.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The 402 response
&lt;/h3&gt;

&lt;p&gt;When there's no payment, the worker builds the requirements and returns 402:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildPaymentRequirements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resourceUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;PaymentRequirements&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;scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxAmountRequired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 0.01 USDC (6 decimals)&lt;/span&gt;
    &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resourceUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Access to Do and Coffee premium article content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RESOURCE_WALLET_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxTimeoutSeconds&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="na"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// USDC on Base&lt;/span&gt;
    &lt;span class="na"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USD Coin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify, then settle
&lt;/h3&gt;

&lt;p&gt;When a payment does arrive, the worker decodes the &lt;code&gt;X-PAYMENT&lt;/code&gt; header, then calls the CDP facilitator twice — once to verify the signature is valid, once to settle it on-chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;settleRes&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FACILITATOR_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/settle`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&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="nx"&gt;settleHeaders&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;x402Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;paymentPayload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paymentRequirements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;reqs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// block until the chain confirms&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;h3&gt;
  
  
  Don't charge twice
&lt;/h3&gt;

&lt;p&gt;A paid payer is cached in Workers KV for 24 hours, keyed by &lt;code&gt;payer:url&lt;/code&gt;, so a crawler that re-fetches the same article within a day isn't billed again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;settleJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;expirationTtl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;86400&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;h2&gt;
  
  
  What it cost / earned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Worker + KV: within Cloudflare's free tier at this volume — &lt;strong&gt;$0&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Facilitator: Coinbase CDP, no per-call fee at this scale.&lt;/li&gt;
&lt;li&gt;Revenue over 30 days: &lt;strong&gt;5 payments × $0.01 = ~$0.05&lt;/strong&gt; (the dashboard tracks counts, not USD; this is the configured price × paid count).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So: net a few cents. As a business, this is nothing. As a measurement, it's the whole point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 30-day numbers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F405tc9a0ezubhili048b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F405tc9a0ezubhili048b.png" alt=" " width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Window: May 14 – June 12. Source: the worker's own crawl-stats dashboard.&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;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total crawler requests&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5,811&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Served 402&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5,806&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Per crawler:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Crawler&lt;/th&gt;
&lt;th&gt;Requests&lt;/th&gt;
&lt;th&gt;Paid&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude-SearchBot&lt;/td&gt;
&lt;td&gt;1,536&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PetalBot&lt;/td&gt;
&lt;td&gt;1,502&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ClaudeBot&lt;/td&gt;
&lt;td&gt;651&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amzn-SearchBot&lt;/td&gt;
&lt;td&gt;444&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OAI-SearchBot&lt;/td&gt;
&lt;td&gt;337&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPTBot&lt;/td&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other (long tail of minor UAs)&lt;/td&gt;
&lt;td&gt;1,293&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5,811&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most-hit paths: &lt;code&gt;/blog/article/*&lt;/code&gt; (1,633), &lt;code&gt;/robots.txt&lt;/code&gt; (1,575), &lt;code&gt;/sitemap.xml&lt;/code&gt; (840).&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke / what I learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Almost nothing pays.&lt;/strong&gt; 0.086% of requests resulted in a payment. The overwhelming default behavior when an AI crawler meets a 402 is to leave. If you're imagining passive USDC income from agentic traffic, the live data says: not yet, not at this volume.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The crawlers that &lt;em&gt;can&lt;/em&gt; pay still mostly don't.&lt;/strong&gt; GPTBot paid on 4 of 48 requests (~8%); ClaudeBot on 1 of 651 (~0.15%). Treat exact attributions cautiously — these are user-agent strings, which can be spoofed — but as a first-party observation, the agents presenting OpenAI's GPTBot UA were by far the most likely to actually complete an x402 payment. Everything else (search-indexing bots like Claude-SearchBot, PetalBot, OAI-SearchBot) just hammered &lt;code&gt;/robots.txt&lt;/code&gt; and &lt;code&gt;/sitemap.xml&lt;/code&gt; and bounced off the 402.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The origin has to trust the paywall.&lt;/strong&gt; The worker proxies to my real site, so my site needs to know "this request was paid." I had it mint a short-lived &lt;strong&gt;HMAC access token&lt;/strong&gt; (signed &lt;code&gt;{payer, resource, exp}&lt;/code&gt;) and pass it as an &lt;code&gt;Authorization: Bearer&lt;/code&gt; header, plus spoof a browser User-Agent so the origin returns full HTML instead of its own bot treatment. Without the browser UA, the origin's own anti-bot logic fought the paywall.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Settling synchronously costs latency.&lt;/strong&gt; &lt;code&gt;waitUntil: "confirmed"&lt;/code&gt; blocks the response until Base confirms the transfer. It's the safe choice (you serve content only after the money is real), but it adds seconds to a paid request. For a $0.01 article that's fine; for a high-frequency API it would be the first thing I'd revisit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;URL normalization matters for verify.&lt;/strong&gt; The &lt;code&gt;resource&lt;/code&gt; in the payment requirements has to match exactly on the retry, so I strip operational query params (&lt;code&gt;__x402&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt;) before composing it. A mismatch there silently fails verification.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;There are a lot of essays about the "agentic web" and machines paying machines. There are very few sites actually returning 402 to real crawlers and reading the receipts. This is one of them, and the honest takeaway is: the rails work — verify, settle, USDC on Base, all fine — but the demand side isn't here yet. The number that matters today isn't the $0.05. It's the 5-out-of-5,811, and the fact that one vendor's bot pays 50× more often than another's. That ratio is the thing worth watching as this evolves.&lt;/p&gt;

&lt;p&gt;I'll keep it running and report the curve.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm KAMO, a developer in Kyoto. I write implementation logs — working code, real costs, what broke.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Get every log:&lt;/strong&gt; &lt;a href="https://bykamo.substack.com" rel="noopener noreferrer"&gt;https://bykamo.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portfolio:&lt;/strong&gt; bykamo.dev&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aiagents</category>
      <category>web3</category>
      <category>cloudflare</category>
      <category>x402</category>
    </item>
    <item>
      <title>I Bought a $100 Hat for $0 — Proving an AI Agent Was Human-Backed with World AgentKit</title>
      <dc:creator>bykamo</dc:creator>
      <pubDate>Fri, 12 Jun 2026 05:46:26 +0000</pubDate>
      <link>https://dev.to/bykamo/i-bought-a-100-hat-for-0-proving-an-ai-agent-was-human-backed-with-world-agentkit-23e0</link>
      <guid>https://dev.to/bykamo/i-bought-a-100-hat-for-0-proving-an-ai-agent-was-human-backed-with-world-agentkit-23e0</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmp85490bq4pi28ow2kc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmp85490bq4pi28ow2kc.png" alt=" " width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A $100 hat showed up at my door in Kyoto. I paid nothing for it — not for the hat, not for shipping. $0.00.&lt;/p&gt;

&lt;p&gt;It wasn't a coupon I found or a sale. The store, &lt;a href="https://humanrequired.shop" rel="noopener noreferrer"&gt;humanrequired.shop&lt;/a&gt;, only gives that discount to AI agents that can &lt;em&gt;prove a real human is standing behind them&lt;/em&gt;. So I made my agent prove it, on-chain, with a zero-knowledge proof. The discount it got back was 100% off. Here's the whole path, because the design held together far better than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;World Foundation's &lt;strong&gt;AgentKit&lt;/strong&gt; lets you prove on-chain that an agent is "human-backed."&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://humanrequired.shop" rel="noopener noreferrer"&gt;humanrequired.shop&lt;/a&gt; hands a human-only &lt;strong&gt;100%-off&lt;/strong&gt; discount to verified agents — once per World ID.&lt;/li&gt;
&lt;li&gt;The official Claude Code plugin (&lt;code&gt;worldcoin/agentkit-shopify-demo&lt;/code&gt;) ships the whole flow as skills.&lt;/li&gt;
&lt;li&gt;Result: one "Human in the Loop" Hat, &lt;strong&gt;$100 → $0, shipping included&lt;/strong&gt;, delivered to Japan.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Claude Code]
   ├─ plugin: agentkit-shopify
   │   ├─ skill: shopify-agent-discount  (SIWE signature → World discount API)
   │   └─ skill: shopify-storefront       (Shopify product JSON → cart URL)
   │
   ├─ Agent Wallet (local Ethereum keypair)
   │   └─ registered in AgentBook (on World Chain) = the human-backed proof
   │
   └─ Shopify (humanrequired.shop)
       └─ /api/verify gate (Worldcoin's verification endpoint)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key idea: you don't give the &lt;em&gt;agent&lt;/em&gt; a World ID. A human delegates the agent's public key on-chain. The private key never leaves the machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 — Install the plugin
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin marketplace add worldcoin/agentkit-shopify-demo
/plugin &lt;span class="nb"&gt;install &lt;/span&gt;agentkit-shopify@worldcoin-agentkit
/reload-plugins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 — Generate an agent key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run &lt;span class="nt"&gt;--with&lt;/span&gt; eth-account python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"from eth_account import Account; print(Account.create().key.hex())"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .agent-key
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 .agent-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the wallet address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run &lt;span class="nt"&gt;--with&lt;/span&gt; eth-account python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"from eth_account import Account; print(Account.from_key(open('.agent-key').read().strip()).address)"&lt;/span&gt;
&lt;span class="c"&gt;# =&amp;gt; 0xC56A...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 — Register in AgentBook (the human-backed proof)
&lt;/h3&gt;

&lt;p&gt;In a &lt;strong&gt;separate terminal&lt;/strong&gt; (it shows a QR code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @worldcoin/agentkit-cli register 0xC56A...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Scan the QR in the World App.&lt;/li&gt;
&lt;li&gt;Your Orb-verified World ID generates a zero-knowledge proof.&lt;/li&gt;
&lt;li&gt;The AgentBook contract on World Chain writes &lt;code&gt;agent_address → human_nullifier&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Worldcoin's relayer covers the gas, so you pay nothing to register.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4 — Call the discount API
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;get-coupon.py&lt;/code&gt; does four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Signs a &lt;strong&gt;SIWE&lt;/strong&gt; message (Sign-In with Ethereum, EIP-4361) with the key in &lt;code&gt;.agent-key&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Base64-encodes the signature into an &lt;code&gt;agentkit:&lt;/code&gt; HTTP header.&lt;/li&gt;
&lt;li&gt;POSTs the product URL to &lt;code&gt;https://discount-app.worldcoin.org/api/verify&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The server checks AgentBook — if the agent maps to a registered human, it returns a discount code.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .agent-key&lt;span class="si"&gt;)&lt;/span&gt; ./get-coupon.py https://humanrequired.shop/products/human-in-the-loop-hat
&lt;span class="c"&gt;# =&amp;gt; WORLD-ID-ced1a8fe6682&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5 — Build the checkout URL
&lt;/h3&gt;

&lt;p&gt;No special API needed on the Shopify side — the plain product JSON endpoint is enough.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://humanrequired.shop/products/human-in-the-loop-hat.json"&lt;/span&gt; | jq &lt;span class="s1"&gt;'.product.variants[0].id'&lt;/span&gt;
&lt;span class="c"&gt;# =&amp;gt; 46991516106914&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shopify's standard cart permalink finishes it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;https://humanrequired.shop/cart/&lt;span class="nt"&gt;&amp;lt;variant_id&amp;gt;&lt;/span&gt;:&lt;span class="nt"&gt;&amp;lt;qty&amp;gt;&lt;/span&gt;?discount=&lt;span class="nt"&gt;&amp;lt;code&amp;gt;&lt;/span&gt;
https://humanrequired.shop/cart/46991516106914:1?discount=WORLD-ID-ced1a8fe6682
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;The hat: &lt;strong&gt;$0.00&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Shipping to Kyoto: &lt;strong&gt;$0.00&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Gas to register on World Chain: &lt;strong&gt;$0.00&lt;/strong&gt; (Worldcoin's relayer paid it)&lt;/li&gt;
&lt;li&gt;Total out of pocket: &lt;strong&gt;$0.00&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What broke
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;npx ...register&lt;/code&gt; needs its own terminal.&lt;/strong&gt; It draws a QR code; running it inside Claude Code's Bash mangles the output. Run it in a real terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't do a test run.&lt;/strong&gt; The discount code is derived deterministically from your nullifier hash, so "let me just call it once to see" spends your one real code. I held off on hitting the API until the checkout URL was fully assembled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strip query params off the product URL.&lt;/strong&gt; A trailing &lt;code&gt;?variant=123&lt;/code&gt; can make the discount API treat it as a different product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/reload-plugins&lt;/code&gt; did nothing in my setup.&lt;/strong&gt; The skills are just bash scripts under &lt;code&gt;~/.claude/plugins/marketplaces/worldcoin-agentkit/skills/&lt;/code&gt;, so I ran them by hand and the flow still completed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;A few design choices stood out, and they're the reason I bothered writing this down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The discount code appears to be deterministic.&lt;/strong&gt; The tail of &lt;code&gt;WORLD-ID-ced1a8fe6682&lt;/code&gt; matched the tail of the nullifier hash from registration (&lt;code&gt;0x2fd8701b...a8fe6682&lt;/code&gt;). I can't see the server, but that match strongly suggests the code is derived from the nullifier rather than from a stored counter or random value — which would mean "one human, one code" is enforced cryptographically, not by a database row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The private key never goes over the wire.&lt;/strong&gt; Auth is a SIWE signature; the server does &lt;code&gt;ecrecover&lt;/code&gt; to get the signer address, then looks up the human link in AgentBook.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The MCP turned out to be unnecessary.&lt;/strong&gt; The plugin registers a Shopify Storefront MCP, but the &lt;code&gt;shopify-storefront&lt;/code&gt; skill just curls Shopify's public product JSON. Easy to miss, and the right call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I spend a lot of time watching the supplier side of agentic commerce — sites that gate AI traffic. This was the rare chance to be the &lt;em&gt;consumer&lt;/em&gt; passing through one of those gates, and the gate's logic is positive: not "block AI," but "let human-backed agents through." Claude Code + plugin + external MCP + a local private key + a ZK proof + an on-chain registry, all meshing cleanly. The hat is the souvenir.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm KAMO, a developer in Kyoto. I write implementation logs — working code, real costs, what broke.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Get every log:&lt;/strong&gt; &lt;a href="https://bykamo.substack.com" rel="noopener noreferrer"&gt;https://bykamo.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portfolio:&lt;/strong&gt; bykamo.dev&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aiagents</category>
      <category>automation</category>
      <category>agenticcommerce</category>
      <category>worldcoin</category>
    </item>
  </channel>
</rss>
