<?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: Mark Zhurbin</title>
    <description>The latest articles on DEV Community by Mark Zhurbin (@0rkz).</description>
    <link>https://dev.to/0rkz</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%2F3999212%2F93bdae03-e203-41d2-a5ac-e0686d5deac1.png</url>
      <title>DEV Community: Mark Zhurbin</title>
      <link>https://dev.to/0rkz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/0rkz"/>
    <language>en</language>
    <item>
      <title>Charge per API call in USDC — and give buyers a receipt they can verify</title>
      <dc:creator>Mark Zhurbin</dc:creator>
      <pubDate>Mon, 29 Jun 2026 20:23:54 +0000</pubDate>
      <link>https://dev.to/0rkz/charge-per-api-call-in-usdc-and-give-buyers-a-receipt-they-can-verify-59nc</link>
      <guid>https://dev.to/0rkz/charge-per-api-call-in-usdc-and-give-buyers-a-receipt-they-can-verify-59nc</guid>
      <description>&lt;p&gt;AI agents can now pay for things on their own. The &lt;a href="https://x402.org" rel="noopener noreferrer"&gt;x402 protocol&lt;/a&gt; reuses HTTP &lt;code&gt;402 Payment Required&lt;/code&gt; so a client — human or agent — pays per API call in USDC, no API keys, no accounts, no subscription. It's a genuinely nice primitive.&lt;/p&gt;

&lt;p&gt;But there's a gap that bites the moment an agent acts on what it paid for: &lt;strong&gt;the payment rail proves the money moved. It says nothing about the bytes that came back.&lt;/strong&gt; An agent can pay perfectly and still act on tampered or spoofed data.&lt;/p&gt;

&lt;p&gt;So here's what I wired up, and how you can too: an Express endpoint that charges per call in USDC &lt;strong&gt;and&lt;/strong&gt; hands the buyer a signed receipt they can verify &lt;em&gt;before&lt;/em&gt; acting on the response. The packages underneath are free and MIT — this is just the assembly, shown plainly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're building
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client ──GET /price──▶  402 Payment Required  (USDC terms)
client ──pay USDC───▶  200 OK + your data + an X-BYTE-Attestation receipt
client ──verify────▶  recompute hash, recover signer → act or refuse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two small libraries do the work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.npmjs.com/package/@foreseal/gate" rel="noopener noreferrer"&gt;&lt;code&gt;@foreseal/gate&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; — seller-side Express middleware. Turns any upstream into a paid, attested endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.npmjs.com/package/@payperbyte/sdk" rel="noopener noreferrer"&gt;&lt;code&gt;@payperbyte/sdk&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; — buyer-side verifier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both MIT. You can absolutely wire them yourself; that's the point of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1 — the seller, in one middleware call
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;trustMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@foreseal/gate&lt;/span&gt;&lt;span class="dl"&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/price&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;trustMiddleware&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://your-api.example.com/data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// your real endpoint&lt;/span&gt;
    &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;perCallUsdc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.01&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;                &lt;span class="c1"&gt;// or { perKBUsdc, floorUsdc }&lt;/span&gt;
    &lt;span class="na"&gt;payTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xYourUSDCAddressOnBase&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;// where USDC settles&lt;/span&gt;
    &lt;span class="na"&gt;attestation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delivery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                        &lt;span class="c1"&gt;// stamp every paid 200&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole integration. An unpaid call gets a &lt;code&gt;402&lt;/code&gt; with x402 USDC terms. On payment, the gate proxies your upstream and stamps the response with an &lt;strong&gt;EIP-712 attestation over the exact bytes it served&lt;/strong&gt; — byte for byte — in an &lt;code&gt;X-BYTE-Attestation&lt;/code&gt; header.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2 — what the buyer sees
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; http://localhost:3000/price
&lt;span class="c"&gt;# HTTP/1.1 402 Payment Required&lt;/span&gt;
&lt;span class="c"&gt;# ... x402 payment terms (asset, amount, network, payTo) ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client pays in USDC, retries with the payment header, and gets &lt;code&gt;200&lt;/code&gt; plus the data and the receipt. Standard x402 flow — the gate just adds the receipt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3 — verify before acting (the part that matters)
&lt;/h2&gt;

