Prerequisites
A wallet with ETH on Base Sepolia for gas (faucet )
Node.js 18+ and npm
Operator and escrow addresses from the Merchant Setup
1. Install Dependencies
npm install @x402r/sdk @x402r/helpers
2. Create the Arbiter Client
import { createPublicClient , createWalletClient , http } from 'viem'
import { baseSepolia } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts'
import { createArbiterClient } from '@x402r/sdk'
import { fromNetworkId } from '@x402r/core'
const account = privateKeyToAccount ( process . env . ARBITER_PRIVATE_KEY as `0x ${ string } ` )
const arbiter = createArbiterClient ({
publicClient: createPublicClient ({ chain: baseSepolia , transport: http () }),
walletClient: createWalletClient ({
account ,
chain: baseSepolia ,
transport: http (),
}),
operatorAddress: process . env . OPERATOR_ADDRESS as `0x ${ string } ` ,
escrowPeriodAddress: process . env . ESCROW_PERIOD_ADDRESS as `0x ${ string } ` ,
})
3. Handle the Verify Endpoint
The merchant’s forwardToArbiter() hook POSTs to /verify. Use parseForwardedPayload() to extract typed PaymentInfo with BigInt fields restored:
import { parseForwardedPayload } from '@x402r/helpers'
import express from 'express'
const app = express ()
app . use ( express . json ())
app . post ( '/verify' , async ( req , res ) => {
const { responseBody , paymentInfo , network , transaction } =
parseForwardedPayload ( req . body )
const chainId = fromNetworkId ( network ) // "eip155:84532" -> 84532
// Your evaluation logic
const passed = await evaluate ( responseBody )
if ( passed ) {
const amounts = await arbiter . payment . getAmounts ( paymentInfo )
await arbiter . payment . release ( paymentInfo , amounts . capturableAmount )
res . json ({ verdict: 'PASS' })
} else {
// Do nothing. Funds auto-refund after escrow expires.
res . json ({ verdict: 'FAIL' })
}
})
app . listen ( 3001 )
4. Implement Your Evaluation Logic
The evaluate() function is where your logic lives. It could be:
Heuristic checks: HTTP status code, response size, content-type validation
AI evaluation: send response body to an LLM and ask “is this a valid response?”
Schema validation: check if the response matches an expected JSON schema
async function evaluate ( responseBody : string ) : Promise < boolean > {
// Example: reject empty or error responses
if ( ! responseBody || responseBody . length < 10 ) return false
if ( responseBody . includes ( '"error"' )) return false
// Example: LLM evaluation
// const result = await llm.evaluate(responseBody)
// return result.verdict === 'PASS'
return true
}
5. What Happens on Failure
If your service goes down, no payments get evaluated and funds stay in escrow until timeout. The escrow period protects payers, but add uptime monitoring and alerting.
Next Steps
Merchant Setup Deploy the operator and configure forwardToArbiter().
Dispute Resolution For human-reviewed disputes instead of automated evaluation.
Examples Runnable examples for every SDK operation.