Portreeve
explainer · 3 Sept 202612 min read

x402 and the abuse problem nobody has priced in yet

x402 lets any script with a wallet pay your API per request. What the protocol does, what sellers lose without the card, and how to count abuse per wallet.

x402 is the first payment protocol where the buyer is expected to be a program. A request hits your endpoint, you answer with HTTP 402 and a price, the client signs a stablecoin transfer and retries with the signature in a header, and you serve the response. No account, no card, no checkout page. Coinbase released it in May 2025, and it has since moved under a foundation at the Linux Foundation governed initially by Cloudflare and Stripe, with Adyen, Visa, Mastercard, and Google among the founding members.

If you sell an API or an AI tool, you will be asked to accept it. Before you turn it on, understand what you give up. The card and the account were never just billing. They were your abuse controls, and x402 removes both.

What the x402 protocol does

The v2 specification describes a round trip with three headers. The seller responds to an unpaid request with status 402 and a PAYMENT-REQUIRED header carrying a base64 JSON object: an accepts array of payment options, each with a scheme (today, exact), a network in CAIP-2 form such as eip155:8453 for Base, an amount in atomic token units, the asset contract address, a payTo address, and maxTimeoutSeconds.

The client picks an option, signs, and retries with a PAYMENT-SIGNATURE header. On EVM chains the exact scheme uses EIP-3009 transferWithAuthorization: the payload is an EIP-712 signature over an authorization with from, to, value, validAfter, validBefore, and a 32-byte nonce. USDC implements this natively, which is why the whole protocol is a USDC protocol in practice.

The seller does not talk to the chain. It posts the payload and its own requirements to a facilitator's /verify endpoint, which checks that the signature recovers to authorization.from, that the wallet holds enough of the asset, that amount and validity window satisfy the requirements, and that a simulated transferWithAuthorization call succeeds. If isValid comes back true, the seller does the work, then posts the same payload to /settle. The facilitator broadcasts the transaction, pays the gas, and returns a transaction hash the seller can echo back in a PAYMENT-RESPONSE header. Coinbase's own flow diagram puts verify before the work and settle after it.

The economics are what make people excited. The CDP facilitator charges nothing for the first 1,000 onchain transactions a month and $0.001 each after that, with a batch-settlement scheme that folds thousands of payments into one transaction. A $0.001 API call that would be absurd on card rails is a normal x402 price.

What the seller loses

Run through what a card checkout gives you that you never had to think about.

An issuer sits behind every card. Before money moves, a bank with a history on that cardholder decides whether the transaction looks like them. You inherit that decision for free.

An account sits in front of every purchase. Signup is where you check the email, set a cookie, look at the device, and rate-limit. A stolen card still has to get through a form you control.

A chargeback sits after every purchase. It is painful, it is expensive, and it is also the only reason a merchant on card rails ever hears that a customer was defrauded. It is a feedback channel.

x402 has none of these. The wallet is its own issuer: the only check is "does it hold the funds and did it sign". There is no signup, because the entire point is that an agent can pay without one. And settlement is final. One commenter on a Hacker News thread about x402, kingo55, described it as a "blockchain where reversal depends on the grace of the merchant." That grace is now your support queue.

The standard reassurance is that finality is a feature: no disputes, no fees, no fraud losses. That is true for the narrow case of a buyer who paid and then lied about it. It is false for every other kind of abuse, because chargebacks were never where the loss was. The loss was always in the free tier, the probing, and the compute you gave away before anyone paid.

The wallet is the new disposable email

A wallet is a keypair. Generating one is a single function call, costs nothing, and produces an address that is indistinguishable from a real user's until it does something. A disposable email address at least had to be issued by a domain you could blocklist. A wallet has no issuer at all.

Sellers are already leaning on the wallet as an identity anyway. QuickNode's x402 free tier is 1 million requests a month per agent, where the agent is identified by the wallet it connects with, and the abuse policy is that they "reserve the right to revoke the free tier for wallets that abuse the system." Read that as an attacker: 1 million free requests per keypair, and the penalty for getting caught is losing a keypair.

The one thing the wallet has that an email never did is a public history. Every transfer into and out of it is on-chain and readable by anyone. A wallet funded five minutes ago by a single transfer from an exchange hot wallet, with no other activity, is a different object from one that has paid a hundred sellers over six months. That history is the strongest reputation signal x402 offers, and few sellers read it at request time. It is expensive to fetch inline, and it says nothing about the first request from a fresh wallet, which is exactly the request an attacker sends.

Old attacks, new clothes

Every abuse pattern you already know maps onto x402. The names change; the mechanics do not.

Free-tier farming becomes wallet farming. If your free tier is keyed by wallet, an attacker with a script mints wallets in a loop and rotates. The per-wallet cap you set is now the per-request cost of a new keypair, which is zero. This is the trial-abuse problem with the one gate you had, the signup form, removed.

Card testing becomes endpoint probing. On card rails, testers run $0 and $1 authorizations to find live cards, and you eat the fees and the disputes. On x402 there is no card to test, but there is your endpoint, your facilitator quota, and your compute. Every paid request that passes /verify costs you the work before /settle runs. A July 2026 study of 15 facilitators covering 99% of observed x402 transactions found violations in every one of them, and catalogued "free shopping" attacks that get the resource without a valid settlement and "service denial" attacks that submit proofs which pass verification and then revert on-chain, burning the facilitator's sponsored gas. The probe is aimed at your infrastructure instead of at an issuer.

Chargebacks vanish, and so does recourse. No disputes also means no dispute data. When an agent drains a compromised wallet through your API, the wallet's owner has no bank to call. The first you hear of it is a support email, or a post naming your service.

