Skip to main content
x402 is a revolutionary open payment protocol developed by Coinbase that enables instant, automatic stablecoin payments directly over HTTP. It empowers AI agents to autonomously pay for services without subscriptions or API keys, unlocking truly autonomous, monetized chat agents capable of reasoning, transacting, and delivering value seamlessly.

What is x402?

x402 transforms how agents interact with paid services by embedding payments directly into HTTP requests. Instead of managing API keys or subscriptions, your agents can pay for exactly what they use, when they use it.

Key Benefits

  • Autonomous economic transactions - Agents can transact without human intervention
  • Pay-as-you-go monetization - Only pay for the services you actually use
  • Minimal setup - Often requires just one middleware line
  • Instant settlement - Payments are verified on-chain in real-time

Protocol Flow

Understanding the x402 payment flow is essential for building autonomous agents:
1

Initial Request

Client requests access to a protected resource.
2

Payment Required Response

Server returns HTTP 402 status with payment details including:
  • Payment amount
  • Recipient address
  • Payment reference
3

Payment Execution

Client sends payment payload via HTTP header (e.g., X-PAYMENT).
The payment facilitator handles the on-chain settlement verification automatically.
4

Content Delivery

Client retries the original request, and server responds with:
  • Requested content
  • Payment confirmation header (e.g., X-PAYMENT-RESPONSE)

Integrating x402 with XMTP Agents

Combining x402 with XMTP chat agents creates powerful autonomous economic agents. Here’s how to implement this integration:

Agent Architecture

Your agent needs to handle three main scenarios:
  1. Free requests - Standard queries that don’t require payment
  2. Payment-gated requests - Premium features that trigger x402 payments
  3. Payment failures - Graceful handling when payments fail

Basic Implementation

Here’s how your agent handles payment-gated requests using the agent SDK:
import { Agent } from '@xmtp/agent-sdk';
import { PaymentFacilitator } from '@coinbase/x402-sdk';

// Create agent
const agent = await Agent.createFromEnv({
  env: 'production',
});

// Handle text messages with premium features
agent.on('text', async (ctx) => {
  const message = ctx.message.content;
  
  // Check if user is requesting premium feature
  if (message.includes('floor price')) {
    await handlePremiumRequest(ctx);
  }
});

// Initialize payment facilitator
const facilitator = new PaymentFacilitator({
  privateKey: process.env.XMTP_WALLET_KEY!,
  network: process.env.NETWORK || 'base'
});

// Agent handles payment-gated requests
async function handlePremiumRequest(ctx: any) {
  try {
    const endpoint = "/premium-api/nft-floor-price";
    const response = await fetch(endpoint);

    // Check if payment is required
    if (response.status === 402) {
      const paymentDetails = await response.json();
      
      // Notify user about payment requirement
      await ctx.sendText(`💰 Payment required: ${paymentDetails.amount} USDC. Processing...`);
      
      // Execute payment
      const payment = await facilitator.createPayment({
        amount: paymentDetails.amount,
        recipient: paymentDetails.recipient,
        reference: paymentDetails.reference,
        currency: 'USDC'
      });
      
      // Retry request with payment
      const retryResponse = await fetch(endpoint, {
        headers: { "X-PAYMENT": payment.payload }
      });

      if (retryResponse.ok) {
        const data = await retryResponse.json();
        await ctx.sendText(`📊 NFT floor price: ${data.floorPrice} ETH`);
      } else {
        await ctx.sendText("❌ Payment processed but service unavailable. Please try again.");
      }
    } else if (response.ok) {
      // Handle free-tier response
      const data = await response.json();
      await ctx.sendText(`📊 NFT floor price: ${data.floorPrice} ETH`);
    }
  } catch (error) {
    await ctx.sendText("❌ Unable to fetch data. Please try again later.");
  }
}

Environment Setup

Make sure to set up your environment variables:
XMTP_WALLET_KEY= # the private key of the wallet
XMTP_DB_ENCRYPTION_KEY= # encryption key for the local database
XMTP_ENV=production # local, dev, production
NETWORK=base # blockchain network for payments

Complete Agent Example

