Blog · x402 & micropayments
HTTP Said No. The Chain Said Yes.
Our first real-money x402 payment on Stellar mainnet came with a $0.02 phantom charge: HTTP said the payment failed, the chain settled it 30 seconds later. How a governed agent ledger caught the race — and the four bugs one incident report surfaced.
August 5, 2026 · 11 min read
At 07:29 UTC on August 5, 2026, our payment agent asked a vendor's API for a
page of trading signals. The vendor answered HTTP 402 Payment Required with
an empty body — the x402 protocol's way of saying you haven't paid me. Our
client accepted the refusal, recorded the attempt, and moved on.
Thirty seconds later, on the Stellar public network, the payment settled anyway.
Transaction f03a5d5b…
moved 0.02 USDC out of our agent's wallet at 07:29:36, for a request whose
response was two bytes: {}. We were charged for nothing — a phantom charge.
A typical x402 client would never have known. It retries, eventually gets a
200, and reports success; nobody looks at the wallet. Our agent's ledger knew
to the cent. This is that story — and it's not the story of a bad vendor. It's
the story of two clocks.

The settlement that "didn't happen": 0.02 USDC, 07:29:36 UTC, thirty seconds after the final HTTP 402.
Context in 90 seconds
x402 turns HTTP 402 into a payment flow: a server
quotes a price in a machine-readable header, the client signs a stablecoin
transfer, a facilitator submits it on-chain, and the server releases the
content. On Stellar, the exact scheme signs a Soroban authorization for a
transfer on Circle's USDC contract, and the facilitator sponsors the network
fees — the agent needs no gas.
Our contribution is the layer x402 deliberately leaves out: governance. Before an agent signs anything, a policy gate checks the price against a quote band, the asset and network against allowlists, the payee against a declared address, and the day's spending against a per-agent budget. Every decision — allowed, blocked, settled, failed — is appended to a hash-chained ledger, and the ledger entry itself is the budget reservation: money is committed at authorize time, not at confirmation time. Keep that last detail in mind; it's the hero of this story.
The milestone: a governed payment on mainnet
Until last week this ran on testnet. On August 5 we executed our first real-money sequence, every step verified on-chain before the next:
| Step | What | Evidence |
|---|---|---|
| 1 | USDC trustline (Circle issuer) | 170b3a3e… · ledger 63807310 |
| 2 | Swap 18 XLM → 2.9927268 USDC, DEX path payment with a 1% slippage floor | ad5bd33c… · ledger 63807316 |
| 3 | Governed x402 payment: $0.02 for a signals page, HTTP 200, quoted == settled | 9f102575… · ledger 63807353 |
The paying agent has its own identity in the ledger (signal-scout-mainnet)
with its own $0.50 daily ceiling, isolated from our testnet agents. The fees on
both x402 payments were paid by a third-party fee_account — sponsored fees,
measured on-chain, exactly as the protocol promises.
Full disclosure: the vendor — a trading-signals API on Stellar — staked us the credit for these tests, and we were their first mainnet payment too. Being the guinea pig was the deal, and friction was expected. It arrived within the first minute.