Replay becomes the velocity problem. The spec prevents on-chain replay: the 32-byte nonce means the chain will only honor an authorization once. It does not prevent HTTP replay. Researchers at Ohio State, CSIRO, and Manchester resent a single valid payment header against a production endpoint and observed 248 HTTP-layer grants against one on-chain settlement, because the server verified the payload each time and only the first settlement could ever succeed. Their fix is to treat the payment header as a single-use capability and atomically claim it before releasing any response. Yours should be the same, and it should count: a wallet resubmitting the same nonce fifty times is testing you.

Where to stand

You have three hooks that touch money, and each one answers a different question.

Before verify. This is the cheapest place to say no, because you have not called the facilitator yet. You have the wallet address from authorization.from, the requested resource, the client IP, and whatever else the client sent. Rate-limit per wallet here. Check the nonce against a short-lived store and reject duplicates. If the wallet has been convicted before, decline without ever verifying. The v2 spec has no rate-limiting section, so this layer is entirely yours to build.

Before settle. Verification passed, which tells you the wallet can pay. It does not tell you that you want its money. This is where velocity across wallets matters, and where a soft decision belongs: serve the request but mark it, or serve a degraded response, or pause settlement. Do not do expensive work before this point. The same paper measured a revert-grant probability of up to 5.18% in its testbed when servers granted access under optimistic settlement, meaning the work was done and the payment later fell out of the chain.

After settle. You have a transaction hash, or you have a failure. Failures after a successful verify are the signal for the gas-burning attacks above and should be counted against the wallet and everything linked to it. Successes are your ledger. Record the wallet, the amount, the resource, and the device and IP the request came from, because that record is what lets you convict a cluster later.

If you use the reference middleware, you get verify-then-settle around your handler and nothing else. The three checks above are the part it leaves to you.

Counting per wallet is not enough

The instinct is to rate-limit per wallet, because the wallet is the only identifier the protocol hands you. The attacker's counter-move is one line of code: generate a new wallet per request. So per-wallet counting catches lazy abuse and misses the deliberate kind.

The next instinct is per-IP, and it is worse in the other direction. Agents run from cloud egress. A hundred honest agents behind one NAT look like one attacker, and one attacker across a residential proxy pool looks like a hundred honest agents. IP is a real signal, but it cannot be the key you link on.

What works is the same thing that works against trial abuse and card testing: link identities into clusters and count per cluster. Two wallets that sign from the same browser device, fund from the same source, or hit you in the same timing pattern from the same ASN are one actor. Once one of them is convicted, the whole cluster is. Wallet farming only pays if each wallet is judged alone.

Portreeve treats identity.payer_wallet as a hard identity key, hashed and linked the same way as email, device fingerprint, and card fingerprint, so wallets that share a device fold into one cluster with memory, and a convicted cluster blocks its wallets on sight. A seller can call verdict() from x402 middleware today with event_type: "checkout_attempt", the wallet, and the amount. There is no dedicated x402 package yet, and on-chain wallet history is not read; this is the general identity graph pointed at a new key.

In an Express handler that already runs the x402 middleware, the pre-verify check looks like this:

import { Portreeve } from "portreeve";
const portreeve = new Portreeve(process.env.PORTREEVE_SECRET_KEY!);

// after decoding PAYMENT-SIGNATURE, before calling the facilitator
const result = await portreeve.verdict({
  event_type: "checkout_attempt",
  external_user_id: authorization.from,
  ip: req.ip,
  identity: { payer_wallet: authorization.from },
  payment: { amount: Number(authorization.value) / 10_000, currency: "usd" }, // USDC 6dp -> minor units
});
if (result.verdict === "block") return res.status(402).end();

review never blocks the request. The payment proceeds, the event lands in a queue, and a later deny comes back over a signed webhook so you can cut the wallet off. The handling verdicts page covers that split, and the reason codes tell you which counter tripped. On a pure machine-to-machine call there is no browser, so there is no device key: the wallet is the only hard key and IP stays a soft signal. If your buyers are agents running inside a browser, the device fingerprinting snippet gives you the device key back.

A seller's checklist before turning x402 on

Most of this is an afternoon of work. All of it is cheaper than the first weekend an attacker finds you.

  1. Reject duplicate nonces before verify. Keep a short-lived store keyed on the authorization nonce and refuse repeats. This is the replay fix, and it also gives you a counter.
  2. Do no expensive work before verify returns. If the resource costs you compute, gate it behind isValid, and gate anything irreversible behind /settle.
  3. Rate-limit per wallet, and know that it is the weakest limit you have. It stops the lazy script. Set it anyway.
  4. Link wallets into clusters. Device where you can get it, IP and ASN as soft signals, funding source if you read the chain. Count velocity per cluster, and convict per cluster.
  5. Treat verify-passed, settle-failed as an attack signal. It is the x402 version of a card probe, and it costs your facilitator quota.
  6. Cache with intent. The same researchers served cached paid content to all 1,000 unpaid requests through a default nginx proxy cache with no cache-control protection. Set Cache-Control: private, no-store on paid responses.
  7. Keep a ledger you can act on. Wallet, amount, resource, IP, device, transaction hash. That record is your chargeback data now.
  8. Decide your refund policy before you need it. Finality means reversal is on you. Write down when you will and will not send money back, because someone will ask.

The probing shapes above are the same ones card testers run against a Stripe checkout. If you sell on both rails, expect the crew that found your card form to find your x402 endpoint next.

If you want the cluster memory without building the graph, Portreeve's free tier screens 1,000 events a month with no card required. Create an account and call verdict() from your middleware.

← Back to all posts