> ## Documentation Index
> Fetch the complete documentation index at: https://docs.x402r.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Arbiter Setup

> Build a service that evaluates responses and releases funds.

### Prerequisites

* A wallet with ETH on Base Sepolia for gas ([faucet](https://www.alchemy.com/faucets/base-sepolia))
* Node.js 18+ and npm
* Operator and escrow addresses from the [Merchant Setup](/sdk/delivery-merchant)

<Note>
  The [AI garbage detector example](https://github.com/BackTrackCo/arbiter-examples) implements this pattern end-to-end with heuristic plus LLM evaluation.
</Note>

<Steps>
  <Step title="Install dependencies">
    <CodeGroup>
      ```bash npm theme={null}
      npm install @x402r/sdk @x402r/helpers
      ```

      ```bash pnpm theme={null}
      pnpm add @x402r/sdk @x402r/helpers
      ```

      ```bash bun theme={null}
      bun add @x402r/sdk @x402r/helpers
      ```
    </CodeGroup>
  </Step>

  <Step title="Create the arbiter client">
    The role-narrowed `createArbiterClient` exposes `payment.voidPayment`, `payment.getState`, and `payment.getAmounts`. Capturing requires the full surface, so use `createX402r()` directly:

    ```typescript theme={null}
    import { createPublicClient, createWalletClient, http } from 'viem'
    import { baseSepolia } from 'viem/chains'
    import { privateKeyToAccount } from 'viem/accounts'
    import { createX402r } from '@x402r/sdk'

    const account = privateKeyToAccount(process.env.ARBITER_PRIVATE_KEY as `0x${string}`)

    const arbiter = createX402r({
      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}`,
    })
    ```
  </Step>

  <Step title="Handle the verify endpoint">
    The merchant's `forwardToArbiter()` hook POSTs `{ responseBody, transaction, paymentInfoWire }` to `/verify`. The `paymentInfoWire` is the JSON-safe wire form of `PaymentInfo`; call `PaymentInfo.fromWire(...)` to recover the `bigint`-typed struct expected by SDK actions.

    ```typescript theme={null}
    import { PaymentInfo } from '@x402r/sdk'
    import express from 'express'

    const app = express()
    app.use(express.json())

    app.post('/verify', async (req, res) => {
      const { responseBody, transaction, paymentInfoWire } = req.body

      if (!paymentInfoWire) {
        res.status(400).json({ error: 'missing_payment_info' })
        return
      }

      const paymentInfo = PaymentInfo.fromWire(paymentInfoWire)

      const passed = await evaluate(responseBody)

      if (passed) {
        const amounts = await arbiter.payment.getAmounts(paymentInfo)
        await arbiter.payment.capture(paymentInfo, amounts.capturableAmount)
        res.json({ verdict: 'PASS' })
      } else {
        // Arbiter can refund immediately without waiting for escrow expiry.
        await arbiter.payment.voidPayment(paymentInfo)
        res.json({ verdict: 'FAIL' })
      }
    })

    app.listen(3001)
    ```
  </Step>

  <Step title="Implement your evaluation logic">
    The `evaluate()` function is where your logic lives. It can run:

    * **Heuristic checks**: HTTP status code, response size, content-type validation
    * **AI judgment**: send response body to an LLM and ask "is this a valid response?"
    * **Schema validation**: check if the response matches an expected JSON schema

    ```typescript theme={null}
    async function evaluate(responseBody: string): Promise<boolean> {
      // Reject empty or error responses
      if (!responseBody || responseBody.length < 10) return false
      if (responseBody.includes('"error"')) return false

      // LLM evaluation
      // const result = await llm.evaluate(responseBody)
      // return result.verdict === 'PASS'

      return true
    }
    ```
  </Step>

  <Step title="What happens on failure">
    With delivery protection, the arbiter can call `voidPayment()` immediately on a FAIL verdict. You do not need to wait for escrow expiry. The receiver (merchant) can also trigger a voluntary refund at any time.

    <Warning>
      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.
    </Warning>
  </Step>
</Steps>

## Next Steps

<CardGroup cols={2}>
  <Card title="Merchant Setup" icon="store" href="/sdk/delivery-merchant">
    Deploy the operator and configure forwardToArbiter().
  </Card>

  <Card title="Examples" icon="code" href="/sdk/examples">
    Runnable examples for every SDK operation.
  </Card>
</CardGroup>
