Skip to main content
Prerequisites
  • A crypto wallet funded with USDC (EVM or Solana supported by Coinbase x402)
  • Node.js + npm or Python + pip
  • Access to the Horizon base URL (https://api.horizon.new/v1)
  • Familiarity with Coinbase’s Quickstart for buyers

Step 1 — Install x402 client helpers

  • Node.js
  • Python
Install one of Coinbase’s helper packages so 402 challenges are handled automatically:
npm install x402-fetch
# or
npm install x402-axios

Step 2 — Instantiate a wallet client

Create a signer that can authorise onchain payments.
  1. Export credentials:
    export CDP_API_KEY_ID=your-api-key-id
    export CDP_API_KEY_SECRET=your-api-key-secret
    export CDP_WALLET_SECRET=your-wallet-secret
    
  2. Install dependencies:
    npm install @coinbase/cdp-sdk dotenv
    # or
    pip install cdp python-dotenv
    
  3. Instantiate the wallet client:
    • Node.js
    • Python
    import { CdpClient } from '@coinbase/cdp-sdk';
    import dotenv from 'dotenv';
    
    dotenv.config();
    
    const cdp = new CdpClient();
    const account = await cdp.evm.createAccount();
    // pass `account` into x402 helpers
    
  1. Install Privy’s Node SDK:
    npm install @privy-io/node
    
  2. Instantiate the client using the app credentials from your Privy dashboard:
    import { PrivyClient, APIError, PrivyAPIError } from '@privy-io/node';
    import dotenv from 'dotenv';
    
    dotenv.config();
    
    const privy = new PrivyClient({
      appId: process.env.PRIVY_APP_ID!,
      appSecret: process.env.PRIVY_APP_SECRET!,
    });
    
  3. Create an embedded wallet and capture its id for future requests:
    • Ethereum
    • Solana
    try {
      const wallet = await privy.wallets().create({ chain_type: 'ethereum' });
      const walletId = wallet.id;
    } catch (error) {
      if (error instanceof APIError || error instanceof PrivyAPIError) {
        console.error(error.status, error.name, error.message);
      } else {
        throw error;
      }
    }
    
  4. When you need to fund jobs or replay invoices, use Privy helpers to sign transactions:
    const paymentMessage = Buffer.from(invoice, 'utf8').toString('base64');
    const { signature } = await privy.wallets().solana().signMessage(walletId, {
      message: paymentMessage,
    });
    
Review Privy’s wallet quickstart for end-user provisioning and transaction helpers.
  1. Install Dynamic’s Node SDK:
    npm install @dynamic-labs/node
    
  2. Configure the client with your Dynamic environment ID and server token:
    import { DynamicServerWallets } from '@dynamic-labs/node';
    import dotenv from 'dotenv';
    
    dotenv.config();
    
    const dynamic = new DynamicServerWallets({
      environmentId: process.env.DYNAMIC_ENVIRONMENT_ID!,
      authToken: process.env.DYNAMIC_AUTH_TOKEN!,
    });
    
  3. Create an MPC wallet on the chain you plan to use for x402 settlements:
    • EVM (Base Sepolia)
    • Solana
    const wallet = await dynamic.createWallet({
      chain: 'EVM',
      chainId: 84532, // Base Sepolia
      label: 'horizon-x402',
    });
    
    const address = wallet.address;
    
  4. Use Dynamic’s helpers to sign and send payments when replaying 402 invoices:
    const { signature } = await dynamic.signMessage({
      walletId: wallet.id,
      message: invoicePayload, // received from the x402 challenge
    });
    
Enable embedded wallets and target chains in the Dynamic dashboard so wallets can transact immediately.
  • Node.js (viem)
  • Python (eth-account)
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(process.env.PRIVATE_KEY!);
import { createKeyPairSignerFromBytes } from '@solana/kit';
import { base58 } from '@scure/base';

const signer = await createKeyPairSignerFromBytes(
  base58.decode(process.env.SOLANA_PRIVATE_KEY!)
);

Step 3 — Wrap your HTTP client to auto-pay

The helper packages intercept 402 Payment Required responses, sign the invoice, and replay the request for you.
  • Node.js (x402-fetch)
  • Node.js (x402-axios)
  • Python (HTTPX)
  • Python (Requests)
import { wrapFetchWithPayment } from 'x402-fetch';

const account = /* wallet client from Step 2 */;
const fetchWithPayment = wrapFetchWithPayment(fetch, account);

const response = await fetchWithPayment('https://api.horizon.new/v1/search?q=horizon', {
  method: 'GET',
});

const body = await response.json();
console.log(body);

Step 4 — Call Horizon endpoints

Once your HTTP client auto-replays paid requests, you can hit any Horizon endpoint directly. The helper packages take care of X-PAYMENT headers; you only need to focus on request bodies.
const baseUrl = process.env.HORIZON_BASE_URL ?? 'https://api.horizon.new/v1';

const response = await fetchWithPayment(`${baseUrl}/generate/text`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    prompt: 'Write a welcome note for new Horizon users.',
    metadata: { campaign: 'spring-launch' },
    webhookUrl: 'https://example.com/webhooks/horizon',
  }),
});

const job = await response.json();
console.log(job.content);

// Optional: inspect the recorded job later
const audit = await fetchWithPayment(`${baseUrl}/jobs/${job.jobId}`).then((res) => res.json());
import FormData from 'form-data';
import { createReadStream } from 'fs';

const baseUrl = process.env.HORIZON_BASE_URL ?? 'https://api.horizon.new/v1';

const form = new FormData();
form.append('file', createReadStream('./guides/onboarding.pdf'));
form.append('sourceName', 'Agent Playbook 2025');
form.append('metadata', JSON.stringify({ department: 'Support', quarter: 'Q1-2025' }));

const response = await fetchWithPayment(`${baseUrl}/extract/pdf`, {
  method: 'POST',
  headers: form.getHeaders(),
  body: form,
});

const job = await response.json();

const status = await fetchWithPayment(job.statusUrl).then((res) => res.json());

console.log(status.state);

Optional — Discover services dynamically

Leverage the x402 Bazaar when you want code to discover available services instead of hardcoding endpoints. See Coinbase’s Bazaar documentation and the example clients for Node.js and Python.

Where to go next