2026-07-15

An x402 Challenge Is a Quote, Not Authorization

A 40-case pre-signing contract binds task intent, request, payee, network, asset, amount, budget, approval, idempotency, and settlement state before an AI agent can pay.

An x402 Challenge Is a Quote, Not Authorization cover illustration

Pre-signing payment admission

The dangerous x402 failure is not a rejected payment. It is a successful payment that no retained task intent authorized. A PaymentRequired object tells an agent what a server will accept. It does not tell the agent whether the purchase belongs to the task, fits the budget, targets the expected resource, or deserves a signature.

That missing decision belongs in a narrow admission layer between the HTTP client and the signer. The layer binds method, resource URL, scheme, network, asset, payee, atomic amount, timeout, payment identifier, task intent, session budget, and any approval receipt into one canonical purchase envelope. A signer receives the envelope only after every field passes.

A deterministic local fixture exercised that boundary with 40 synthetic challenges. Three reached READY_TO_SIGN, one already-settled retry returned a cached receipt, and the remaining 36 stopped. No wallet, token, funded account, facilitator, external request, signature, blockchain transaction, settlement, or real purchase was used.

Retained result: 40 of 40 expected decisions matched across 25 observable states. Fixture SHA-256: 5142d138397d4acde6dbd8e4baadb8e2336691c9e1a58ec0791f97335cf1ceed.

A payment-required message is external input

HTTP itself gives status code 402 no general payment semantics. RFC 9110 reserves it for future use. x402 supplies a concrete protocol: a resource server can return a versioned PaymentRequired object, the client selects one accepted requirement, forms a signed payment payload, retries the request, and later receives settlement information.

The current x402 v2 specification is precise about the wire objects. A requirement names a scheme, network, atomic amount, asset, payee, timeout, and resource. For the exact EVM scheme, the signed authorization additionally binds a destination, value, validity window, and nonce. Facilitator verification checks the signature and the requirement-specific payment facts before settlement.

Those checks protect the payment protocol. They do not reconstruct the agent's business intent. The core specification explicitly lists client-side budget management and session handling as out of scope. That is not a defect; it is the seam where the application must add its own authority model.

Protocol claimThe server offers one or more payment methods for a resource.
Task claimThe agent was actually asked to buy this exact resource under these limits.
Signer claimOne approved envelope may become one payment authorization.

Collapsing the three claims into “the endpoint returned 402” grants the remote server too much influence. It lets a changed redirect, query, payee, asset, or network become an instruction to the wallet. The server is allowed to propose terms. Only the local contract can admit them.

Validation: forty mutations met the gate

The base fixture described one synthetic GET request for a weather resource. It used protocol version 2, the exact scheme, a pinned test network, one placeholder asset, one placeholder payee, an amount of 1,500 atomic units, a per-request cap of 2,000, and 5,000 units of remaining session budget. The only advertised extension was payment-identifier. Amounts were strings and compared as integers, not floating-point prices.

Every negative case changed one boundary. The decision function is ordinary JavaScript with a fixed order. It has no model call and no “close enough” branch.

40synthetic challenge cases
3purchase envelopes admitted
36held or blocked
25observable decision states
MutationObserved decisionBoundary retained
Method, path, query, or origin changed.BLOCK_RESOURCEThe signature cannot authorize a neighboring request or redirect target.
Scheme, network, asset, or payee changed.One exact protocol-field blockA valid payment method is not automatically the approved payment method.
Amount was decimal, negative, zero-prefixed, zero, or above the request cap.BLOCK_AMOUNT_FORMAT or BLOCK_REQUEST_CAPAtomic units stay canonical and bounded before arithmetic.
The request fit its individual cap but exceeded remaining session budget.HOLD_SESSION_BUDGETSmall repeated purchases cannot silently cross the task budget.
An unknown extension or invalid payment identifier appeared.BLOCK_EXTENSION or BLOCK_PAYMENT_IDOptional protocol features do not gain authority merely by being advertised.
The same payment identifier was reused for different terms.BLOCK_PAYMENT_ID_CONFLICTIdempotency cannot alias two purchases.
A threshold purchase lacked an exact approval, or the approval bound another intent, amount, payee, or expiry.One approval hold or blockApproval is a receipt for exact terms, not a reusable “yes.”
The client asked for a raw wallet signer or supplied incomplete provenance.BLOCK_SIGNER_ROUTE or BLOCK_PROVENANCEThe model never receives generic signing authority.
A prior settlement result was ambiguous.HOLD_RECONCILIATIONUncertainty cannot trigger a second payment.

This is a policy fixture, not a live protocol conformance suite. It shows that the proposed application gate makes deterministic decisions over retained inputs. It does not prove wallet security, facilitator correctness, token behavior, network finality, or vendor interoperability.

The purchase envelope is the authorization boundary

An agent-friendly client often hides the x402 cycle behind a fetch wrapper: request, parse 402, create a payload, sign, retry. That convenience is valuable after admission. Placing an unrestricted signer inside the wrapper is the risky shortcut.

The safer interface accepts a purchase envelope, not an arbitrary challenge:

{
  "contract": "x402-admission-v1",
  "intentId": "intent_demo_weather_001",
  "request": {
    "method": "GET",
    "url": "https://api.example.test/weather?city=berlin"
  },
  "accepted": {
    "scheme": "exact",
    "network": "eip155:84532",
    "asset": "0x...0042",
    "payTo": "0x...0099",
    "amount": "1500",
    "maxTimeoutSeconds": 60
  },
  "paymentIdentifier": "pay_demo_weather_001",
  "budget": {
    "perRequestCap": "2000",
    "remainingBeforeAdmission": "5000"
  },
  "decision": "READY_TO_SIGN"
}

