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
;;
esacUsage:
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 10Error 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