batch-settlement scheme is for high-throughput payments where many requests are authorized individually and redeemed later in batches. The EVM implementation uses stateless unidirectional payment channels: a buyer deposits funds into onchain escrow once, then signs off-chain cumulative vouchers for each request. The seller verifies vouchers quickly and redeems them onchain later in batches.
Use batch-settlement for repeated paid API calls, usage-metered endpoints, and other workloads where many small payments would be too expensive or slow to settle one-by-one.
How It Works
- Deposit: the client opens or tops up a channel by depositing ERC-20 funds into escrow. Deposits use EIP-3009 or Permit2 and are submitted by the facilitator.
- Voucher: each paid request includes a signed cumulative voucher for the channel’s total claimable amount.
- Verify: the server verifies the voucher and serves the response without waiting for an onchain transfer.
- Claim: the server’s channel manager periodically claims the latest vouchers from many channels in one transaction.
- Settle: claimed funds are swept to the receiver in a separate settle transaction.
- Refund: idle channels can be cooperatively refunded after outstanding vouchers are claimed.
price is still a per-request maximum. For dynamic pricing, the server can charge less than that maximum with settlement overrides.
Server Setup
Register the scheme with a resource server, protect routes withscheme: "batch-settlement", configure server-side channel storage, and run a channel manager to claim, settle, and refund channels.
- TypeScript
- Go
- Python
Server Storage
Server storage keeps the latest voucher and channel session state that the channel manager uses to claim, settle, and refund later. In-memory storage is useful for local demos, but production servers should configure durable storage so outstanding vouchers survive restarts. Use file storage for single-process deployments. For serverless or multi-instance deployments, use Redis/Valkey storage so channel updates are shared atomically across processes. See the Next.js batch-settlement Redis example for a fullwithX402 setup with RedisChannelStorage and cron-based claim/settle routes.
Client Setup
RegisterBatchSettlementEvmScheme for eip155:*. The client SDK handles deposits, voucher signing, channel recovery, and corrective 402 resync.
- TypeScript
- Go
- Python
Deposit Policy
The deposit policy controls how much the client deposits when a channel needs funding or top-up.Server-announced minDeposit hint
Servers always include extra.minDeposit in their 402 response. The default is 10 × amount; servers can override it per route via accepts.extra.minDeposit (an atomic integer string or a money string like "$1.00" for default assets).
When the client receives a valid minDeposit hint (a positive integer >= amount), it uses that as the deposit target instead of depositMultiplier × amount. This lets servers guide clients toward a deposit size that reduces top-up frequency without requiring clients to configure a multiplier.
If a spend cap is set via spendControls.maxAmountPerPayment, the deposit is clamped to maxAmountPerPayment × depositMultiplier. Payments without a spend cap leave deposits uncapped.
Servers can opt in to rejecting deposits below the announced hint by setting enforceMinDeposit: true (default false). The facilitator never enforces this limit. When a deposit is rejected for being below the hint, the server returns invalid_batch_settlement_evm_deposit_below_min_deposit.
To set a per-route minDeposit override on the server:
- TypeScript
Custom deposit strategy
UsedepositStrategy for fine-grained control: cap deposits, skip top-ups, or return a custom amount. The callback receives the computed deposit amount (already incorporating the minDeposit hint) and can return a capped value, false to skip the deposit, or undefined to accept the computed amount.
- TypeScript
- Go
- Python
Voucher Signer Delegation
By default, vouchers are signed by the payer key. For higher-throughput clients, especially smart wallets using EIP-1271, delegate voucher signing to a dedicated EOA. The delegated address is committed into the channel aspayerAuthorizer, which lets the facilitator verify vouchers with ECDSA recovery instead of an onchain smart-wallet signature check.
- TypeScript
- Go
- Python
Client Persistence and Refunds
Client channel state is stored in memory by default. Persistent client storage is optional because the SDK can recover channel state through corrective 402 responses and onchain state on the next paid request. Long-lived clients can still persist state to avoid that recovery round trip after restarts.- TypeScript
- Go
- Python
Receiver Authorizer
Every channel commits to areceiverAuthorizer, which signs claim and refund authorizations. The server must have a receiverAuthorizer available from one of two sources:
The server validates this configuration at startup. If no
receiverAuthorizerSigner is configured and the facilitator does not advertise a valid receiverAuthorizer in its /supported response, the server will throw an error during initialize() rather than failing on the first protected request.
On the facilitator side, the authorizerSigner passed to BatchSettlementEvmScheme is optional. When provided, the facilitator advertises its address as receiverAuthorizer in /supported and signs missing authorizer signatures. When omitted, no receiverAuthorizer is advertised and servers must supply their own receiverAuthorizerSigner.
Facilitator Setup
The batch-settlement facilitator scheme accepts an optionalauthorizerSigner. Pass one to enable facilitator-delegated authorization; omit it to require servers to supply their own.
- Go
- Python
Settlement Policy
Choose claim, settle, and refund intervals based on throughput and gas cost:- Claim often enough that outstanding vouchers are claimed before a payer’s timed withdrawal can finalize after
withdrawDelay. - Settle less often when gas savings matter more than cash-flow latency.
- Refund idle channels to return unclaimed balance to payers.
withdrawDelay greater than your claim cadence plus an operational safety margin.
Examples
- TypeScript client
- TypeScript server
- Next.js server + Redis storage
- Go client
- Go server
- Go facilitator
- Python client
- Python server
- Python facilitator