&lt;p&gt;This is the half everyone skips. Before your code (or your agent) acts on the response, recompute the hash of the exact bytes and recover the signer. If either is wrong, refuse.&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;verifyFromGatewayResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ARBITRUM_SEPOLIA&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@payperbyte/sdk&lt;/span&gt;&lt;span class="dl"&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;res&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="nx"&gt;url&lt;/span&gt;&lt;span class="p"&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="nx"&gt;paymentHeaders&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// your paid call&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                              &lt;span class="c1"&gt;// the EXACT bytes&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-byte-attestation&lt;/span&gt;&lt;span class="dl"&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;v&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;verifyFromGatewayResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ARBITRUM_SEPOLIA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gatewayAttester&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// pin the seller's attester&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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verified&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;refuse: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ...safe to act on `body`.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can prove the mechanic offline, no wallet and no network — sign a sample receipt, then verify it and two attacks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;verify-before-act&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;genuine        → verified=true    received bytes match the attested hash AND signer — safe to act&lt;/span&gt;
  &lt;span class="s"&gt;tampered byte  → verified=false   HASH MISMATCH — do not act&lt;/span&gt;
  &lt;span class="s"&gt;forged signer  → verified=false   bad recover — do not act&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accept genuine. Refuse tampered and forged. That's the gate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest part: provenance, not truth
&lt;/h2&gt;

&lt;p&gt;This is important enough to say out loud, because plenty of "verified data" pitches blur it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The receipt proves&lt;/strong&gt; the bytes are &lt;em&gt;authentic and unaltered&lt;/em&gt;, signed by the attester you pinned. Tamper-evident, signer-pinned, recomputable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It does not prove&lt;/strong&gt; the data is &lt;em&gt;correct&lt;/em&gt;. A genuine receipt over a wrong number still verifies — authentic bytes, garbage value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the claim is narrow and useful: &lt;em&gt;"these are genuinely the bytes the seller signed,"&lt;/em&gt; not &lt;em&gt;"this number is right."&lt;/em&gt; You still decide whether to trust the seller. The receipt just removes the question of whether you got their actual bytes. Tell your own users which one you mean.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one gotcha that trips everyone
&lt;/h2&gt;

&lt;p&gt;The EIP-712 signing domain is anchored at &lt;strong&gt;&lt;code&gt;chainId 421614&lt;/code&gt; (Arbitrum Sepolia)&lt;/strong&gt; — a &lt;em&gt;frozen signing namespace&lt;/em&gt; for signature recovery. It is &lt;strong&gt;not&lt;/strong&gt; a settlement chain. Payments settle in &lt;strong&gt;USDC on Base mainnet&lt;/strong&gt;. So you pass &lt;code&gt;ARBITRUM_SEPOLIA&lt;/code&gt; to the verifier even though the money moved on Base. Recovery happens in the domain; settlement happens on the rail. Mix them up and your signatures won't recover — budget five minutes of confusion here, then never again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testnet today, mainnet when you're ready
&lt;/h2&gt;

&lt;p&gt;Default to &lt;strong&gt;&lt;code&gt;base-sepolia&lt;/code&gt;&lt;/strong&gt;: the public x402 facilitator advertises testnet, so the full &lt;code&gt;402 → pay → 200 → receipt&lt;/code&gt; loop works for free with testnet USDC, no keys. (On Base mainnet &lt;em&gt;without&lt;/em&gt; a mainnet facilitator the paid route correctly fails closed.)&lt;/p&gt;

