Back to blog

Field note

The State Required for Safe Agent Payment Retry

A controlled x402 experiment showing how buyer-held Payment-Identifier and seller-held response state prevented a second settlement after buyer restart.

The first experiment produced a specific failure: a buyer agent paid, restarted before saving the result, then paid again for the same intended purchase.

The payment rail received two distinct, valid authorizations even though buyer intended one purchase.

Agent Payment State Is Not Purchase State described that gap. This follow-up tests one control against it.

The question is narrower:

What state must survive a buyer restart, and where must it be enforced, to prevent another settlement for the same purchase?

The control under test is x402 Payment-Identifier. The seller declares the extension required in its 402 Payment Required response.

State Under Test

The evaluation uses four identifiers with different owners:

IdentifierOwnerPurpose
purchase_idBuyer onlyLocates the buyer’s logical purchase record.
Payment-IdentifierBuyer and sellerBuyer supplies stable retry identity; seller uses it to recover a retained response.
Authorization noncePayment railIdentifies one signed transfer authorization.
Transaction hashChainIdentifies one settled transaction.

purchase_id is part of the experiment’s buyer state, not x402. Its record contains the requested resource and Payment-Identifier. A fresh process loads that record to recover retry identity.

Only Payment-Identifier is sent to the seller through the x402 extension.

The prediction was specific: preserving Payment-Identifier at buyer would prevent another settlement only if seller also retained and returned the earlier paid response.

Observed Retry Path

Buyer persisted Payment-Identifier before payment. After settlement, seller retained the paid response under that identifier.

The experiment then simulated buyer failure before either settlement or resource outcome was saved locally. Only purchase_id and Payment-Identifier survived.

Payment-Identifier recovery across buyer and sellerSeller declares Payment-Identifier required. The first buyer process pays through seller and facilitator, and seller retains the successful response. A fresh buyer process reuses the same identifier and receives the retained response without another settlement.INITIAL PAYMENTRETRY AFTER BUYER RESTARTBUYER PROCESSSELLER BOUNDARYFACILITATORCHAINBuyerfirst processSellerrequires Payment-IdentifierFacilitatorverify and settleChaintransfer recordedCached paid responseidentifier → paid responseBuyerfresh processSelleridentifier cache hitFacilitatornot called402 + requiredpayment + IDsettlerecordsame IDcached result
Seller-required retry identity prevents another settlement only while seller-side cache retains the corresponding paid response.

After restart, buyer retries access to the same protected resource using saved Payment-Identifier. Seller finds a cached entry for that identifier and returns the original paid response with:

X-PAYMENT-IDENTIFIER-CACHE: HIT

The response carries the original transaction hash. The request does not enter facilitator verification or settlement again.

The Base Sepolia transaction records the original transfer of 1000 atomic USDC units. The fresh-process retry returned that result rather than producing another settlement.

Request Binding Added by the Experiment

x402 documentation says seller caches responses by Payment-Identifier and buyer must not reuse one identifier across logical requests. The experiment tested an additional seller control for that misuse.

Seller-side middleware stored a request fingerprint with each identifier using:

  • payment scheme, network, asset, amount, and recipient
  • HTTP method
  • resource path and query

The follow-up reused an existing Payment-Identifier while changing request URL from:

/resource

to:

/resource?variant=conflict

Seller-side middleware compared request fingerprints and returned:

409 Conflict

The conflicting request did not enter another settlement path.

Payment-Identifier identified the retry. Request fingerprint identified the paid operation. The 409 Conflict behavior came from the experiment’s seller implementation, not the native extension.

Results

In practical terms, buyer agent intended to purchase one API response. After restart, its wallet paid twice because new process had no saved record of the first result.

Payment-Identifier changed that experience: restarted buyer recovered original paid response without another wallet payment.

ScenarioObserved result
Buyer restart without retry controlOne intended purchase produced two payment authorizations and two settlements.
Buyer restart with Payment-IdentifierOne intended purchase produced one settlement; seller returned cached paid response with original transaction.
Same Payment-Identifier with changed request URLExperiment-added request binding returned 409 Conflict before settlement.

The supported claim is bounded:

A fresh buyer process can recover an earlier paid response without another settlement when seller requires Payment-Identifier, buyer preserves it, and seller retains the corresponding result.

This result is conditional: buyer must preserve Payment-Identifier, seller must retain the corresponding response, and retry must occur before cache expiry.

Conditions and Limits

Observed result applies when:

  • seller declares and enforces Payment-Identifier
  • buyer preserves Payment-Identifier across restart
  • seller retains original paid response
  • retry arrives before cache expiry

Request binding and 409 Conflict are experiment-added controls, not native extension guarantees.

Seller restart, cache loss, concurrent retries, shared cache behavior across seller instances, and settlement before cache insertion were not evaluated.

Payment-Identifier does not replace application authentication or authorization. It lets seller recognize a repeated paid request; access policy still decides who may receive the protected resource.

Control Placement

Facilitator can verify a signed payment authorization, and chain can record transfer and nonce use. Neither knows whether seller returned protected resource or whether two authorizations came from one buyer purchase.

Seller sees Payment-Identifier before another settlement and retains resource response after payment. Seller can therefore return prior result before another payment begins. Where changed requests must be rejected, seller also has HTTP and resource context required to compare operations.

Safe retry therefore depends on state preserved by buyer and enforced by seller, not on identifier alone.

Outcome Resolution After Cache Eviction

Payment-Identifier recovers an earlier response only while seller retains it. If that state is unavailable, buyer still cannot determine whether payment settled.

When seller no longer retains the response, buyer needs evidence that distinguishes settled, not settled, and unresolved before another payment can be authorized.

References

x402 Payment-Identifier: Extension documentation

Experiment reproduction: agent-control-lab/001-x402-payment-state

Prior finding: Agent Payment State Is Not Purchase State