2026 · live

x402 Gateway

HTTP 402-native paywall for agent-to-agent API calls. 1 cent per call, USDC on Base, no accounts.

TypeScript · Hono · Cloudflare Workers · Base Sepolia · USDC · viem · Zod · Durable Objects

Paid calls served
124

last 7 days

p50 latency
180ms

last 7 days (includes payment verify)

Revenue
$1.24

last 7 days — small on purpose

Callers
8 distinct agents

since launch

The problem

The next wave of software is autonomous agents calling APIs to accomplish tasks. But how do they pay for those calls? The current web's financial infrastructure is built for humans. Systems like Stripe require accounts, API keys tied to a credit card, and a human on both sides of the transaction to set things up. This model fundamentally breaks the premise of autonomous, composable agents. An agent can't just "sign up" for an API key.

I wanted to build an API that speaks the language of machines and money. The goal was simple: an endpoint that, when called, responds with 402 Payment Required. The response tells the agent how much to pay, in what currency, and where. The agent can then construct a transaction, sign it with its private key, and retry the original request with the signature attached. If the payment is valid, the API serves the response. No accounts, no cookies, no API keys—just a signature per call. This is a truly permissionless, machine-to-machine financial transaction, settled on-chain.

The constraint

To make this viable, the system had to operate under a strict set of constraints:

  1. Low Latency: The entire payment verification process had to add minimal overhead. The gateway runs as a single Cloudflare Worker, putting the logic at the edge, with a budget of less than 50ms for the entire verification step.
  2. Non-Custodial & Permissionless: The gateway must never take custody of user funds or private keys. There are no accounts, no sign-ups, and no KYC. Any agent with a valid keypair can call the API. This is critical for composability.
  3. Cheap & Fast Settlement: The economics only work with low transaction fees. Settlement happens on Base, an Ethereum Layer 2, for its sub-cent gas fees and fast finality (seconds, not minutes). This ensures that micropayments of a fraction of a cent are economically feasible.

The flow

The entire interaction is a two-step dance native to the HTTP protocol. The client makes a request, is told to pay, and then retries with proof of payment. It's designed to be simple enough for any agent developer to implement.

  1. The agent makes a standard GET request to a protected endpoint.
  2. The gateway intercepts this, checks if payment is required, and responds with a 402 Payment Required status. The Payment-Required header contains the price, the accepted asset (e.g., USDC on Base Sepolia), and the facilitator's address.
  3. The agent's client-side logic parses this header. It then uses a wallet or keypair to sign an EIP-3009 transferWithAuthorization message. This is a gasless approval that authorizes the gateway's facilitator contract to pull a specific amount of USDC from the agent's address.
  4. The agent retries the original request, but this time includes an X-Payment header containing a JSON object with the signature, a unique nonce, an expiry timestamp, and the amount.
  5. The gateway receives the second request. It verifies the nonce hasn't been used, the signature is valid for the given parameters, the expiry is in the future, and the amount is sufficient.
  6. If all checks pass, the gateway forwards the request to the upstream service, gets the response, and returns it to the agent with a 200 OK. The payment settlement is queued to happen asynchronously.

Here is a simplified sequence diagram of the interaction:

Client                     x402 Gateway                 Upstream API
  |                           |                           |
  | GET /api/ask?q=...        |                           |
  |-------------------------->|                           |
  |                           |                           |
  |   402 Payment Required    |                           |
  |   Payment-Required: ...   |                           |
  |<--------------------------|                           |
  |                           |                           |
  | signs USDC transfer       |                           |
  | with its wallet/key       |                           |
  |                           |                           |
  | GET /api/ask?q=...        |                           |
  | X-Payment: <sig_json>     |                           |
  |-------------------------->|                           |
  |                           | verifies signature, nonce |
  |                           |-------------------------->| GET /
  |                           |                           |
  |                           |                           |<-----------
  |                           |                           |
  |                           |                           |   200 OK
  |                           |                           |   { "answer": "..." }
  |                           |<--------------------------|
  |                           |                           |
  |   200 OK                  |                           |
  |   { "answer": "..." }     |                           |
  |<--------------------------|                           |
  |                           | (async) settles payment   |
  |                           | on Base blockchain        |

The nonce registry

To prevent a malicious agent from replaying the same payment signature over and over (a "replay attack"), every payment intent must include a unique 32-byte nonce. The gateway must ensure that each nonce is used only once.

This is the perfect use case for Cloudflare's Durable Objects. I use a Durable Object, keyed by the caller's address, to store a set of hashes of all nonces used by that address. When a new request comes in, the gateway checks if hash(nonce) already exists in the object. If it does, the request is rejected with a 400 Bad Request.

Crucially, this check must be strongly consistent. Eventual consistency would create a race condition where an agent could fire off two requests with the same nonce to different data centers, and both might be processed before the "seen" state propagates. By using a single-region Durable Object, I guarantee that all checks for a given address are serialized and strongly ordered, eliminating the risk of a double-spend. Used nonces are stored with a 24-hour TTL to keep the storage footprint small.

What runs behind the gateway

