> For the complete documentation index, see [llms.txt](https://docs.greenowl.money/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.greenowl.money/x402-payments/x402/facilitator_express.md).

# facilitator-express

The **x402 protocol** is an HTTP payment standard that enables AI agents to pay for API access using on-chain settlements. The protocol allows services to require upfront payment verification before delivering protected resources, with cryptographic proof of payment intent settled on EVM and SVM chains.

The Owl Smart Wallet ecosystem uses x402 to enable autonomous agents to access paid APIs and services without manual payment flows.

***

## Overview

The x402 protocol operates in three phases:

1. **Discovery** — Client queries `/supported` to learn accepted payment methods
2. **Verification** — Client submits signed payment proof to `/verify` for validation
3. **Settlement** — Facilitator broadcasts the payment transaction on-chain via `/settle`

The **facilitator-express** reference implementation is an Express.js service that validates payment signatures and settles transactions on supported networks.

### Use Cases

* **Paid API access** — AI agents authenticate via payment instead of API keys
* **Micropayments** — Per-request pricing for LLM inference, weather data, etc.
* **Cross-chain payments** — Unified payment interface across EVM and Solana
* **Gas abstraction** — Facilitator handles gas fees and transaction broadcasting

***

## Architecture

```
┌─────────────┐                  ┌──────────────┐                  ┌─────────────┐
│  AI Agent   │───── HTTP ──────▶│ Facilitator  │───── RPC ───────▶│   Chain     │
│ (MCP Tool)  │                  │  (Express)   │                  │ (EVM/SVM)   │
└─────────────┘                  └──────────────┘                  └─────────────┘
      │                                  │                                  │
      │ 1. GET /supported               │                                  │
      │◀────────────────────────────────│                                  │
      │                                  │                                  │
      │ 2. POST /verify                 │                                  │
      │─────────────────────────────────▶│                                  │
      │    (signed payment proof)        │                                  │
      │                                  │                                  │
      │ 3. POST /settle                 │                                  │
      │─────────────────────────────────▶│                                  │
      │                                  │  4. Broadcast tx                │
      │                                  │─────────────────────────────────▶│
      │                                  │                                  │
      │◀────── Protected Resource ───────│                                  │
```

**Components:**

* **AI Agent** — Owl Smart Wallet-based agent using MCP tools to make paid API calls
* **Facilitator** — Express.js service that verifies signatures and settles payments
* **Chain** — Target blockchain (EVM or Solana) where payment is settled

***

## Installation

### Prerequisites

* Node.js v20+
* pnpm v10
* Private keys with gas tokens:
  * EVM: Base Sepolia ETH for transaction fees
  * SVM: Solana Devnet SOL for transaction fees

### Setup

```bash
# Clone repository
git clone https://github.com/your-org/facilitator-express
cd facilitator-express

# Install dependencies
pnpm install

# Copy environment template
cp .env.example .env
```

### Environment Configuration

Edit `.env`:

| Variable               | Description                          | Example                                                |
| ---------------------- | ------------------------------------ | ------------------------------------------------------ |
| `PORT`                 | HTTP server port                     | `4022`                                                 |
| `GOOGLE_CLOUD_PROJECT` | GCP project ID (production only)     | `owl-wallet-prod`                                      |
| `ENABLED_CHAINS`       | Comma-separated CAIP-2 chain IDs     | `eip155:84532,solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` |
| `USDL_ADDRESS`         | USDL token contract address          | `0x036CbD53842c5426634e7929541eC2318f3dCF7e`           |
| `EVM_PRIVATE_KEY`      | EVM signer private key (dev only)    | `0x...`                                                |
| `SVM_PRIVATE_KEY`      | Solana signer private key (dev only) | `...`                                                  |

### Development

```bash
# Start development server
pnpm dev

# Build for production
pnpm build

# Start production server
pnpm start
```

***

## Production Deployment (GCP)

In production, the facilitator retrieves secrets from **Google Cloud Secret Manager** instead of environment variables. This prevents private key exposure in logs or configuration files.

### Required Secrets

| Secret Name                 | Description                                 |
| --------------------------- | ------------------------------------------- |
| `x402-facilitator-evm-key`  | Private key for signing EVM transactions    |
| `x402-facilitator-svm-key`  | Private key for signing Solana transactions |
| `x402-facilitator-api-keys` | Comma-separated API keys for authentication |

### Setup Secrets

```bash
# Enable Secret Manager API
gcloud services enable secretmanager.googleapis.com

# Create EVM private key secret
echo -n "0xYOUR_PRIVATE_KEY" | gcloud secrets create x402-facilitator-evm-key \
  --data-file=- --replication-policy="automatic"

# Create SVM private key secret
echo -n "YOUR_SOLANA_PRIVATE_KEY" | gcloud secrets create x402-facilitator-svm-key \
  --data-file=- --replication-policy="automatic"

# Create API keys secret
echo -n "api-key-1,api-key-2,api-key-3" | gcloud secrets create x402-facilitator-api-keys \
  --data-file=- --replication-policy="automatic"
```

### Grant Service Account Access

```bash
# Grant Cloud Run service account access to secrets
gcloud secrets add-iam-policy-binding x402-facilitator-evm-key \
  --member="serviceAccount:YOUR_SA@PROJECT.iam.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"

gcloud secrets add-iam-policy-binding x402-facilitator-svm-key \
  --member="serviceAccount:YOUR_SA@PROJECT.iam.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"

gcloud secrets add-iam-policy-binding x402-facilitator-api-keys \
  --member="serviceAccount:YOUR_SA@PROJECT.iam.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"
```

**Security Note:** Private keys are NEVER stored in environment variables in production. All secrets are retrieved at runtime from Secret Manager with audit logging enabled.

***

## API Reference

### GET /supported

Returns the payment schemes and networks this facilitator supports.

**Response:**

```json
{
  "kinds": [
    {
      "x402Version": 2,
      "scheme": "exact",
      "network": "eip155:84532"
    },
    {
      "x402Version": 2,
      "scheme": "exact",
      "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
      "extra": {
        "feePayer": "AbC...xyz"
      }
    }
  ],
  "extensions": [],
  "signers": {
    "eip155": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"],
    "solana": ["AbC...xyz"]
  }
}
```

**Fields:**

| Field     | Description                                             |
| --------- | ------------------------------------------------------- |
| `kinds`   | Array of supported payment schemes                      |
| `scheme`  | Payment scheme type (`exact` for fixed-amount payments) |
| `network` | CAIP-2 network identifier                               |
| `extra`   | Network-specific metadata (e.g., Solana fee payer)      |
| `signers` | Public addresses that will sign settlement transactions |

***

### POST /verify

Verifies a payment payload against requirements before settlement. Does NOT broadcast a transaction.

**Request:**

```json
{
  "paymentPayload": {
    "x402Version": 2,
    "resource": {
      "url": "http://localhost:4021/weather",
      "description": "Weather data API",
      "mimeType": "application/json"
    },
    "accepted": {
      "scheme": "exact",
      "network": "eip155:84532",
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
      "amount": "1000",
      "payTo": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "USDC",
        "version": "2"
      }
    },
    "payload": {
      "signature": "0x1234567890abcdef...",
      "authorization": {
        "tokenAddress": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
        "amount": "1000",
        "deadline": 1234567890,
        "nonce": 42,
        "v": 27,
        "r": "0x...",
        "s": "0x..."
      }
    }
  },
  "paymentRequirements": {
    "scheme": "exact",
    "network": "eip155:84532",
    "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "amount": "1000",
    "payTo": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "maxTimeoutSeconds": 300
  }
}
```

**Response (Success):**

```json
{
  "isValid": true,
  "payer": "0xABCDEF1234567890..."
}
```

**Response (Failure):**

```json
{
  "isValid": false,
  "invalidReason": "invalid_signature"
}
```

**Validation Checks:**

* Signature is valid and signed by payer
* Payment scheme matches requirements
* Network matches requirements
* Asset address matches requirements
* Amount meets or exceeds requirements
* Timeout is within acceptable range
* Authorization nonce is not already used

***

### POST /settle

Settles a verified payment by broadcasting the transaction on-chain.

**Request:**

Same as `/verify` endpoint.

**Response (Success):**

```json
{
  "success": true,
  "transaction": "0x1234567890abcdef...",
  "network": "eip155:84532",
  "payer": "0xABCDEF1234567890..."
}
```

**Response (Failure):**

```json
{
  "success": false,
  "errorReason": "insufficient_balance",
  "transaction": "",
  "network": "eip155:84532"
}
```

**Error Reasons:**

| Reason                   | Description                                |
| ------------------------ | ------------------------------------------ |
| `invalid_signature`      | Payment signature is invalid               |
| `insufficient_balance`   | Payer has insufficient token balance       |
| `insufficient_allowance` | Token allowance not granted to facilitator |
| `transaction_failed`     | On-chain transaction reverted              |
| `network_error`          | RPC endpoint unreachable                   |
| `nonce_used`             | Payment nonce already consumed             |

***

## Network Support

Networks are identified using [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) format: `<namespace>:<reference>`

### EVM Networks

| Network          | CAIP-2 Identifier | Description                 |
| ---------------- | ----------------- | --------------------------- |
| Base Sepolia     | `eip155:84532`    | Base testnet                |
| Base Mainnet     | `eip155:8453`     | Base production network     |
| Ethereum Sepolia | `eip155:11155111` | Ethereum testnet            |
| Ethereum Mainnet | `eip155:1`        | Ethereum production network |

### Solana Networks

| Network        | CAIP-2 Identifier                         | Description               |
| -------------- | ----------------------------------------- | ------------------------- |
| Solana Devnet  | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` | Solana testnet            |
| Solana Mainnet | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | Solana production network |

***

## Extension Guide

### Adding New Networks

Register additional networks by extending the facilitator configuration:

```typescript
import { x402Facilitator } from "@x402/facilitator";
import { registerExactEvmScheme } from "@x402/evm/exact/facilitator";
import { registerExactSvmScheme } from "@x402/svm/exact/facilitator";

const facilitator = new x402Facilitator();

// Register EVM networks
registerExactEvmScheme(facilitator, {
  signer: evmSigner,
  networks: ["eip155:84532", "eip155:8453"],
});

// Register Solana networks
registerExactSvmScheme(facilitator, {
  signer: svmSigner,
  networks: ["solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"],
});
```

### Lifecycle Hooks

Add custom logic before and after verification and settlement operations:

```typescript
const facilitator = new x402Facilitator()
  .onBeforeVerify(async (context) => {
    console.log(`Verifying payment from ${context.payer}`);
    // Add custom validation logic
  })
  .onAfterVerify(async (context) => {
    // Log verified payment to analytics
    await analytics.track("payment_verified", {
      payer: context.payer,
      amount: context.amount,
    });
  })
  .onVerifyFailure(async (context) => {
    // Alert on verification failures
    await alerting.notify(`Verification failed: ${context.reason}`);
  })
  .onBeforeSettle(async (context) => {
    // Check rate limits before settlement
    const isAllowed = await rateLimiter.check(context.payer);
    if (!isAllowed) {
      return { abort: true, reason: "rate_limit_exceeded" };
    }
  })
  .onAfterSettle(async (context) => {
    // Record settlement in database
    await db.settlements.create({
      txHash: context.transaction,
      payer: context.payer,
      amount: context.amount,
      timestamp: Date.now(),
    });
  })
  .onSettleFailure(async (context) => {
    // Retry failed settlements
    await retryQueue.add({
      payment: context.payment,
      error: context.error,
    });
  });
```

### Custom Payment Schemes

Implement custom payment schemes beyond `exact`:

```typescript
import { PaymentScheme } from "@x402/facilitator";

class SubscriptionScheme implements PaymentScheme {
  async verify(payload: PaymentPayload, requirements: PaymentRequirements) {
    // Verify subscription is active
    const subscription = await db.subscriptions.findOne({
      payer: payload.payer,
      status: "active",
    });
    
    if (!subscription) {
      return { isValid: false, invalidReason: "no_active_subscription" };
    }
    
    return { isValid: true, payer: payload.payer };
  }
  
  async settle(payload: PaymentPayload) {
    // Extend subscription period instead of charging
    await db.subscriptions.update({
      payer: payload.payer,
      expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
    });
    
    return {
      success: true,
      transaction: "",
      network: payload.network,
      payer: payload.payer,
    };
  }
}

facilitator.registerScheme("subscription", new SubscriptionScheme());
```

***

## Integration with Owl Smart Wallet

AI agents using Owl Smart Wallet interact with x402 facilitators through MCP tools. The typical flow:

1. **Agent discovers payment requirements** via `GET /supported`
2. **Agent signs payment intent** using wallet's session key or owner key
3. **Agent submits payment** to `/verify` to validate signature
4. **Facilitator settles payment** on-chain via `/settle`
5. **Service delivers protected resource** to agent

### Example MCP Tool Flow

```typescript
// MCP tool: pay_for_api_access
async function payForApiAccess(url: string, amount: string) {
  // 1. Get payment requirements
  const facilitator = "https://facilitator.owl.build";
  const supported = await fetch(`${facilitator}/supported`).then(r => r.json());
  
  // 2. Sign payment with wallet
  const payment = await wallet.signPayment({
    resource: { url, description: "API access" },
    amount,
    network: "eip155:84532",
    asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
  });
  
  // 3. Verify payment
  const verification = await fetch(`${facilitator}/verify`, {
    method: "POST",
    body: JSON.stringify({ paymentPayload: payment }),
  }).then(r => r.json());
  
  if (!verification.isValid) {
    throw new Error(`Payment verification failed: ${verification.invalidReason}`);
  }
  
  // 4. Settle payment
  const settlement = await fetch(`${facilitator}/settle`, {
    method: "POST",
    body: JSON.stringify({ paymentPayload: payment }),
  }).then(r => r.json());
  
  if (!settlement.success) {
    throw new Error(`Settlement failed: ${settlement.errorReason}`);
  }
  
  // 5. Access protected resource
  const resource = await fetch(url, {
    headers: { "X-Payment-Tx": settlement.transaction },
  }).then(r => r.json());
  
  return resource;
}
```

This enables fully autonomous AI agents to pay for API access without manual approval flows.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.greenowl.money/x402-payments/x402/facilitator_express.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