&lt;p&gt;For real USDC on Base mainnet, point at the Coinbase CDP facilitator:&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;NETWORK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;base
&lt;span class="nv"&gt;FACILITATOR_AUTH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cdp
&lt;span class="nv"&gt;CDP_API_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...
&lt;span class="nv"&gt;CDP_API_KEY_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...
&lt;span class="c"&gt;# and: npm i @coinbase/x402&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran this end-to-end on Base mainnet — the &lt;code&gt;402&lt;/code&gt; advertises &lt;code&gt;network: eip155:8453&lt;/code&gt;, the canonical Base USDC asset, and your &lt;code&gt;payTo&lt;/code&gt;. Develop on testnet, flip one env block for mainnet. That's the only change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it in one second
&lt;/h2&gt;

&lt;p&gt;No install, no wallet — see the whole accept-genuine / refuse-tampered loop locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @foreseal/demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs offline and shows an agent &lt;strong&gt;act&lt;/strong&gt; on genuine bytes and &lt;strong&gt;refuse&lt;/strong&gt; a tampered byte, a forged signature, a missing receipt, and a forked signing domain — in about a second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take it further
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Building an &lt;strong&gt;AI agent&lt;/strong&gt; that should buy data over MCP? &lt;a href="https://www.npmjs.com/package/byte-mcp-server" rel="noopener noreferrer"&gt;&lt;code&gt;byte-mcp-server&lt;/code&gt;&lt;/a&gt; (MIT) gives Claude Desktop / Claude Code / Cursor a &lt;code&gt;buy → verify-before-act&lt;/code&gt; tool out of the box.&lt;/li&gt;
&lt;li&gt;Want the wired-up, deploy-ready versions instead of assembling it yourself? I packaged two starter kits — an &lt;a href="https://payperbyte.gumroad.com/l/nszyv" rel="noopener noreferrer"&gt;x402 + verify-before-act Express kit&lt;/a&gt; and an &lt;a href="https://payperbyte.gumroad.com/l/pvykda" rel="noopener noreferrer"&gt;MCP agent kit&lt;/a&gt; (server + examples + deploy/integrate guides), $39 each. The libraries above stay free MIT; the kits are the afternoon you'd otherwise spend wiring them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either way: if your code acts on data it paid for, verify it first. Provenance is cheap. Acting on tampered bytes isn't.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions or corrections welcome in the comments — I'd rather fix something than leave it wrong.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>api</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Verify before your agent acts: a trust check for x402 data feeds (in one npm install)</title>
      <dc:creator>Mark Zhurbin</dc:creator>
      <pubDate>Tue, 23 Jun 2026 18:05:35 +0000</pubDate>
      <link>https://dev.to/0rkz/verify-before-your-agent-acts-a-trust-check-for-x402-data-feeds-in-one-npm-install-4eco</link>
      <guid>https://dev.to/0rkz/verify-before-your-agent-acts-a-trust-check-for-x402-data-feeds-in-one-npm-install-4eco</guid>
      <description>&lt;h3&gt;
  
  
  Your agent paid for the data. That doesn't mean the bytes are real.
&lt;/h3&gt;

&lt;p&gt;Autonomous agents are starting to &lt;em&gt;pay&lt;/em&gt; for data — per call, in stablecoins, over &lt;a href="https://x402.org" rel="noopener noreferrer"&gt;x402&lt;/a&gt;. That solves billing. It doesn't solve trust. A &lt;code&gt;200 OK&lt;/code&gt; from a paid endpoint tells you the money moved. It tells you nothing about whether the bytes you got back are the bytes the publisher actually signed — or whether a proxy, a cache, or a man-in-the-middle rewrote them on the way to your agent.&lt;/p&gt;

&lt;p&gt;If your agent is about to &lt;em&gt;act&lt;/em&gt; on that data — release funds, open a position, file a report, trigger a workflow — "I paid for it" is not the bar. "I can prove it's intact and I know who stands behind it" is.&lt;/p&gt;

&lt;p&gt;This is a 5-minute walkthrough of doing that check before your agent acts, using an open MIT kit. No token, no signup, no API key.&lt;/p&gt;

