Examples

Code examples for integrating AI agents with the x402 Payment Gateway.

JavaScript/TypeScript

Basic AI Agent Client

import axios from 'axios';
import { ethers } from 'ethers';

class X402Client {
  private baseURL = 'https://x402.serendb.com';
  private wallet: ethers.Wallet;
  private providerId: string;

  constructor(privateKey: string, providerId: string) {
    this.wallet = new ethers.Wallet(privateKey);
    this.providerId = providerId;
  }

  // Check balance
  async getBalance(): Promise<string> {
    const response = await axios.get(
      `${this.baseURL}/api/balance/${this.wallet.address}/${this.providerId}`
    );
    return response.data.balance;
  }

  // Deposit USDC
  async deposit(amount: string, txHash: string): Promise<void> {
    await axios.post(`${this.baseURL}/api/deposit`, {
      txHash,
      agentWallet: this.wallet.address,
      providerId: this.providerId
    });
  }

  // Execute query with automatic balance check
  async query(sql: string): Promise<any> {
    try {
      const response = await axios.post(`${this.baseURL}/api/query`, {
        sql,
        agentWallet: this.wallet.address,
        providerId: this.providerId
      });

      console.log(`Query cost: ${response.data.actualCost} USDC`);
      console.log(`Execution time: ${response.data.executionTime}`);

      return response.data.rows;
    } catch (error: any) {
      if (error.response?.status === 402) {
        throw new Error('Insufficient balance. Please deposit USDC.');
      }
      throw error;
    }
  }

  // Get transaction history
  async getTransactions(limit = 50): Promise<any[]> {
    const response = await axios.get(
      `${this.baseURL}/api/transactions/${this.wallet.address}`,
      { params: { providerId: this.providerId, limit } }
    );
    return response.data.transactions;
  }
}

// Usage
async function main() {
  const client = new X402Client(
    process.env.WALLET_PRIVATE_KEY!,
    '945349e6-6d49-4c07-9257-2f2839e07173'
  );

  // Check balance
  const balance = await client.getBalance();
  console.log(`Current balance: ${balance} USDC`);

  // Execute query
  const users = await client.query('SELECT * FROM users LIMIT 10');
  console.log(`Retrieved ${users.length} users`);

  // View spending history
  const transactions = await client.getTransactions();
  console.log(`Transaction history: ${transactions.length} transactions`);
}

main();

Python

AI Agent with Automatic Recharge

import os
import requests
from web3 import Web3
from eth_account import Account

class X402Client:
    def __init__(self, private_key: str, provider_id: str):
        self.base_url = 'https://x402.serendb.com'
        self.account = Account.from_key(private_key)
        self.provider_id = provider_id
        self.min_balance = 10.0  # Auto-recharge threshold

    def get_balance(self) -> float:
        """Get current USDC balance"""
        response = requests.get(
            f'{self.base_url}/api/balance/{self.account.address}/{self.provider_id}'
        )
        response.raise_for_status()
        return float(response.json()['balance'])

    def deposit(self, tx_hash: str) -> dict:
        """Verify and credit a deposit"""
        response = requests.post(
            f'{self.base_url}/api/deposit',
            json={
                'txHash': tx_hash,
                'agentWallet': self.account.address,
                'providerId': self.provider_id
            }
        )
        response.raise_for_status()
        return response.json()

    def query(self, sql: str, auto_recharge: bool = True) -> list:
        """Execute SQL query with optional auto-recharge"""
        # Check balance first
        balance = self.get_balance()
        if balance < self.min_balance and auto_recharge:
            print(f'Balance low ({balance} USDC), triggering recharge...')
            self._auto_recharge()

        # Execute query
        response = requests.post(
            f'{self.base_url}/api/query',
            json={
                'sql': sql,
                'agentWallet': self.account.address,
                'providerId': self.provider_id
            }
        )

        if response.status_code == 402:
            raise Exception('Insufficient balance. Deposit more USDC.')

        response.raise_for_status()
        data = response.json()

        print(f"Query cost: {data['actualCost']} USDC")
        print(f"Rows returned: {data['rowCount']}")
        print(f"Execution time: {data['executionTime']}")

        return data['rows']

    def _auto_recharge(self):
        """Automatically deposit USDC when balance is low"""
        # This would integrate with Base network to send USDC
        # For demo purposes, we'll raise an error
        raise NotImplementedError(
            'Auto-recharge requires Base network integration'
        )

    def get_transactions(self, limit: int = 50) -> list:
        """Get transaction history"""
        response = requests.get(
            f'{self.base_url}/api/transactions/{self.account.address}',
            params={'providerId': self.provider_id, 'limit': limit}
        )
        response.raise_for_status()
        return response.json()['transactions']

