Skip to content

Agents Now Pay for API Access: An x402 Report

• Updated
11 min read
View as Markdown Share this post: Share on X Share this post on X Share on LinkedIn Share this post on LinkedIn

Agents can now pay for API access on this site with zero account infrastructure: a live endpoint, POST /v1/resume-eval, returns a deterministic resume evaluation for a flat $0.05, settled in USDC (USD Coin, a dollar-pegged stablecoin) on Base Sepolia. Payment is the authentication; there are no accounts, no API keys, and no OAuth flows. The proof is a confirmed settlement transaction on chain, with a PAYMENT-RESPONSE header carrying the payer, the transaction hash, and the network; Table 1 reproduces the full receipt.

Machine-payable APIs are real, small, and shippable. This post documents the shipped stack, the settlement state machine, two barely-documented replay-protection findings, and what the testnet demonstration does not yet prove. The site’s agent surface was already machine-readable (Agent-Ready Websites); HTTP 402 adds the incentive layer on top of that addressability.

Table 1: Production settlement receipt for POST /v1/resume-eval, observed October 2026.
FieldObserved value
Challenge statusHTTP 402 with Base64-encoded PAYMENT-REQUIRED header
Price50000 base units ($0.05; USDC uses 6 decimals)
AssetUSDC at 0x036CbD53842c5426634e7929541eC2318f3dCF7e
Networkeip155:84532 (Base Sepolia testnet)
Settlement transaction0x384144ffd91fb9a3dc01ef1069b4607fae0794f8e2c71b8ddbe4598939986429
PAYMENT-RESPONSE{ success: true, payer, transaction, network }

What Is x402?

x402 is an open protocol that turns HTTP status code 402 into a machine-negotiable payment request, in its version 2 specification from December 2025. Historically, 402 sat unused in the HTTP standard; x402 activates it for agents: a server answers an unauthenticated request with 402 plus a structured challenge, and the client pays and retries.

Three Headers, One Payment

The protocol runs on three headers. PAYMENT-REQUIRED carries the challenge: a Base64-encoded JSON document declaring the price, the asset, and the network. PAYMENT-SIGNATURE carries the client’s signed payment on the retry. PAYMENT-RESPONSE returns the settlement receipt on the successful response.

Version 2 renamed the v1 X-PAYMENT headers to these scheme-neutral names and adopted CAIP-2 chain identifiers such as eip155:84532. The scoped @x402/hono package implements v2; the unscoped legacy package implements v1.

The Challenge Is Self-Describing

The decoded challenge from production shows how self-describing the contract is. The agent learns the exact amount, the token contract, and the chain identifier without any out-of-band configuration.

Code Snippet 1: The 402 challenge returned by production, Base64-decoded and abridged. Values are as observed; amount is in the token’s base units. Once the X402_PAY_TO secret is set, the challenge also carries a payTo receiving address, elided here.
{
  "resource": {
    "url": "https://clouatre.ca/v1/resume-eval",
    "description": "Resume rubric evaluation",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:84532",
      "amount": "50000",
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
      "maxTimeoutSeconds": 60
    }
  ]
}PAYMENT-REQUIRED

How Does the Settlement State Machine Work?

Figure 1 shows the machine as its four actors see it; this section walks it gate by gate, because each gate exists to keep a specific failure from reaching settlement.

The Ordering Is the Security Property

The route verifies the payment signature first via the facilitator (a public service that checks payment proofs on behalf of resource servers; the verify call is free), then checks for replays, then validates input, then executes, and settles only after a 2xx response exists. The verify-first order is deliberate: because verify is free, unauthenticated garbage never writes to Durable Object storage, and duplicate detection costs only the free call.

Client Worker ReplayGuard Facilitator 1. POST + signature 2. verify (free) 3. reject: 402 4. check sig hash 5. duplicate: 409 8. settle (1-3s tail) 9. receipt 10. 200 + PAYMENT-RESPONSE 6. validate input, 7. evaluate on-chain wait
Figure 1: One paid request across the four actors. The two facilitator hops are the call’s entire outbound subrequest budget; the synchronous wait during step 10 is where the one-to-three-second chain tail lands. Dotted arrows are rejections and receipts.

