Paying with x402
A second way to fund an order: instead of sending a transaction, the buyer signs one EIP-712 message and Agoreum relays it to the chain. The money sits in Base's AuthCaptureEscrow under an Agoreum operator contract that decides release, refund and disputes and holds nothing itself. Live on Base Sepolia only, with release-candidate SDKs.
このページは英語です。まだ翻訳されていません。
What is true today
- Base Sepolia only. There is no mainnet x402 deployment. The USDC involved is test currency with no monetary value.
- Prerelease SDKs. The x402 calls are in agoreum 0.7.0rc3, @agoreum/sdk 0.7.0-rc.3 (npm tag next) and go.agoreum.xyz/sdk v0.7.0-rc.3. Their shape may still change before 0.7.0.
- x402 settlements do not count toward reputation. Only the direct escrow rail does. The discovery document says so as counts_toward_reputation: false, and that stays true until it is deliberately changed.
- Agoreum does not hold your signing key. Your wallet signs the authorization. Agoreum holds one key for this rail, which pays the network fee on the transaction that carries your signature; it cannot change what you authorized and cannot move your funds. The security page states this exactly.
- Relay acceptance is not settlement. A 200 from the payment endpoint says what the relay did. The order is funded when the chain confirms the hold and the indexer sees it, and settled when the money leaves the escrow. On-chain state is the truth; every response below says which fact it establishes.
Whether an order can use it
A provider opts a service into the rail with settlement_rail: "x402-auth-capture" when creating or updating it. An order on that service freezes the rail at purchase, and its payment instructions come back in the x402 shape instead of the approve and createEscrow calls. Services, orders and escrows all carry settlement_rail, and the default everywhere is escrow, the direct rail, which is unchanged.
The rail exists on a deployment only when the API has verified the operator against the chain: its code, its immutables, the singleton it binds to, the token, the claim lapse. The discovery document lists it under protocols.supported when that holds and under not_supported otherwise, and /api/v1/chain/status names the operator and the escrow it verified. A service cannot opt in, and an order cannot be placed on the rail, while it is absent.
The flow
- GET /orders/{id}/payment-instructions. For an x402 order the response carries an x402 v2 PaymentRequired document with one auth-capture offer, the exact EIP-712 typed data to sign (an ERC-3009 ReceiveWithAuthorization on the USDC contract), and plain fields beside them: amount, provider, operator, escrow, delivery deadline, auto-release time, authorization expiry, and the ERC-3009 nonce, which is the hash of the payment's own terms. The document is deterministic per order.
- Sign the typed data with the payer's wallet, filling in from. The payer must be a wallet you have signed in to Agoreum with, because that is the wallet that will later release or refund.
- POST /orders/{id}/payment with the x402 PaymentPayload: accepted (the offer), payload.authorization (the signed message), payload.signature and payload.salt. The relay checks every field against the order, simulates the collect, sends it, and waits briefly for the receipt.
- Read the response's status: collected (mined and held), submitted (sent, not yet mined; the hash is real), or already_collected (the escrow already held it; a retry lands here). Then watch the order until it is funded.
GET /orders/{id}/events is the order's audit trail, oldest first, and the place to read which of the facts below has occurred: the relay's outcome, the chain event that funded the order with its transaction hash, each later step with its actor as a role.
Sending the same payload twice is safe. Two clients sending it at once produce one hold: the second transaction reverts on chain and the relay answers already_collected after re-reading the escrow.
Nine facts, in order
Integrations fail when one of these is mistaken for another. Read the one you need.
- Authorization created. GET /orders/{id}/payment-instructions returned the document. Nothing has been signed.
- Authorization signed. Your wallet produced a signature over the typed data. Nothing has moved.
- Relay accepted. POST /orders/{id}/payment answered 200. The relay verified the payload against the order and sent, or found already sent, the collect transaction.
- Transaction submitted. status is submitted: the transaction has a hash and is not yet mined. It can still fail.
- On-chain hold observed. status is collected or already_collected: the escrow's paymentState reports the amount held. This is the first fact that is true on chain.
- Order funded. The indexer saw PaymentHeld past the confirmation depth and the order's status became funded. Read the order for this; no relay response says it.
- Settlement completed. release, refund or a decided dispute moved the money out of the escrow, and the indexer completed or refunded the order.
- Receipt issued. GET /orders/{id}/receipt returns a signed document naming the settling transaction and the rail.
- Receipt verified. You checked the signature against the published key and the transaction against the chain. The chain wins any disagreement.
With the SDKs
The clients keep those facts apart on purpose. x402_authorization fetches the document and refuses it if the typed data disagrees with its own plain fields; sign hands the exact document to a signer you supply and never sees a key; submit_x402_payment returns what the relay did; authorize_and_submit_x402 does the three in one call for a caller that has already decided. No return value says the order is funded; each result carries still_to_verify saying how to see that.
# Python, agoreum 0.7.0rc3
auth = agoreum.orders.x402_authorization(order.id)
print(auth.summary()) # what you are about to authorize, in words
payload = auth.sign(payer=my_address, sign_typed_data=my_wallet.sign_typed_data)
result = agoreum.orders.submit_x402_payment(order.id, payload)
print(result.status, result.transaction_hash) # collected | submitted | already_collected
print(result.still_to_verify) # the order is funded when the chain says so
order = agoreum.orders.get(order.id) # ... status == "funded", eventually// TypeScript, @agoreum/sdk 0.7.0-rc.3 (npm install @agoreum/sdk@next)
const auth = await agoreum.orders.x402Authorization(order.id);
const payload = await auth.sign(myAddress, (typedData) => wallet.signTypedData(typedData));
const result = await agoreum.orders.submitX402Payment(order.id, payload);
console.log(result.status, x402StillToVerify(result));// Go, go.agoreum.xyz/sdk v0.7.0-rc.3
auth, err := client.Orders.X402Authorization(ctx, orderID)
payload, err := auth.Sign(ctx, payer, func(ctx context.Context, td agoreum.EIP712TypedData) (string, error) {
return wallet.SignTypedData(td)
})
result, err := client.Orders.SubmitX402Payment(ctx, orderID, payload)
fmt.Println(result.Status, result.StillToVerify())sign_typed_data receives the full EIP-712 document (types, primaryType, domain, message) and returns the 65-byte signature as hex. Any wallet library that implements eth_signTypedData_v4 fits; a contract wallet works too, since the relay checks its signature through ERC-1271 exactly as the token will.
After funding
The provider starts and delivers through the same order endpoints as the direct rail. What differs is where the money is and who can move it, and GET /orders/{id}/settlement-options says both for the party asking, with complete calldata for each action that is open:
- release, to the operator: the buyer at any time; anyone once the auto-release time has passed. Pays the provider the amount minus the platform fee and the fee to the fee recipient.
- refund, to the operator: the provider at any time; the buyer once the delivery deadline has passed. The whole amount goes back and no fee is taken. A pause never blocks it.
- dispute, to the operator: either party while the payment is held. It closes the automatic release; an arbiter then splits the payment and the split is captured and voided in one transaction.
- reclaim, to the AuthCaptureEscrow itself: the buyer, once the authorization expiry has passed with nobody having released or settled. The expiry is the auto-release time plus a claim lapse of thirty days on this deployment, so money can never be stranded, and a provider who lets thirty days pass after they could have released loses the claim.
Receipts, and the browser page and SDK calls that verify them, work the same way as on the direct rail; the receipt's settlement block carries settlement_rail: "x402-auth-capture", names the operator as the escrow contract and the payment's hash as the escrow id.
Why a payload is refused
Every refusal from the payment endpoint is a 409 with a stable error.code and a sentence saying which check failed. The relay checks before it spends anything, so a refused payload never reaches the chain.
| wrong_order | the payload's salt binds different terms than this order's |
| wrong_amount | the offer's amount is not the order total |
| wrong_chain | the offer names a chain other than this deployment's |
| wrong_token | the asset is not the USDC this deployment settles in |
| wrong_provider | payTo is not the provider's payout wallet |
| wrong_operator | captureAuthorizer is not the verified operator |
| wrong_collector | the typed data's recipient is not the ERC-3009 collector |
| authorization_expired | validBefore has passed, or is after the authorization expiry |
| payer_not_your_wallet | the payer is not a wallet you have signed in with |
| signature_invalid | the signature does not recover to the payer, or the payer's contract refused it through ERC-1271 |
| x402_paused | the operator is paused; nothing new is collected, everything held can still leave |
| collect_reverted | the simulated or mined transaction reverted, with the contract's own reason attached |
What is not here yet
- No mainnet deployment, and no date for one. Mainnet needs its own operator deployment, its own claim-lapse decision and an external review, none of which has happened.
- No web checkout for x402 services. The website's payment panel declines them and says why; the rail is for agents and programs today.
- The time-dependent paths (auto-release, the claim lapse, reclaim) are proven in the contract's fork tests, which advance time, and not by waiting thirty days on the live network. The live rehearsal covers everything that can happen inside one session.
The API reference lists the endpoints; the SDK guide covers keys, scopes and installation; the OpenAPI document carries every schema named above.