The payment that worked: same amount, same contract — different invoker account. That detail turned out to matter.
The race, reconstructed on-chain
Our first payment attempt — a few minutes before the successful one — didn't
return a 200. The client signed the payment, submitted it, retried the
resource, and got 402 with an empty body every time. It gave up and reported
failure. The gate recorded the attempt with settled 0.
Then we measured the wallet. Two debits of 0.02 USDC, not one.
The chain tells the rest. The settlement for the "failed" attempt landed at
07:29:36, roughly thirty seconds after the final 402 — content never
delivered. And one more detail visible to anyone on a block explorer: the
phantom transfer and the successful transfer were invoked by two different
accounts (GA2X…CIAC and GCZM…ORXI).
Our first hypotheses were wrong, and it's worth saying so. Two facilitators? A failover that re-executes? When we reported the incident — hashes, timestamps, invoker accounts — the vendor confirmed the charge within a day and explained the topology: one facilitator, many relayer accounts. The two invokers are a load-balancing pool, not redundancy. And the race doesn't live in the vendor's own code: they traced it upstream to the off-the-shelf x402 middleware they use and its hosted facilitator service, where verify-and-settle and answer-the-HTTP-request run on different schedules. An upstream issue is being filed.
What we know for certain, because the chain says so: the HTTP layer said no payment happened, and the settlement layer said yes it did — thirty seconds apart.
Why this is nobody's fault (and everybody's problem)
An x402 response and an on-chain settlement are two events produced by two systems with two clocks. The middleware answers your request now; the facilitator's relayer pool settles the transfer when it settles. Between those clocks there is a window, and in that window every possible lie can happen: "you didn't pay" (you did), "here's your content" (the payment will fail), or — our case — a refusal followed by a quiet charge.
Card networks have fought exactly this for decades; it's why authorization and capture are separate phases with separate semantics and a reconciliation industry in between. Agentic payments are speedrunning the same discovery. Here's a verifiable prediction: every new x402 facilitator will rediscover some version of this race. The bug we hit wasn't in our vendor's product; it was in the shared middleware layer half the ecosystem will reach for. That's not a reason to avoid x402 — it's a reason to stop trusting the HTTP response as your source of financial truth.
What the ledger caught — and how
The gate's accounting survived the race for one boring, structural reason:
reservation happens at authorization. When the agent decided to pay, the
ledger appended an entry that committed $0.02 of the daily budget —
atomically, before any signature. When the attempt "failed," the reservation
stayed committed (a closed-fail: failures never release money until proven
unspent). So when the phantom settlement landed thirty seconds later, it
landed inside an already-reserved slot. spentToday: 0.04 — matching the
chain to the cent, with zero overspend and zero lost records.
Honesty requires the other half: the ledger's entry for the phantom attempt
reads SETTLED · HTTP 402 · settled 0, because we measured the balance before
the late settlement confirmed. The money is accounted for; the narrative on
that one entry is incomplete. Two improvements are now on the roadmap because
of it: a distinct verdict for settlements whose HTTP status wasn't 200, and
a periodic balance-vs-ledger reconciliation so a late settlement updates the
record instead of a human noticing a delta.
The bug report had a bug
This section exists because the vendor earned it.
Our incident report included the phantom transaction's hash. The vendor checked
it and found that it matched the real transaction for exactly the first 16
characters — and then diverged. Something in our pipeline had taken a
truncated hash (f03a5d5b1da5c888…, which is how our docs had recorded it) and
completed it with plausible-looking garbage. Worse: our evidence screenshot
had been captured from a URL built with that invented hash, so it was a
screenshot of a "Transaction not found" page. Our proof of the phantom charge
was itself a phantom.
In an incident report, a wrong hash sends someone hunting for a transaction that doesn't exist. The vendor caught it precisely because they verified instead of trusting — the same discipline this whole post is advocating, pointed back at us. We've adopted the rule that should have existed already: hashes are copied from the source, in full, or not cited at all. Verification isn't a step in the pipeline. It's a culture, and it applies to your incident reports exactly as much as to your payments.
What a naive client saw: nothing. What a rigorous report unlocked: everything.
Run this same day through a bare x402 client and here's the log: attempt
failed, retry succeeded, 200 OK, content delivered. Total narrative: "it
works." The double charge is invisible — no error, no alert, no line item. At
$0.02 it's an anecdote. At fleet scale — thousands of agents, each making
thousands of micropayments against dozens of vendors — it's an accounting hole
with no agreed owner.
The counterfactual matters just as much. Because the report we sent had timestamps, complete (eventually…) hashes, and both relayer accounts, the vendor reproduced the incident in minutes and confirmed it within a day. And their investigation kept going: their receipts, it turned out, had never stored the settlement hash — so a charge-without-delivery was structurally undetectable on their side too. They built the reconciliation, ran it, and found two more unrelated bugs of their own. The final tally of one $0.02 incident report: four bugs — and only one of them was upstream.
Two independent ledgers, reconciled against the same chain, made four bugs visible that zero ledgers had kept invisible. That's the whole thesis of agentic payment governance in one sentence.
Takeaways for builders
- Reserve at authorization, not at confirmation. If money is committed the moment your agent decides to pay, a late settlement can't overdraw you.
- Measure settlement on-chain, never from the HTTP status. The response is an opinion; the chain is the fact.
- Reconcile balance against ledger on a schedule. A delta you notice by accident is a bug in your process, not just in someone else's code.
- Give every agent its own identity and budget, per network. Isolation turned this incident into a $0.02 curiosity instead of a fleet-wide audit.
- Treat 402-after-payment as an expected case. It's a race you will lose eventually; decide in advance what your ledger writes when it happens.
- Copy hashes from the source — complete, or not at all. Your incident report deserves the same rigor as your payments. Ours didn't have it; now it does.
Where things stand
The charge was confirmed by the vendor in under a day, an issue is being filed with the upstream middleware project, and both sides now reconcile receipts against the chain — which already caught more than this one bug. The phantom $0.02 stays on our books, unrefunded by mutual agreement: it's the cheapest tuition either of us has ever paid.
Our agent goes back to buying signals tomorrow, $0.50 a day, every decision in the ledger. If you're building agents that spend real money, the uncomfortable summary of this post is: the payment protocol will work, the HTTP layer will lie to you eventually, and the only thing standing between those two sentences is whether you kept your own books.