Paid 200s and unbilled 4xx: the x402 settlement line

x402 specifies how a payment settles, not what the buyer is paying for. Where Invoket draws the line: which answers are billed, which are free, and why.

By Matthias Begot ·

The x402 specification defines how an agent proves it can pay and how the server turns that proof into an on-chain transfer. It does not define what the agent is paying for. Settlement is a server-side call made after the response exists, and no normative rule says a seller must skip it when the request was malformed, the key was unknown, or the answer came back empty. Every x402 seller therefore ships a billing policy — the only question is whether they wrote it down.

This is Invoket’s, stated plainly enough that an agent can rely on it: a 2xx that carries a real answer is billed, everything else is free, including the honest negatives’ opposite — a response with no usable result at all. The interesting part is not the rule, it is the cases that force you to choose, and the failure mode the rule creates on the seller’s side.

What the protocol fixes, and what it leaves open

The loop is fixed. An unpaid call gets a 402 with the terms in the PAYMENT-REQUIRED header; the client signs and replays with PAYMENT-SIGNATURE; the server asks a facilitator to /verify; the server does the work; the server asks the facilitator to /settle; the outcome comes back to the client in PAYMENT-RESPONSE. Two properties of that ordering do all the work here.

The money moves last. Verification happens before the request reaches the business service, but the transfer happens after the response has been produced. A seller who wants to bill only for successful work does not need an escrow, a credit system or a refund path — it needs to not call one function.

The spec is silent on when not to call it. The v2 specification describes /settle as executing a verified payment by broadcasting the transaction, with no MUST or SHOULD tying that call to the outcome of the work, and it contains no discussion of refunds or reversals at all. The facilitator documentation is explicit that verification and settlement results are returned to the server, “allowing the server to decide whether to fulfill the client’s request.” The decision is delegated, on purpose, to the seller.

That asymmetry is worth stating once: not settling costs nothing, settling wrongly is final. There is no chargeback in this protocol. A seller who bills an error has no clean way to undo it, and an agent that was billed for one has no support queue to open.

The signal an agent can read

A buyer does not have to trust prose. The v2 transport binding returns PAYMENT-RESPONSE after a settlement attempt, successful or failed — so its absence on a 200 means no attempt was made. Nothing was broadcast, nothing moved, and the signed authorization simply expires unused.

That is the whole mechanism behind every “free” row in the table below. There is no refund to reconcile, no credit to track, no state on either side. It is also how the dust-amount trial rail works: a trial grant never settles, so a trial call carries no PAYMENT-RESPONSE either.

Where the line falls, case by case

SituationHTTPSettled
Malformed request, bad checksum, out-of-window period4xxno
Well-formed key unknown to the reference404no
Question answered, and the answer is negative200yes
Response carries no usable result at all200no
Upstream error, or gateway timeout on a valid call5xxno

Four of those five rows are uncontroversial once written down. The two that decide whether an agent can trust the number on its balance are the middle ones.

”Unknown” is not “known, with nothing to report”

These look identical from the outside — both are an empty answer to a well-formed question — and they are not the same product.

/company/events checks a SIREN against the Sirene register before it reads BODACC. A nine-digit identifier that passes its checksum but belongs to no French company is a 404 NOT_FOUND, not billed: there is no report to sell. A SIREN that does exist and carries no legal announcement over the stated coverage window is a billed 200 with events: [], a total of zero and the window it looked at. Both verified against the production service on 12 August 2026:

// GET /company/events?siren=123456782  → 404, not billed
{ "code": "NOT_FOUND", "error": "siren not found in the Sirene register" }
// GET /company/events?siren=<active company, no announcement> → 200, billed
{ "data": { "events": [], "total": 0,
            "flags": { "has_open_insolvency_announcement": false },
            "coverage": { "window": { "from": "2008-01-01", "to": "…" } } } }

The same split runs through the catalog: /medication/availability bills the 200 that says no_known_shortage, /company/peppol bills the 200 that says a live company is reachable: false, and /company/vat bills a firm invalid exactly as it bills a firm valid.

A well-founded negative is the product

This is the deliberate part of the policy. An agent that screens a counterparty, a substance or an invoice is buying a decision it can act on, and most decisions in compliance work are negative. “No open insolvency proceeding over eighteen years of BODACC” is not the absence of an answer; it is the answer, and producing it costs the same as producing a hit.

There is also an incentive argument, and it is not subtle: a seller who is only paid when it finds something has a standing reason to find something. Billing the negative removes that pressure entirely.

A response with no usable result at all is free

The mirror case. When a service can answer the shape of the question but has nothing to put in any field of the response — every requested variable empty, coverage complete on nothing — the call is served and not billed.

The layering matters here, because the gateway must never read a business payload to decide a price. The microservice, which knows nothing about payment, declares a business fact on an internal response header (X-Coverage: none, “this response carries no usable result for the request”). The gateway, which knows nothing about the business, applies the billing policy: 2xx with the signal → skip settle, count it, strip the header from the public response. The agent sees its answer, an intact body, and no PAYMENT-RESPONSE.