&lt;h3&gt;
  
  
  See it first — one command, no wallet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @foreseal/demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs entirely offline. It walks an agent through a handful of payloads — a genuine signed one, a byte-tampered one, a forged-key one, one with the receipt stripped off, one with the domain rewritten — and prints &lt;strong&gt;ACT&lt;/strong&gt; or &lt;strong&gt;REFUSE&lt;/strong&gt; for each. The point: a verifier that fails &lt;em&gt;closed&lt;/em&gt;. Tampered, forged, or missing-receipt → the agent refuses. No real money is involved; it's the mechanism, distilled.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the receipt works
&lt;/h3&gt;

&lt;p&gt;Every paid response from the &lt;a href="https://x402.payperbyte.io" rel="noopener noreferrer"&gt;PayPerByte&lt;/a&gt; gateway carries an&lt;br&gt;
&lt;code&gt;X-BYTE-Attestation&lt;/code&gt; header: an &lt;a href="https://eips.ethereum.org/EIPS/eip-712" rel="noopener noreferrer"&gt;EIP-712&lt;/a&gt; &lt;code&gt;PayloadAttestation&lt;/code&gt;.&lt;br&gt;
The recipe is published, machine-readable, at &lt;code&gt;/.well-known/agent.json&lt;/code&gt;:&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="nf"&gt;keccak256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;payloadHash&lt;/span&gt; &lt;span class="nx"&gt;AND&lt;/span&gt; &lt;span class="nf"&gt;recoverTypedDataAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;PayloadAttestation&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;message&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="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;attester&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two legs. The &lt;strong&gt;hash&lt;/strong&gt; leg proves the bytes weren't altered. The &lt;strong&gt;signer&lt;/strong&gt; leg proves the receipt was&lt;br&gt;
issued by the key you expect — not self-asserted by whoever sent the response. You check both, locally,&lt;br&gt;
before acting. (One deliberate detail that looks like a bug but isn't: payments settle in USDC on &lt;strong&gt;Base&lt;/strong&gt;, but the attestation's EIP-712 domain is anchored on &lt;strong&gt;Arbitrum, chainId 421614&lt;/strong&gt;. Settlement rail and trust anchor are intentionally separate.)&lt;/p&gt;
&lt;h3&gt;
  
  
  Wire it into your agent
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @payperbyte/sdk@^0.1.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Version matters: the full hash &lt;strong&gt;and&lt;/strong&gt; signer check (&lt;code&gt;verify&lt;/code&gt; / &lt;code&gt;verifyAttestation&lt;/code&gt; /&lt;br&gt;
&lt;code&gt;verifyFromGatewayResponse&lt;/code&gt;) ships in &lt;strong&gt;0.1.2+&lt;/strong&gt;. Earlier &lt;code&gt;0.1.0&lt;/code&gt;/&lt;code&gt;0.1.1&lt;/code&gt; only export the hash-only&lt;br&gt;
&lt;code&gt;verifyPayload&lt;/code&gt;. Pin &lt;code&gt;^0.1.2&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The one-call path, for a response you just fetched from the gateway:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;verifyFromGatewayResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ARBITRUM_SEPOLIA&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@payperbyte/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Pin this once from https://x402.payperbyte.io/.well-known/agent.json → receipt.attester&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GATEWAY_ATTESTER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0x77c86a5367d941091a31BC97104609F2Db33C472&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&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;getVerifiedData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="o"&gt;&amp;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;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-BYTE-Attestation&lt;/span&gt;&lt;span class="dl"&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;verdict&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;verifyFromGatewayResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ARBITRUM_SEPOLIA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// the attestation domain's chain (421614)&lt;/span&gt;
    &lt;span class="nx"&gt;GATEWAY_ATTESTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// pin it — a self-asserted header can't prove itself&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;verdict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verified&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// fail-closed: tampered, forged, expired-key, or no receipt at all&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`refusing to act on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&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;verdict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&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="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// safe to act on&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;GATEWAY_ATTESTER&lt;/code&gt; argument is not optional ceremony. If you don't pin the attester, the kit&lt;br&gt;