# Usage
def main():
    client = X402Client(
        private_key=os.environ['WALLET_PRIVATE_KEY'],
        provider_id='945349e6-6d49-4c07-9257-2f2839e07173'
    )

    # Execute queries
    users = client.query('SELECT * FROM users WHERE active = true LIMIT 100')
    print(f'Found {len(users)} active users')

    # Check spending
    transactions = client.get_transactions(limit=10)
    total_spent = sum(
        float(tx['amount'])
        for tx in transactions
        if tx['type'] == 'query'
    )
    print(f'Total spent: ${abs(total_spent)} USDC')

if __name__ == '__main__':
    main()

Provider Setup Script

Automated Provider Registration

import axios from 'axios';
import * as fs from 'fs';

interface ProviderConfig {
  name: string;
  email: string;
  walletAddress: string;
  connectionString: string;
  pricing: {
    basePricePer1000Rows: number;
    markupMultiplier: number;
  };
}

async function setupProvider(config: ProviderConfig) {
  const baseURL = 'https://x402.serendb.com';

  console.log('Step 1: Registering provider...');
  const registerResponse = await axios.post(
    `${baseURL}/api/providers/register`,
    {
      name: config.name,
      email: config.email,
      walletAddress: config.walletAddress,
      connectionString: config.connectionString
    }
  );

  const { provider, apiKey } = registerResponse.data;
  console.log(`✓ Provider registered: ${provider.id}`);
  console.log(`✓ API Key: ${apiKey}`);

  // Save API key securely
  fs.writeFileSync('.env.local', `X402_API_KEY=${apiKey}\n`, { flag: 'a' });

  console.log('\nStep 2: Configuring pricing...');
  const pricingResponse = await axios.post(
    `${baseURL}/api/providers/${provider.id}/pricing`,
    config.pricing,
    { headers: { 'X-API-Key': apiKey } }
  );

  console.log(`✓ Pricing configured:`);
  console.log(`  - Base: $${config.pricing.basePricePer1000Rows} per 1000 rows`);
  console.log(`  - Markup: ${config.pricing.markupMultiplier}x`);
  console.log(`  - Effective: $${config.pricing.basePricePer1000Rows * config.pricing.markupMultiplier} per 1000 rows`);

  console.log('\nStep 3: Testing health check...');
  const healthResponse = await axios.get(`${baseURL}/api/health`);
  console.log(`✓ Gateway status: ${healthResponse.data.status}`);

  console.log('\n✅ Provider setup complete!');
  console.log(`\nShare this provider ID with AI agents: ${provider.id}`);
  console.log(`API Key saved to .env.local`);

  return { providerId: provider.id, apiKey };
}

// Configuration
const config: ProviderConfig = {
  name: 'Yearn Finance Database',
  email: 'devops@yearn.finance',
  walletAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1',
  connectionString: process.env.DATABASE_URL!,
  pricing: {
    basePricePer1000Rows: 1.0,
    markupMultiplier: 2.0
  }
};

setupProvider(config).catch(console.error);

React Component

Balance Monitor Widget

import React, { useState, useEffect } from 'react';
import axios from 'axios';

interface BalanceWidgetProps {
  walletAddress: string;
  providerId: string;
}

export function BalanceWidget({ walletAddress, providerId }: BalanceWidgetProps) {
  const [balance, setBalance] = useState<string>('0');
  const [loading, setLoading] = useState(true);
  const [transactions, setTransactions] = useState<any[]>([]);

  useEffect(() => {
    fetchBalance();
    fetchTransactions();

    // Poll every 30 seconds
    const interval = setInterval(() => {
      fetchBalance();
      fetchTransactions();
    }, 30000);

    return () => clearInterval(interval);
  }, [walletAddress, providerId]);

  const fetchBalance = async () => {
    try {
      const response = await axios.get(
        `https://x402.serendb.com/api/balance/${walletAddress}/${providerId}`
      );
      setBalance(response.data.balance);
    } catch (error) {
      console.error('Failed to fetch balance:', error);
    } finally {
      setLoading(false);
    }
  };

  const fetchTransactions = async () => {
    try {
      const response = await axios.get(
        `https://x402.serendb.com/api/transactions/${walletAddress}`,
        { params: { providerId, limit: 10 } }
      );
      setTransactions(response.data.transactions);
    } catch (error) {
      console.error('Failed to fetch transactions:', error);
    }
  };

  const totalSpent = transactions
    .filter(tx => tx.type === 'query')
    .reduce((sum, tx) => sum + Math.abs(parseFloat(tx.amount)), 0);

  if (loading) {
    return <div>Loading balance...</div>;
  }

  return (
    <div className="balance-widget">
      <h3>x402 Balance</h3>
      <div className="balance-amount">
        <span className="currency">USDC</span>
        <span className="amount">{parseFloat(balance).toFixed(2)}</span>
      </div>

      <div className="stats">
        <div className="stat">
          <label>Total Spent</label>
          <value>${totalSpent.toFixed(2)}</value>
        </div>
        <div className="stat">
          <label>Transactions</label>
          <value>{transactions.length}</value>
        </div>
      </div>

      <div className="transactions">
        <h4>Recent Activity</h4>
        {transactions.slice(0, 5).map(tx => (
          <div key={tx.id} className="transaction">
            <span className="type">{tx.type}</span>
            <span className={tx.amount.startsWith('-') ? 'negative' : 'positive'}>
              {tx.amount} USDC
            </span>
            <span className="time">
              {new Date(tx.timestamp).toLocaleTimeString()}
            </span>
          </div>
        ))}
      </div>
    </div>
  );
}