The risk is asymmetric by construction. A microservice that sets the header wrongly loses the seller revenue; it can never cost the agent anything. That is the correct direction for a bug to fail in.

In practice the signal should stay rare, because most no-data situations have a better answer than an empty paid 200 — an unbilled 4xx that tells the agent what would have worked:

// GET /climate/anomaly?…&from=1997-01-01&to=1997-01-31&variables=wind → 400, not billed
{ "code": "OUT_OF_RANGE",
  "error": "requested period 1997-01-01..1997-01-31 is outside the covered window of
            the requested data: wind is covered 2021-08-12..2026-08-06; temperature,
            precipitation cover this period — request one of those, or move the
            period inside the window above" }

An agent that receives this can fix its own call on the next iteration. An agent that receives an empty billed 200 learns nothing and pays for the lesson.

Errors are 4xx and 5xx, and staleness is neither

Two corollaries that cost nothing to state and are easy to get wrong.

A 200 whose body contains {"error": …} is the single most effective way to bill an agent for a failure, and it is invisible to every monitor that watches status codes. Business errors leave the 2xx range or they are not errors.

And stale reference data is never a 5xx. When a snapshot ages past its threshold, the operator gets alerted; the caller gets a served answer whose provenance.freshness says exactly how old the underlying data is, and decides for itself. Turning old data into a server error would convert a disclosure problem into an outage — and an agent cannot act on an outage.

The mode nobody specifies: work done, not settled

Draw the line at “settle only on success” and you inherit a failure mode. If the upstream service produces a perfectly good 200 but the gateway’s own budget for that call has already expired, the gateway returns 504 and — correctly — does not settle. The compute happened. Nobody paid for it. The agent is right not to be charged for a response it never received, and the seller is right to eat the cost, because the latency was the seller’s.

The consequence is a design rule that has nothing to do with payments and everything to do with getting paid: size upstream timeouts on the cold path, not the median. The gateway’s global budget is 5,000 ms; 34 of the 67 paid endpoints carry an explicit per-endpoint override, capped at 30,000 ms. Every one of those overrides exists because a real call once fell off a cliff a median would never show:

  • /weather/forecast/daily measured 5,885 ms on a cold page cache on 13 July 2026 — a 504 on a call the upstream had answered 200.
  • /company/events measured an 18,301 ms first touch on a SIREN that had never been queried, six hours after container start, in a series of twelve calls whose other eleven ran between 72 ms and 263 ms.

Neither is a slow endpoint. Both are fast endpoints with a cold-index tail, and the tail is precisely where a settlement is silently lost. A p50 tells you nothing about this number; the only measurement that matters is the first touch on a region of the store nobody has read yet.

The mirror case is the one the reference middlewares already agree on: when the work succeeded but the settlement fails, the successful response is replaced by a fresh 402 rather than served for free. The work is lost, and the agent can re-present its payment. It is the conservative side of a documented attack surface — the academic analysis of x402 lists “grant before finality → unpaid service after settlement failure” as its first attack class, and the defense is to release nothing until the transfer is confirmed.

The market has not converged, and that is the point

Two mainstream data APIs, two opposite lines, both stated openly:

SellerBilledFree
SearchApi”only successful searches with a 200 status code incur charges”everything else
Oxylabs”all scraping attempts that return 2xx or 4xx status codes are counted as successful”5xx/6xx system errors

Neither is wrong. Charging for a 4xx is defensible when a malformed request still burns compute, and the buyer is a human with a dashboard and a monthly invoice who will notice. The pay-per-match convention in data enrichment — where a no-hit is never charged — is a third line again, and it exists because a 50 % match rate on a pay-per-attempt price is a doubled price in disguise.

What changes with agents is not the ethics, it is the feedback loop. An agent retries on its own, at machine speed, with no dashboard and no month-end statement. A policy that bills malformed input turns a client-side bug into a recurring, silent, irreversible cost. That is why our 4xx messages are also guiding — they list the accepted parameters or the covered window — and why they are free: the fastest way to stop being asked a broken question is to answer it usefully, once, for nothing.

Write it down, per endpoint

The policy is only worth something if a buyer can read it before it signs. A workable form for any x402 seller:

  • Settle after the response exists, never before, and skip the call rather than build a refund path.
  • Enumerate your 4xx codes as unbilled, and make the messages actionable.
  • Decide, publish, and never fudge the “known but empty” case — that is the one where a lazy design bills a non-answer.
  • Prefer an unbilled 4xx that names the covered window over an empty billed 200.
  • Never turn stale data into a 5xx; declare freshness in the payload.
  • Size upstream timeouts on the cold path, then measure the first touch, not the median.

Ours is published where an agent looks: each API reference page carries a status table with a billed column — see /company/events or /medication/availability — and the terms — rails, amounts, recipient — live in the gateway’s catalog, never in prose like this. The loop itself is runnable in the Quickstart, the discovery surfaces are mapped in For agents, and the assets are listed in Payments and rails.

Sources