The load-bearing detail is the final step. Settlement, the on-chain transfer of the $0.05, fires strictly on 2xx. A malformed request dies at validation with a 400, which is not a 2xx, so the middleware never charges for it. A duplicate signature dies at the replay check with a 409 before the handler runs. The route costs the operator nothing unless the caller receives a valid response, a property worth calling fail-closed commerce.

One consequence matters to client implementers: the replay guard records the signature before input validation, so a payment signature is single-attempt per HTTP transaction regardless of status code. A client whose first attempt died with a 400 cannot retry the same signature; the retry gets a 409 even though nothing settled.

Latency and Subrequest Budget

The ordering also bounds the latency and subrequest budget. Each paid call makes two outbound facilitator subrequests, one for verify and one for settle, plus the evaluation compute. Settlement is an on-chain operation and adds a one to three second tail; because the response releases only after settle, client-facing timeout expectations must account for that tail. The maxTimeoutSeconds value of 60 in the challenge exists for exactly this reason.

A Deliberately Small Stack

The shipped stack is deliberately small: Hono v4 (a lightweight router framework) dispatched only for the /v1/* prefix, @x402/hono 2.28.0, the public facilitator at https://x402.org/facilitator, and a Durable Object (a single-threaded Cloudflare stateful actor) named ReplayGuard. The 600-line production handler is untouched; the dispatch is a three-line prefix check.

What Does Replay Protection Look Like in Practice?

Replay protection, the defense against one payment being reused for many requests, turned out to have two independent layers, and the interaction between them produced the most interesting findings of the project. Neither layer relies on application logic staying correct; one is enforced by chain semantics, the other by execution isolation.

Layer One: Execution Isolation

The first layer is the ReplayGuard Durable Object. Its check() method performs an atomic check-and-record of a payment signature’s hash; Durable Object execution is single-threaded, so no two requests can interleave inside it. Entries expire after 120 seconds, swept lazily on access and by a single rolling alarm that purges every expired entry and rearms to the earliest remaining expiry. A newer signature can move the alarm earlier, which is safe: the alarm only sweeps expired entries, so firing sooner merely purges them sooner. The 120-second window is not arbitrary: the @x402 SDK (2.28.0) derives the EIP-3009 authorization’s validBefore deadline from the challenge’s maxTimeoutSeconds (60), so a signature older than 60 seconds is permanently unspendable on chain, and the guard’s memory outlives the signature’s cryptographic validity by design (Figure 2).

EIP-3009 validity (validBefore) ReplayGuard DO memory (TTL) Chain nonce (permanent) 0s 60s 120s signature expires DO memory ends
Figure 2: Coverage overlap over one signature’s life. The Durable Object’s memory ends 60 seconds after the signature itself expires, so no gap exists between the application guard and the chain guard.
Code Snippet 2: The core of check() in the ReplayGuard Durable Object. The check and the record happen in one call, which makes the duplicate window impossible to interleave.
async check(sigHash: string): Promise<"ok" | "duplicate"> {
  const now = Date.now();
  const existing =
    await this.ctx.storage.get<number>(`nonce:${sigHash}`);
  if (existing !== undefined) {
    if (existing > now) return "duplicate"; 
    await this.ctx.storage.delete(`nonce:${sigHash}`);
  }
  const expiresAt = now + TTL_MS;
  await this.ctx.storage.put(`nonce:${sigHash}`, expiresAt);
  const alarm = await this.ctx.storage.getAlarm();
  if (alarm === null || expiresAt < alarm) {
    await this.ctx.storage.setAlarm(expiresAt);
  }
  return "ok";
}src/lib/replay-guard.ts

Layer Two: Chain-Enforced Nonces, and Two Field Findings

Which layer catches a replay depends on when it arrives, and the answer changes twice over one signature’s life; Table 2 walks the windows.

Table 2: The three defense windows against payment replay, over one signature’s lifetime.
WindowElapsed timeCatching layerReturned status
In flight0 to ~3 secondsReplayGuard DO, atomic check-and-record409 replay_blocked
Post-settlement~3 to 60 secondsFacilitator, spent EIP-3009 nonce on chain402
Post-expiryOver 60 secondsEIP-3009 validBefore deadline passed402

Finding one is about test environments. Under wrangler dev, Cloudflare’s local development server, the Worker’s configured custom domain rewrites the request host, so the SDK derives the resource URL as the production domain even on localhost. The public facilitator accepts verify and settle calls for that dev-origin resource without complaint, which is less mysterious than it looks: the facilitator validates the signed payment against the requirements that the resource server itself submits, resource URL included, and never probes the origin. Teams reproducing x402 handshakes locally should expect test runs to reference production URLs and to settle anyway.

The intuitive expectation for a post-settlement replay is a conflict error; the field result is HTTP 402. That mismatch is the finding: the facilitator rejects the spent EIP-3009 nonce (EIP-3009 is the Ethereum standard that lets USDC transfers carry a single-use nonce, so the chain itself enforces one-use payments) before the handler ever runs. The Durable Object’s 409 therefore covers only the concurrent verify window, the seconds between the first verify and the settlement landing on chain. The two layers are independent and both fail closed; across three settled test runs, no double-settlements occurred.

What Did the KISS Re-Scope Cut?

The initial architecture plan sequenced the full Hono re-route of the production Worker, a dedicated custom domain, Coinbase Developer Platform secrets, and telemetry before verifying a single transaction. The implementation re-scope inverted that: it cut everything not strictly security-bearing, validated the full handshake under wrangler dev before any deploy, and shipped in three incremental changesets within a single session.

Table 3: Planned architecture versus shipped implementation.
Original planKISS decision
Full Hono re-route of the 600-line handlerHono dispatched only for /v1/*; default branch untouched
api.clouatre.ca custom domain plus CORSExisting production routes; no DNS changes
CDP facilitator with JWT secretsPublic facilitator; no secrets issued
Analytics Engine telemetry and KV audit layerDeferred to a later graduation stage
Playwright suite and worker typecheck gateOne client smoke test as the merge gate

The Lesson for Live Workers

Minimal means not touching the default branch of the fetch handler; a prefix dispatch preserves existing production behavior exactly. Because the full handshake passed locally before any deployment, the merge became a non-event. The deferred features remain the production end-state; the initial release proved the payment primitive first.

What Doesn’t This Prove Yet?

The honest answer is that the strongest claim this demonstration supports is narrower than it looks.

Testnet USDC has zero value. No real money moved, and no charity has received anything yet; proceeds are designated for the Montreal Indigenous Community NETWORK, so the mainnet switch carries a real obligation. Mainnet itself is a deferred, telemetry-gated decision that requires a Coinbase Developer Platform facilitator, JWT secrets, and a real receiving wallet. Replay protection covers a 120-second window, after which expiry removes the guard’s memory; the chain’s single-use nonce is what protects late replays. Volume is roughly four payments ever, which is evidence of correctness, not evidence of demand.

For readers reproducing the handshake, one caveat belongs on the record: settlement requires the X402_PAY_TO receiving-address secret, and the challenge omits payTo until it is set, which blocks any funded run against the deployed URL. The receipt in Table 1 was captured only after that step completed.

What Comes Next?

The gate to mainnet is a strict graduation checklist: production telemetry first, then the CDP facilitator with authenticated secrets, then a dedicated receiving wallet, and only then a value-bearing asset. Until it opens, the endpoint keeps settling at $0.05 in testnet USDC.

Two extensions are already scoped. The first is an MCP (Model Context Protocol) tool surface at /mcp, where the same evaluation core is exposed as a paid tool with the price declared in tool metadata, extending the read-only tools evaluated in Does WebMCP Pay Off for Content Sites? from free reads to a paid write. The second is a paid decision-judgment endpoint: typed questions in, typed verdicts out, priced per judgment batch, which turns an existing stateless service into a metered one with the 402 challenge as its entire access-control layer.

For technical executives, the takeaway is concrete: machine-payable APIs require no billing vendor and no identity system, just a Worker, a payment middleware mount, and a receiving address. The open questions, demand and treasury operations on mainnet, are business decisions that the protocol deliberately leaves untouched.


Previous Post
When Should an AI Agent Enumerate, and When Delegate?

Related Posts