Here’s a full implementation combining XMTP with x402:
import { Agent } from '@xmtp/agent-sdk';
import { PaymentFacilitator } from '@coinbase/x402-sdk';

// Initialize payment facilitator
const facilitator = new PaymentFacilitator({
  privateKey: process.env.XMTP_WALLET_KEY!,
  network: process.env.NETWORK || 'base'
});

// Create the agent
const agent = await Agent.createFromEnv({
  env: 'production', // base app works only on production
});

// Handle text messages
agent.on('text', async (ctx) => {
  const content = ctx.message.content.toLowerCase();

  // Route messages based on content
  if (content.includes('floor price')) {
    await handleFloorPriceRequest(ctx, content);
  } else if (content.includes('market data')) {
    await handleMarketDataRequest(ctx, content);
  } else {
    await ctx.sendText("Hi! I can help with:\n• NFT floor prices\n• Market data\n\nJust ask!");
  }
});

// Handle NFT floor price requests
async function handleFloorPriceRequest(ctx: any, content: string) {
  // Extract collection name from message
  const collection = extractCollection(content);
  
  if (!collection) {
    await ctx.sendText("Please specify an NFT collection name.");
    return;
  }

  try {
    const response = await fetch(`/api/nft-floor/${collection}`);
    
    if (response.status === 402) {
      await processPaymentAndRetry(
        ctx,
        `/api/nft-floor/${collection}`,
        `📊 Floor price for ${collection}:`
      );
    } else if (response.ok) {
      const data = await response.json();
      await ctx.sendText(`📊 ${collection} floor price: ${data.floorPrice} ETH`);
    }
  } catch (error) {
    await ctx.sendText("❌ Unable to fetch floor price data.");
  }
}

// Handle market data requests
async function handleMarketDataRequest(ctx: any, content: string) {
  const symbol = extractSymbol(content);
  
  if (!symbol) {
    await ctx.sendText("Please specify a token symbol (e.g., BTC, ETH).");
    return;
  }

  try {
    const response = await fetch(`/api/market-data/${symbol}`);
    
    if (response.status === 402) {
      await processPaymentAndRetry(
        ctx,
        `/api/market-data/${symbol}`,
        `💹 Market data for ${symbol.toUpperCase()}:`
      );
    } else if (response.ok) {
      const data = await response.json();
      await ctx.sendText(`💹 ${symbol.toUpperCase()} - Price: $${data.price} | 24h: ${data.change24h}%`);
    }
  } catch (error) {
    await ctx.sendText("❌ Unable to fetch market data.");
  }
}

// Process payment and retry request
async function processPaymentAndRetry(ctx: any, endpoint: string, successPrefix: string) {
  try {
    // Initial request to get payment details
    const response = await fetch(endpoint);
    const paymentDetails = await response.json();
    
    // Notify user of payment
    await ctx.sendText(`💰 Payment required: ${paymentDetails.amount} USDC. Processing...`);
    
    // Execute payment
    const payment = await facilitator.createPayment({
      amount: paymentDetails.amount,
      recipient: paymentDetails.recipient,
      reference: paymentDetails.reference,
      currency: 'USDC'
    });
    
    // Retry with payment
    const retryResponse = await fetch(endpoint, {
      headers: { "X-PAYMENT": payment.payload }
    });
    
    if (retryResponse.ok) {
      const data = await retryResponse.json();
      await ctx.sendText(`${successPrefix} ${JSON.stringify(data, null, 2)}`);
    } else {
      await ctx.sendText("❌ Payment processed but service error occurred.");
    }
  } catch (error) {
    await ctx.sendText(`❌ Payment failed: ${error.message}`);
  }
}

// Extract collection name from message
function extractCollection(content: string): string | null {
  const words = content.toLowerCase().split(' ');
  const collectionIndex = words.findIndex(word => 
    ['cryptopunks', 'bayc', 'azuki', 'pudgypenguins'].includes(word)
  );
  return collectionIndex !== -1 ? words[collectionIndex] : null;
}

// Extract token symbol from message
function extractSymbol(content: string): string | null {
  const words = content.toLowerCase().split(' ');
  const symbolIndex = words.findIndex(word => 
    ['btc', 'eth', 'usdc', 'base'].includes(word)
  );
  return symbolIndex !== -1 ? words[symbolIndex] : null;
}