The gateway itself is just a paywall; it can protect any upstream API. To test the system, I've put a few simple, low-cost services behind it. The prices are intentionally set below my actual cost to encourage agent developers to experiment without financial risk.

  • /api/ask → A short call to Claude 3 Haiku to answer a simple question. Price: $0.001 per call.
  • /api/og → Generates a simple OpenGraph image for a given URL. Price: $0.002 per call.
  • /api/fetch-summarize → Fetches a URL's content and provides a three-sentence summary. Price: $0.005 per call.
  • /api/embed → Generates a single-vector embedding for a short text string. Price: $0.0005 per call.

What broke

Building this was not without its challenges. Several things went wrong along the way:

  1. Signature Malleability: The first version of the signature verification logic only checked that the signature was from the correct address. It was missing the EIP-712 domain separator, which includes the chain ID and verifying contract address. This meant a signature intended for one chain could potentially be replayed on another. This was fixed in v0.2 by correctly implementing the full typed data hashing standard.
  2. Cloudflare Worker CPU Limits: The cryptographic operation ecrecover, used to derive the signer's address from a signature, is computationally expensive. During load testing, I was hitting the Cloudflare Worker CPU time limits on nearly every call. The solution was to replace the pure-JS crypto library with a pre-compiled WASM build of noble-secp256k1. This moved the heavy lifting to near-native code and brought the verification time down from ~45ms to under 5ms.
  3. Denial of Wallet: A well-meaning developer decided to "stress test" the API by sending 400 requests in 10 seconds from the same agent address. While the nonce registry prevented any double-spending, the sheer volume of signature verification requests was enough to cause timeouts. I quickly implemented a simple per-address rate limit (60 requests per minute) to prevent a single agent from degrading service for others.

Why 402 and not Stripe

Stripe is a fantastic product for the human web, but it's the wrong tool for the agent economy. The core difference is the authorization model.

  • Stripe: Authorization is based on a session, represented by a cookie or an API key. This is stateful and tied to an account. It's designed for a user who logs in once and then makes many requests.
  • x402 Gateway: Authorization is per-request, based on a cryptographic signature. This is stateless and tied only to a keypair. It's designed for ephemeral, autonomous agents that may not have a persistent identity or session. This model is infinitely more composable.

Furthermore, the settlement and fee models are worlds apart. Stripe settles in days and charges a percentage fee plus a fixed $0.30. My gateway settles in seconds on Base, and the only fee is the on-chain gas cost, which is a fraction of a cent. You simply cannot build a business on $0.001 micropayments with Stripe's fee structure. The 402 approach, as outlined in emerging standards like RFC 9457, is the only viable path forward for a true machine-to-machine economy.

Design decisions I'd defend

  • Asynchronous Settlement: The API response is synchronous, but the on-chain settlement is asynchronous. When the gateway verifies a signature, it immediately serves the request. The actual transferWithAuthorization call is added to a queue and processed by a separate cron-triggered worker. This provides the lowest possible latency to the calling agent. It introduces a tiny risk of chargeback (if the agent's USDC balance is gone by the time settlement runs), but this risk is bounded by the nonce's short expiry time.

  • Edge Schema Validation: The X-Payment header is a base64-encoded JSON string. The very first thing the worker does is decode and validate this against a strict Zod schema. Any malformed input is rejected immediately, before any expensive cryptographic operations are performed.

    import { z } from "zod";
    
    export const XPaymentHeaderSchema = z.object({
      // EIP-3009 `transferWithAuthorization` signature
      signature: z.string().regex(/^0x[a-fA-F0-9]{130}$/, "Invalid signature format"),
      // 32-byte unique value to prevent replay attacks
      nonce: z.string().regex(/^0x[a-fA-F0-9]{64}$/, "Invalid nonce format"),
      // Unix timestamp (seconds) for when the signature expires
      expiry: z.number().int().positive(),
      // Amount in USDC's 6 decimal units (e.g., 10000 = $0.01)
      amount: z.string().regex(/^\d+$/).transform(BigInt),
      // The address of the caller/signer
      from: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid from address"),
    });
    
  • Prices in Cents: Internally, all prices are defined in cents-USD as integers (e.g., 1 for one cent). The conversion to USDC's 6-decimal representation happens only at the moment of verification. This makes the pricing logic far more readable and less error-prone for humans.

  • One Worker Monolith: The entire gateway—routing, verification, rate limiting, and queuing—is a single Cloudflare Worker. In an era of microservice sprawl, this simplicity is a feature. It makes deployment atomic, reduces network overhead, and simplifies debugging.

What's next

This is just the beginning. The current implementation is a proof-of-concept for a much larger vision.

  • Bazaar Endpoint: A discovery mechanism where other agent-facing APIs can register themselves. This would allow an agent to query a single endpoint to find services that accept this payment method.
  • Prepaid Channels: For high-frequency callers, creating and verifying a signature for every single call is inefficient. I plan to implement a system where an agent can pre-pay a certain amount into a smart contract, allowing them to make many subsequent calls with a much cheaper HMAC signature until their balance is depleted.
  • Mainnet Launch: The ultimate goal is to move this from Base Sepolia to Base mainnet. This will happen once the protocol has been further battle-tested and there is evidence of real, organic traffic from agent developers.

Source

The entire project is open-source under the MIT license. You can find the full code for the Cloudflare Worker, the facilitator smart contract, and an example client in the GitHub repository. Pull requests and feedback are welcome.