DEV Community

John O'Malley Jr
John O'Malley Jr

Posted on

A settlement is a claim, not a fact — so I verify every x402 payment

In my last post I put an MCP server behind HTTP 402 and showed a real $0.005 USDC settlement on Base. A reader could click the BaseScan link and check that one transaction by hand. That works for one payment. It does not work for a business.

If you're an API owner thinking about charging agents per call, here's the question that should bother you: when your dashboard says "paid," what actually proved it? A settlement notification is a claim — someone says a payment happened. Claims can be wrong, early, replayed, or mismatched. I didn't want my demo, let alone a client's endpoint, running on claims. So I wired in an independent verifier.

The verifier

Flow Executor built a settlement verifier for x402 on Base and made it free: POST https://flow-kg4x.vercel.app/api/settlement-verify. Send it a signed EIP-3009 authorization, optionally with the settlement transaction hash, and it runs four checks:

  1. Signature recovery — the signature actually recovers to the payer who supposedly authorized the payment.
  2. Nonce consumption — USDC.authorizationState shows the authorization's nonce was actually consumed on-chain. EIP-3009 authorizations are single-use; an unconsumed nonce means the payment hasn't settled, whatever anyone says.
  3. Transfer match — the settlement transaction's USDC Transfer log matches the authorization's payer, payee, and amount.
  4. Validity window — the settled block sits inside the authorization's validity window.

Without a tx hash, checks 3–4 are skipped and the verdict rests on signature + consumed nonce. Base only, for now.

How I wired it into the demo

Every new settlement on https://demo.quinnsconveyance.com now auto-verifies, and the result is stored in Postgres next to the payment:

  • verify_status: verified, failed, unverified_verifier_down, or unverified_error (default unverified)
  • the existing verified boolean, plus verify_detail with what the checks actually returned

One design rule matters more than the rest: verification never blocks a settlement. If the verifier is down, the payment lands anyway — but it's recorded as unverified_verifier_down, permanently, not silently passed. Fail-open, but loud.

That wording is deliberate. My first version just skipped verification when the endpoint was unreachable, which sounds reasonable until you say it out loud: the moment the checker fails is exactly the moment unchecked payments sail through with no trace. A status column fixes that. "Unverified" is honest. Silence isn't.

The first thing it caught was my own pipeline

Within the first batch of settlements, statuses came back failed and unverified where I expected verified. The cause was a timing race: my integration fired verification at settlement time, before the chain state had propagated, so the nonce check ran against a chain that hadn't caught up yet. Re-checks pass — the payments are real — but at verification time, they didn't verify.

I'm publishing that instead of quietly fixing it because it's the whole argument for doing this:

  • If I hadn't stored a status per settlement, I'd never have seen the race. The settlements "worked." The money moved. The gap would have stayed invisible.
  • Verification isn't a one-shot at settlement time. It needs a re-check pass after chain state settles. That's now on my list, in the open.
  • Any volume number reported without a verification column is counting claims. That includes, today, some of mine — which is the point of the column.

Should verify_status be a standard?

Flow Executor has proposed exactly that: every settlement carries a status — verified / failed / unverified — "no status, no trust." I support the convention. I'm cautious about directories and facilitators requiring it before a payment counts: a hard mandate makes the verifier a gatekeeper and a single point of failure for the whole ecosystem. Fail-open-but-loud gets you the trust signal without the chokepoint. Statuses, yes. Permission, no.

Try it / steal it

The verifier is free and live — the endpoint above returns usage docs on a GET. The demo is live with settlements you can inspect: https://demo.quinnsconveyance.com

And if you're an API business whose endpoints are being hit by agents for free: this verification layer is part of what I install on every Quinn's Conveyance deployment, alongside the settlement logging and the challenge→settlement funnel. One endpoint behind 402, live in under a week, $500 flat — waived with an Operate plan. john@quinnsconveyance.com

— John O'Malley, Quinn's Conveyance

Top comments (0)