Canonicalization happens before hashing or approval. Normalize the URL with application-specific rules; reject redirects unless the resulting origin and path are separately authorized; compare amounts as base-10 integer strings; use the protocol's exact network and asset identifiers; and preserve the unmodified payment requirement used to form the payload.

Layered collage of six exact admission rings sealing one purchase envelope while a mismatched ring is diverted
The signer receives one canonical envelope. A mismatch in any ring diverts the challenge before a payment payload exists.

The x402 v2 PaymentRequired object can advertise multiple accepted methods. Selection therefore belongs inside policy too. “Pick the cheapest” is not sufficient if the cheapest option uses an unapproved network, asset, transfer method, facilitator route, or timeout. A deterministic selector should filter by the allowlist first and compare prices only among admitted candidates.

Budget reservation happens before signature

A per-request cap answers only one question. An agent can remain below it and still drain a session through repetition. The admission store needs both the task's total budget and the amount already reserved by in-flight intents.

Reservation should be atomic. When a challenge passes policy, the store moves the intent from created to admitted and reserves the exact amount in the same compare-and-swap operation. If two workers race to admit the same intent, one wins. If two different intents compete for the last budget, the store admits only the one whose reservation fits.

The fixture keeps this small: 1,500 units pass with 5,000 remaining; the same amount also passes with exactly 1,500 remaining; an 1,800-unit purchase pauses when only 1,700 remains. Production systems need a richer ledger, but the invariant stays simple:

available = task_limit - settled - reserved
admit only when amount <= per_request_cap
                 and amount <= available

Approval thresholds sit on top of that ledger. The synthetic policy requires an approval at 1,750 units. The receipt binds the intent ID, amount, payee, decision, and expiry. Change any one and the receipt is unusable. A human approving “the weather API” did not approve a later amount or a different recipient.

This is deliberately less convenient than a wallet with an open session key. It turns spending into a state transition that can be inspected and replayed. It also prevents a model from treating a conversational phrase such as “go ahead” as a durable financial capability.

Ambiguous settlement freezes the intent

Automatic retry is safe for an ordinary idempotent read. Payment changes the cost of uncertainty. A client can time out after the facilitator broadcasts a transaction but before the resource response arrives. Retrying with a new payment identity can pay twice. Reusing the old identity against different terms can be worse.

The official x402 payment-identifier extension addresses this class of problem at the resource-server or facilitator layer. It defines an ID that can deduplicate repeated submissions. The current specification recommends binding that ID to a normalized fingerprint containing scheme, network, asset, amount, payee, resource path and method, and an application operation identifier. The same ID with different terms should return a conflict rather than execute again.

The application ledger still needs a state for evidence that has not arrived. In the fixture, priorSettlementState: ambiguous yields HOLD_RECONCILIATION. The amount remains reserved. No new signature is created. A reconciliation worker must query a trustworthy receipt or settlement source and close the original intent before another purchase can be admitted.

Layered collage of one payment token frozen in a holding chamber while a duplicate retry path is physically blocked and a receipt path remains open
Ambiguity is a ledger state, not permission to try again. The original reservation stays frozen until a receipt reconciles the intent.

A confirmed settled retry follows the opposite path. The synthetic gate returns RETURN_CACHED_RECEIPT; it does not sign or spend again. A confirmed failure may release the reservation, but only after the failure source and retry policy are explicit.

Receipts close the exact intent

A successful HTTP response is useful evidence, but it is not the whole audit record. The retained receipt should connect the task intent, challenge fingerprint, payment identifier, signed payload hash, selected requirement, facilitator or local settlement result, transaction reference where applicable, resource response digest, and budget transition.

That record supports three questions after the fact:

  • Which task statement and policy admitted this payment?
  • Did the settled network, asset, payee, and amount match the admitted envelope?
  • Was the resource response returned once, cached on retry, or left unresolved?

Do not give the model raw private keys, a generic wallet signer, a browser with wallet approval prompts, or a shell that can invoke those tools indirectly. Expose one admission function and one signer that accepts only its sealed output. OpenClaw's tool-policy guidance is relevant here: denying named tools does not make broad execution narrow. The capability graph has to match the policy sentence.

Evidence and the untested boundary

  • x402 protocol specifications at commit 5a587d3 — v2 payment requirements, payloads, settlement responses, schemes, transports, and the explicit exclusion of client budget/session policy from the core specification.
  • x402 exact EVM scheme — signed destination, value, validity, nonce, network, token, and scheme-specific verification requirements.
  • x402 payment-identifier extension — idempotency-key behavior and normalized request binding for repeat submissions.
  • Buyer quickstart — the automatic client cycle that parses a 402 challenge, creates a payment payload, and retries with the payment signature.
  • RFC 9110 §15.5.3 — HTTP 402 remains reserved in general HTTP semantics.
  • OpenClaw sandbox and tool-policy documentation — deny precedence and why generic execution remains generic authority.
  • Local deterministic fixture — 40 cases, 40 matches, 3 admitted envelopes, 36 held or blocked, one cached receipt return, and 25 decision states. SHA-256: 5142d138397d4acde6dbd8e4baadb8e2336691c9e1a58ec0791f97335cf1ceed.

The retained experiment stops before money. It does not demonstrate a wallet, token allowance, funded testnet, facilitator, payment signature, blockchain broadcast, settlement finality, refund path, resource delivery, or production x402 interoperability. Before using this pattern with value, test the chosen scheme and network in a dedicated environment, review the wallet and facilitator threat models, run duplicate and timeout drills, and reconcile every state transition against real receipts.