Documentation
🤖Agent SDK

@x402/agent

TypeScript SDK for AI agents that consume paid APIs. Drop-in fetch replacement with automatic x402 payment handling.

Installation

bash
npm install @x402/agent
# or
pnpm add @x402/agent
# or
yarn add @x402/agent

Configuration

Create an agent client with your Gateway URL and API key:

typescript
import { createAgentClient, AgentClientConfig } from '@x402/agent';

const config: AgentClientConfig = {
  // Required: Gateway API base URL
  gatewayBaseUrl: 'https://xfour.xyz/api/gateway',
  
  // Required: Your agent API key
  apiKey: process.env.X402_AGENT_KEY!,
  
  // Optional: Custom fetch implementation (default: global fetch)
  fetchImpl: customFetch,
  
  // Optional: Header name for payment proof
  // (default: 'X-MOCK-PAID-INVOICE')
  proofHeaderName: 'X-MOCK-PAID-INVOICE',
};

const client = createAgentClient(config);

Configuration Options

OptionTypeRequiredDescription
gatewayBaseUrlstringYesBase URL of the x402 Gateway
apiKeystringYesYour agent API key
fetchImpltypeof fetchNoCustom fetch implementation
proofHeaderNamestringNoHeader name for payment proof

fetchWithX402

The main method for making requests. Works exactly like fetch, but automatically handles 402 Payment Required responses.

typescript
const response = await client.fetchWithX402(
  'https://api.example.com/ai/complete',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ prompt: 'Hello world' }),
  }
);

console.log('Status:', response.status);
console.log('Body:', await response.json());

How It Works

  1. 1. Initial Request — Makes your request normally
  2. 2. Non-402 Response — If not 402, returns immediately
  3. 3. 402 Response — Extracts X-402-* headers
  4. 4. Quote — Calls Gateway /quote for authorization
  5. 5. Payment — If allowed, calls /pay to execute
  6. 6. Retry — Retries original request with proof header

Error Handling

The SDK provides typed error classes for payment failures:

PaymentDeniedError

Thrown when the Gateway denies a payment quote (policy violation, insufficient balance, etc.).

typescript
import { PaymentDeniedError } from '@x402/agent';

try {
  await client.fetchWithX402(url);
} catch (err) {
  if (err instanceof PaymentDeniedError) {
    console.log('Denied reason:', err.reason);
    // Possible reasons:
    // - "AGENT_DAILY_LIMIT"
    // - "INSUFFICIENT_BALANCE"
    // - "PROVIDER_NOT_ALLOWED"
    // - "AMOUNT_EXCEEDS_LIMIT"
  }
}

PaymentFailedError

Thrown when a payment execution fails (network error, transaction failure, etc.).

typescript
import { PaymentFailedError } from '@x402/agent';

try {
  await client.fetchWithX402(url);
} catch (err) {
  if (err instanceof PaymentFailedError) {
    console.log('Error code:', err.code);
    console.log('Details:', err.details);
  }
}

Complete Error Handling Example

typescript
import { 
  createAgentClient, 
  PaymentDeniedError, 
  PaymentFailedError 
} from '@x402/agent';

async function makeRequest() {
  const client = createAgentClient({
    gatewayBaseUrl: 'https://xfour.xyz/api/gateway',
    apiKey: process.env.X402_AGENT_KEY!,
  });

  try {
    const res = await client.fetchWithX402(
      'https://api.example.com/ai/complete',
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ prompt: 'Hello' }),
      }
    );

    return await res.json();
    
  } catch (err) {
    if (err instanceof PaymentDeniedError) {
      // Handle policy denial
      console.error('Payment denied:', err.reason);
      // Maybe notify admin, show upgrade prompt, etc.
      
    } else if (err instanceof PaymentFailedError) {
      // Handle execution failure
      console.error('Payment failed:', err.message);
      // Maybe retry later, alert monitoring, etc.
      
    } else {
      // Other errors (network, etc.)
      throw err;
    }
  }
}

Examples

Basic AI Completion

typescript
const response = await client.fetchWithX402(
  'https://api.openrouter.ai/v1/complete',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'gpt-4',
      messages: [{ role: 'user', content: 'Hello!' }],
    }),
  }
);

const data = await response.json();
console.log(data.choices[0].message.content);

Streaming Response

typescript
const response = await client.fetchWithX402(
  'https://api.example.com/stream',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ prompt: 'Write a poem' }),
  }
);

const reader = response.body?.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader!.read();
  if (done) break;
  process.stdout.write(decoder.decode(value));
}

Multiple Requests in Parallel

typescript
const [result1, result2, result3] = await Promise.all([
  client.fetchWithX402('https://api.example.com/resource-1'),
  client.fetchWithX402('https://api.example.com/resource-2'),
  client.fetchWithX402('https://api.example.com/resource-3'),
]);

// Each request handles payment independently
console.log(await result1.json());
console.log(await result2.json());
console.log(await result3.json());

Environment Compatibility

The SDK works in any environment with a global fetch implementation:

💻

Node.js 18+

Uses global fetch

🌐

Browser

React, Next.js, etc.

Edge

Vercel Edge, Cloudflare

Older Node.js Versions

For Node.js < 18, provide a custom fetch implementation:

typescript
import nodeFetch from 'node-fetch';

const client = createAgentClient({
  gatewayBaseUrl: '...',
  apiKey: '...',
  fetchImpl: nodeFetch as unknown as typeof fetch,
});