// Start the agent
agent.on('start', () => {
  console.log('Autonomous Payment Agent started!');
  console.log(`Agent address: ${agent.address}`);
});

await agent.start();

Real-World Examples

Several projects have successfully implemented x402 with XMTP agents:

Stableburn x402

A live implementation showcasing:
  • XMTP v4 integration
  • AgentKit functionality
  • x402-based micro-payments (0.0010.001–0.005 per request)
  • Automatic revenue burning via on-chain token swaps

Stableburn x402

Autonomous agent that monetizes premium endpoints with micro-payments

Crossmint Worldstore Agent

An XMTP chat agent for shopping featuring:
  • x402 payment integration
  • EIP-3009 gasless USDC transactions
  • Base network integration
  • Interactive content types in chat

Worldstore Agent

Shopping agent with embedded x402 payments and commerce functionality

Best Practices

Payment UX Guidelines

Always inform users about payment requirements before executing transactions. Transparency builds trust with your agent.
  • Clear pricing - Display exact costs upfront
  • Payment confirmation - Confirm successful payments
  • Graceful failures - Handle payment errors elegantly
  • Value communication - Explain what users get for their payment

Error Handling

Implement comprehensive error handling for common scenarios:
async function robustPaymentHandler(ctx: any, endpoint: string) {
  try {
    const response = await fetch(endpoint);
    
    if (response.status === 402) {
      const paymentDetails = await response.json();
      
      // Validate payment details
      if (!paymentDetails.amount || !paymentDetails.recipient) {
        await ctx.sendText("❌ Invalid payment request from service.");
        return;
      }
      
      // Check if amount is reasonable
      if (parseFloat(paymentDetails.amount) > 1.0) {
        await ctx.sendText(`⚠️ High payment amount: ${paymentDetails.amount} USDC. Skipping for safety.`);
        return;
      }
      
      // Process payment with timeout
      const paymentPromise = facilitator.createPayment({
        amount: paymentDetails.amount,
        recipient: paymentDetails.recipient,
        reference: paymentDetails.reference,
        currency: 'USDC'
      });
      const timeoutPromise = new Promise((_, reject) => 
        setTimeout(() => reject(new Error('Payment timeout')), 30000)
      );
      
      const payment = await Promise.race([paymentPromise, timeoutPromise]);
      
      // Retry with payment
      const retryResponse = await fetch(endpoint, {
        headers: { "X-PAYMENT": payment.payload }
      });
      
      if (retryResponse.ok) {
        const data = await retryResponse.json();
        await ctx.sendText(`✅ Payment successful! ${JSON.stringify(data)}`);
      } else {
        await ctx.sendText("❌ Payment processed but service unavailable.");
      }
    }
  } catch (error) {
    if (error.message.includes('timeout')) {
      await ctx.sendText("⏱️ Payment timed out. Please try again.");
    } else if (error.message.includes('insufficient funds')) {
      await ctx.sendText("💸 Insufficient funds for payment.");
    } else {
      await ctx.sendText(`❌ Payment error: ${error.message}`);
    }
  }
}

Security Considerations

Never expose private keys in your code. Always use environment variables and secure key management practices.
  • Key management - Use secure environment variables
  • Payment limits - Set maximum payment thresholds
  • Rate limiting - Prevent payment spam attacks
  • Audit trails - Log all payment activities
  • Network validation - Verify on-chain settlement

Resources

Next Steps

Ready to build your autonomous payment agent? Here’s your roadmap:
1

Set up development environment

Install the x402 SDK and XMTP agent SDK:
npm i @xmtp/agent-sdk @coinbase/x402-sdk
2

Implement basic agent

Start with a simple XMTP agent following our Getting Started guide.
3

Add payment functionality

Integrate x402 payment handling using the examples above.
4

Test thoroughly

Test payment flows on testnet before deploying to production.
5

Deploy and monitor

Deploy your agent and monitor payment transactions and user interactions.
The future of autonomous agents is here. With x402 and XMTP, you can build agents that truly operate independently in the digital economy.