CLI Tool

Command-Line Agent

#!/bin/bash
# x402-cli.sh - Simple CLI for x402 Gateway

BASE_URL="https://x402.serendb.com"
WALLET_ADDRESS="${X402_WALLET_ADDRESS}"
PROVIDER_ID="${X402_PROVIDER_ID}"

# Check balance
balance() {
  curl -s "${BASE_URL}/api/balance/${WALLET_ADDRESS}/${PROVIDER_ID}" | jq -r '.balance'
}

# Execute query
query() {
  local sql="$1"
  curl -s -X POST "${BASE_URL}/api/query" \
    -H "Content-Type: application/json" \
    -d "{
      \"sql\": \"${sql}\",
      \"agentWallet\": \"${WALLET_ADDRESS}\",
      \"providerId\": \"${PROVIDER_ID}\"
    }" | jq .
}

# Transaction history
history() {
  local limit="${1:-10}"
  curl -s "${BASE_URL}/api/transactions/${WALLET_ADDRESS}?providerId=${PROVIDER_ID}&limit=${limit}" \
    | jq '.transactions'
}

# Main command router
case "$1" in
  balance)
    echo "Current balance: $(balance) USDC"
    ;;
  query)
    query "$2"
    ;;
  history)
    history "$2"
    ;;
  *)
    echo "Usage: $0 {balance|query|history}"
    echo ""
    echo "Examples:"
    echo "  $0 balance"
    echo "  $0 query 'SELECT * FROM users LIMIT 10'"
    echo "  $0 history 20"
    exit 1
    ;;
esac

Usage:

export X402_WALLET_ADDRESS="0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1"
export X402_PROVIDER_ID="945349e6-6d49-4c07-9257-2f2839e07173"

./x402-cli.sh balance
./x402-cli.sh query "SELECT COUNT(*) FROM users"
./x402-cli.sh history 10

Error Handling

Robust Error Handling Example

class X402Error extends Error {
  constructor(
    message: string,
    public statusCode: number,
    public retryable: boolean = false
  ) {
    super(message);
    this.name = 'X402Error';
  }
}

async function executeQuery(
  sql: string,
  options: { maxRetries?: number } = {}
): Promise<any> {
  const { maxRetries = 3 } = options;
  let lastError: Error;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await axios.post(
        'https://x402.serendb.com/api/query',
        {
          sql,
          agentWallet: process.env.WALLET_ADDRESS,
          providerId: process.env.PROVIDER_ID
        }
      );

      return response.data;
    } catch (error: any) {
      const status = error.response?.status;
      const message = error.response?.data?.error || error.message;

      switch (status) {
        case 402: // Payment Required
          throw new X402Error('Insufficient balance', 402);

        case 403: // Forbidden
          throw new X402Error(`Query not allowed: ${message}`, 403);

        case 429: // Rate Limited
          const retryAfter = error.response.headers['retry-after'] || 60;
          if (attempt < maxRetries) {
            console.log(`Rate limited. Retrying in ${retryAfter}s...`);
            await sleep(retryAfter * 1000);
            continue;
          }
          throw new X402Error('Rate limit exceeded', 429, true);

        case 500: // Server Error
          if (attempt < maxRetries) {
            console.log(`Server error. Retry ${attempt}/${maxRetries}...`);
            await sleep(1000 * attempt); // Exponential backoff
            lastError = error;
            continue;
          }
          throw new X402Error('Server error after retries', 500);

        default:
          throw new X402Error(message, status || 500);
      }
    }
  }

  throw lastError!;
}

function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Need More Help?

  • Get Started - Quick start guide

  • API Reference - Complete API documentation

  • Pricing Models - Pricing strategies

  • GitHub: https://github.com/serenorg/serenai-x402

  • Issues: https://github.com/serenorg/serenai-x402/issues

Last updated