&lt;strong&gt;fails closed on purpose&lt;/strong&gt; — because the &lt;code&gt;publisher&lt;/code&gt; field inside an attestation header is attacker- controlled, so trusting it would let a forged response self-certify. Pin the address from the published recipe; then a forged header has nothing to stand on.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Verdict&lt;/code&gt; you get back is explicit, so your logs tell you &lt;em&gt;why&lt;/em&gt; something was refused:&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Verdict&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;verified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// hashMatch &amp;amp;&amp;amp; signerMatch === true — the one "safe to act?" bool&lt;/span&gt;
  &lt;span class="nl"&gt;hashMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// bytes weren't altered&lt;/span&gt;
  &lt;span class="nl"&gt;signerMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;|&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;// null = no attestation present → fail-closed (never "pass on hash alone")&lt;/span&gt;
  &lt;span class="nl"&gt;recovered&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&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;// the address the signature actually recovers to&lt;/span&gt;
  &lt;span class="nl"&gt;expired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// ADVISORY ONLY — staleness is a freshness axis, not a provenance verdict&lt;/span&gt;
  &lt;span class="nl"&gt;reason&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="c1"&gt;// human-readable, for post-mortems&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note &lt;code&gt;expired&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; flip &lt;code&gt;verified&lt;/code&gt; to false. A once-minted &lt;code&gt;now+300s&lt;/code&gt; deadline makes every aged feed look "expired"; that's a freshness question for your own policy, not a tamper verdict. Surface it, decide for yourself.&lt;/p&gt;

&lt;p&gt;If you're working from the lower-level pieces (raw bytes + the attested fields, or an on-chain event&lt;br&gt;
rather than a gateway header), &lt;code&gt;verify(input)&lt;/code&gt; takes the explicit struct, and &lt;code&gt;verifyPayload&lt;/code&gt; does the hash-only leg on its own. Same fail-closed contract: it throws nothing and always returns a &lt;code&gt;Verdict&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Bonus: verify the &lt;em&gt;counterparty&lt;/em&gt;, not just the payload
&lt;/h3&gt;

&lt;p&gt;The same EIP-712 receipt pattern backs a live &lt;code&gt;$0.05&lt;/code&gt; counterparty screen — a signed go/no-go on an address+domain &lt;em&gt;before&lt;/em&gt; your agent releases funds. It's a paid feed; the verdict comes back as a signed ALLOW/WARN/BLOCK you verify the same way:&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;-i&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://x402.payperbyte.io/feeds/address-reputation &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"domain":"payee-checkout-7x9q.example","address":"0xRecipient","amount":5000000,"chain":"base"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(With no payment header you get the HTTP 402 challenge back — that's the handshake, not an error. This is a &lt;em&gt;counterparty&lt;/em&gt; screen — "is it safe to send here?" — not a seller-reputation score.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Where this is honest about itself
&lt;/h3&gt;

&lt;p&gt;This is early. It's dogfooded end-to-end; the only mainnet settlements so far are our own self-tests, and external adoption is exactly the open question we're publishing this to answer. What it &lt;em&gt;does&lt;/em&gt; prove is narrow and real: &lt;strong&gt;authenticity and tamper-evidence&lt;/strong&gt;, not correctness — your agent learns "these are exactly the bytes that were signed, by the key I expected," not "this data is true." It's MIT, USDC-only, no token, no new contracts to deploy. The verifier is the whole pitch: a small amount of code, hardened to fail closed, that you run before your agent acts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Demo: &lt;code&gt;npx @foreseal/demo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Kit: &lt;code&gt;npm i @payperbyte/sdk@^0.1.2&lt;/code&gt; · Gate (for publishers): &lt;code&gt;@foreseal/gate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Recipe: &lt;code&gt;https://x402.payperbyte.io/.well-known/agent.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;MCP server (drive it from Claude/Cursor): &lt;code&gt;npx -y byte-mcp-server&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I build PayPerByte (machine name "BYTE Library").&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>web3</category>
      <category>security</category>
    </item>
  </channel>
</rss>
