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

# Integration Guide

> How to integrate x402Escrow as a facilitator (service operator) or client (agent/payer).

<Badge color="gray">x402Escrow</Badge>

## Architecture Overview

```
+---------------+       HTTP 402        +---------------+
|    Client /   |<--------------------->| Facilitator / |
|    AI Agent   |    X-PAYMENT header   | Service API   |
+-------+-------+                       +-------+-------+
       |                                        |
       | signs EIP-3009                         | calls settle() 
       | authorization                          | / release()
       |                                        |
       |            +--------------+            |
       +----------->|  x402Escrow  |<-----------+
                    |  (on-chain)  |
                    +------+-------+
                           |
                    +------+-------+
                    |  USDC Token  |
                    |  (EIP-3009)  |
                    +--------------+
```

## Facilitator Integration

The facilitator is the service operator — the side that accepts payment and delivers work.

### 1. Receive the Client's Payment Authorization

In the x402 flow, the client sends an HTTP request with an `X-PAYMENT` header containing the signed EIP-3009 authorization. Parse the header to extract:

```typescript theme={null}
interface PaymentAuthorization {
  client: string;       // client wallet address
  maxAmount: bigint;    // max USDC (6 decimals)
  validAfter: bigint;   // unix timestamp
  validBefore: bigint;  // unix timestamp
  nonce: string;        // bytes32
  v: number;
  r: string;
  s: string;
}
```

### 2. Lock Funds with `settle()`

Call `settle()` to pull USDC from the client into escrow:

```typescript theme={null}
import { ethers } from "ethers";

const escrow = new ethers.Contract(ESCROW_ADDRESS, X402_ESCROW_ABI, facilitatorSigner);

const tx = await escrow.settle(
  auth.client,
  auth.maxAmount,
  auth.validAfter,
  auth.validBefore,
  auth.nonce,
  auth.v,
  auth.r,
  auth.s
);

const receipt = await tx.wait();
const depositedLog = receipt.logs.find(
  (log) => log.address.toLowerCase() === ESCROW_ADDRESS.toLowerCase()
);
const escrowId = escrow.interface.parseLog(depositedLog).args.escrowId;
```

Store `escrowId` — you'll need it for release.

### 3. Execute the Service

Perform the metered work (AI inference, API calls, data processing, etc.) and track the actual cost.

### 4. Release with Actual Cost

```typescript theme={null}
const actualCost = ethers.parseUnits("2.50", 6); // actual USDC cost

const tx = await escrow.release(escrowId, actualCost);
await tx.wait();
// facilitator receives actualCost
// client receives (maxAmount - actualCost) automatically
```

If the service failed or no cost was incurred, release with zero:

```typescript theme={null}
await escrow.release(escrowId, 0); // full refund to client
```

### Error Handling

| Scenario                                   | Action                                                                               |
| ------------------------------------------ | ------------------------------------------------------------------------------------ |
| `settle()` reverts with `TransferMismatch` | The client's USDC balance may be insufficient, or a non-standard token is in use.    |
| `settle()` reverts with `TimeoutExpired`   | Authorization expired before you submitted it. Ask the client for a fresh signature. |
| `release()` reverts with `EscrowNotFound`  | Escrow was already released or refunded (timeout). Check `getEscrow()` first.        |
| Service crashes mid-execution              | You have until `refundAt` to call `release()`. Monitor active escrows.               |

## Client Integration

The client is the payer — typically an AI agent or application that consumes a metered service.

### 1. Sign the EIP-3009 Authorization

Construct and sign a `ReceiveWithAuthorization` message:

```typescript theme={null}
import { ethers } from "ethers";

const domain = {
  name: "USD Coin",              // USDC token name
  version: "2",                  // USDC token version
  chainId: 8453,                 // Base
  verifyingContract: USDC_ADDRESS
};

const types = {
  ReceiveWithAuthorization: [
    { name: "from", type: "address" },
    { name: "to", type: "address" },
    { name: "value", type: "uint256" },
    { name: "validAfter", type: "uint256" },
    { name: "validBefore", type: "uint256" },
    { name: "nonce", type: "bytes32" }
  ]
};

const message = {
  from: clientAddress,
  to: ESCROW_ADDRESS,          // must be the escrow contract
  value: ethers.parseUnits("10.00", 6),  // max USDC to lock
  validAfter: 0,               // valid immediately
  validBefore: Math.floor(Date.now() / 1000) + 300, // expires in 5 min
  nonce: ethers.hexlify(ethers.randomBytes(32))
};

const signature = await clientSigner.signTypedData(domain, types, message);
const { v, r, s } = ethers.Signature.from(signature);
```

<Note>
  The `to` field must be the escrow contract address. This prevents anyone else from using your signature.
</Note>

### 2. Send with the HTTP Request

Include the authorization in the `X-PAYMENT` header per the x402 protocol:

```typescript theme={null}
const response = await fetch("https://api.example.com/inference", {
  method: "POST",
  headers: {
    "X-PAYMENT": JSON.stringify({
      client: clientAddress,
      maxAmount: message.value.toString(),
      validAfter: message.validAfter.toString(),
      validBefore: message.validBefore.toString(),
      nonce: message.nonce,
      v, r: r, s: s
    }),
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ prompt: "..." })
});
```

### 3. Monitor the Escrow (Optional)

Check the escrow status on-chain:

```typescript theme={null}
const escrowId = ethers.keccak256(
  ethers.solidityPacked(["address", "bytes32"], [clientAddress, message.nonce])
);

const view = await escrow.getEscrow(escrowId);
console.log({
  amount: view.amount,
  canRefund: view.canRefund,
  timeUntilRefund: view.timeUntilRefund
});
```

### 4. Claim Timeout Refund (If Needed)

If the escrow was settled but the facilitator never called `release()`, claim a refund after the timeout:

```typescript theme={null}
if (view.canRefund) {
  await escrow.refundAfterTimeout(escrowId);
  // full amount returned to client
}
```

This is permissionless — any address can trigger it, and funds always go to the original client.

## Escrow ID Computation

The escrow ID is deterministic and can be computed off-chain before the transaction:

```
escrowId = keccak256(abi.encodePacked(client, nonce))
```

In ethers.js:

```typescript theme={null}
const escrowId = ethers.keccak256(
  ethers.solidityPacked(["address", "bytes32"], [clientAddress, nonce])
);
```

In Python (web3.py):

```python theme={null}
from web3 import Web3

escrow_id = Web3.keccak(
    Web3.to_bytes(hexstr=client_address) + nonce_bytes
)
```

## Event Monitoring

Subscribe to escrow events for off-chain tracking:

```typescript theme={null}
escrow.on("Deposited", (escrowId, client, amount) => {
  console.log(`Escrow ${escrowId}: ${amount} USDC locked by ${client}`);
});

escrow.on("Released", (escrowId, facilitator, toFacilitator, toClient) => {
  console.log(`Escrow ${escrowId}: ${toFacilitator} to facilitator, ${toClient} refunded`);
});

escrow.on("Refunded", (escrowId, client, amount) => {
  console.log(`Escrow ${escrowId}: ${amount} USDC refunded to ${client}`);
});
```
