JSPM

  • Created
  • Published
  • Downloads 355
  • Score
    100M100P100Q101245F
  • License MIT

Full-stack x402 SDK - add paid API monetization to any endpoint. Express middleware, React hooks, Access Pass, dynamic pricing. Solana, Base, Polygon, Arbitrum, Optimism, Avalanche, SKALE.

Package Exports

  • @dexterai/x402/adapters
  • @dexterai/x402/client
  • @dexterai/x402/react
  • @dexterai/x402/server
  • @dexterai/x402/utils

Readme

Dexter

@dexterai/x402

Full-stack x402 SDK. Add paid API monetization to any endpoint. Solana, Base, and 4 more chains.

npm Node Live Demo Marketplace

Try it with real payments →


What is x402?

x402 is a protocol for HTTP-native micropayments. When a server returns HTTP status 402 Payment Required, it includes payment details in a PAYMENT-REQUIRED header. The client signs a payment transaction and retries the request with a PAYMENT-SIGNATURE header. The server verifies and settles the payment, then returns the protected content.

This SDK handles the entire flow automatically—you just call fetch() and payments happen transparently. With Access Pass mode, buyers pay once and get unlimited access for a time window—no per-request signing needed.


Why This SDK?

Monetize any API in minutes. Add payments to your server in ~10 lines. Clients pay automatically—no checkout pages, no subscriptions, no invoices. Just HTTP.

Dynamic pricing. Charge based on usage: characters, tokens, records, pixels, API calls—whatever makes sense. Price scales with input, not fixed rates.

Token-accurate LLM pricing. Built-in tiktoken support prices AI requests by actual token count. Works with OpenAI models out of the box, or bring your own rates for Anthropic, Gemini, Mistral, or local models.

Access Pass. Pay once, get unlimited access for a time window. Buyers connect a wallet, make one payment, and receive a JWT token that works like an API key—no per-request signing, no private keys in code. The Stripe replacement for crypto-native APIs.

Full-stack. Client SDK for browsers, server SDK for backends. React hooks, Express middleware patterns, facilitator client—everything you need.

Multi-chain. Solana and Base (Ethereum L2) with the same API. Add wallets for both and the SDK picks the right one automatically.

Works out of the box. Built-in RPC proxy, pre-flight balance checks, automatic retry on 402. Uses the Dexter facilitator by default—Solana's most feature-rich x402 facilitator.


Automatic Marketplace Discovery

When someone pays for your API through the Dexter facilitator, your endpoint is automatically discovered and listed in the OpenDexter Marketplace — a searchable directory of 5,000+ paid APIs used by AI agents.

No registration step needed. The flow:

  1. You add x402Middleware to your endpoint (see Quick Start below)
  2. An agent pays for your API → the facilitator processes the settlement
  3. Your endpoint is auto-discovered, AI-named, and quality-verified
  4. Agents find it via x402_search in any MCP client (ChatGPT, Claude, Cursor, etc.)

Quality-verified endpoints (score 75+) get promoted in search results. The verification bot tests your endpoint automatically — no action required on your part.


Quick Start

Install

npm install @dexterai/x402

Client (Node.js)

The simplest way to make x402 payments from scripts:

import { wrapFetch } from '@dexterai/x402/client';

// Solana
const x402Fetch = wrapFetch(fetch, {
  walletPrivateKey: process.env.SOLANA_PRIVATE_KEY,
});

// EVM (Base, Polygon, Arbitrum, Optimism, Avalanche, SKALE)
const x402Fetch = wrapFetch(fetch, {
  evmPrivateKey: process.env.EVM_PRIVATE_KEY,  // requires: npm install viem
});

// Both — SDK picks the chain with balance
const x402Fetch = wrapFetch(fetch, {
  walletPrivateKey: process.env.SOLANA_PRIVATE_KEY,
  evmPrivateKey: process.env.EVM_PRIVATE_KEY,
});

// That's it. 402 responses are handled automatically.
const response = await x402Fetch('https://api.example.com/protected');

Check the payment receipt:

import { wrapFetch, getPaymentReceipt } from '@dexterai/x402/client';

const x402Fetch = wrapFetch(fetch, { walletPrivateKey: process.env.SOLANA_PRIVATE_KEY });
const response = await x402Fetch('https://api.example.com/protected');

const receipt = getPaymentReceipt(response);
if (receipt) {
  console.log('Paid:', receipt.transaction, 'on', receipt.network);
}

Client (Browser)

import { createX402Client } from '@dexterai/x402/client';

const client = createX402Client({
  wallets: {
    solana: solanaWallet,
    evm: evmWallet,
  },
});

// That's it. 402 responses are handled automatically.
const response = await client.fetch('https://api.example.com/protected');

RPC URLs are optional—the SDK uses Dexter's RPC proxy by default. Override if needed:

const client = createX402Client({
  wallets: { solana: solanaWallet },
  rpcUrls: {
    'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp': 'https://your-rpc.com',
  },
});

React

Works with @solana/wallet-adapter-react and wagmi out of the box:

import { useX402Payment } from '@dexterai/x402/react';
import { useWallet } from '@solana/wallet-adapter-react';  // Solana
import { useAccount } from 'wagmi';                        // EVM (Base)

function PayButton() {
  // Get wallets from your existing providers
  const solanaWallet = useWallet();
  const evmWallet = useAccount();

  const { fetch, isLoading, balances, transactionUrl } = useX402Payment({
    wallets: { 
      solana: solanaWallet,  // Pass directly - SDK handles the interface
      evm: evmWallet,
    },
  });

  return (
    <div>
      <p>Balance: ${balances[0]?.balance.toFixed(2)}</p>
      <button 
        onClick={() => fetch('/api/protected')} 
        disabled={isLoading || !solanaWallet.connected}
      >
        {isLoading ? 'Paying...' : 'Pay'}
      </button>
      {transactionUrl && <a href={transactionUrl}>View Transaction</a>}
    </div>
  );
}

Supported Networks

All networks supported by the Dexter facilitator. USDC on every chain.

Mainnets:

Network CAIP-2 Status
Solana solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp Production
Base eip155:8453 Production
Polygon eip155:137 Production
Arbitrum eip155:42161 Production
Optimism eip155:10 Production
Avalanche eip155:43114 Production
SKALE Base eip155:1187947933 Production (zero gas)

Testnets:

Network CAIP-2
Solana Devnet solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1
Base Sepolia eip155:84532
SKALE Sepolia eip155:324705682

Accept payments on multiple chains simultaneously:

// Same address across EVM chains
app.get('/api/data', x402Middleware({
  payTo: '0xYourAddress',
  amount: '0.01',
  network: ['eip155:8453', 'eip155:137', 'eip155:42161', 'eip155:10'],
}));

// Different addresses per chain family
app.get('/api/data', x402Middleware({
  payTo: {
    'solana:*': 'YourSolanaAddress...',
    'eip155:*': '0xYourEvmAddress...',
  },
  amount: '0.01',
  network: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', 'eip155:8453', 'eip155:137'],
}));

Package Exports

// Client - browser
import { createX402Client } from '@dexterai/x402/client';

// Client - Node.js (private key wallet)
import { wrapFetch, createKeypairWallet } from '@dexterai/x402/client';

// React hook
import { useX402Payment } from '@dexterai/x402/react';

// Server - Express middleware
import { x402Middleware } from '@dexterai/x402/server';

// Server - Access Pass (pay once, unlimited requests)
import { x402AccessPass } from '@dexterai/x402/server';

// Server - manual control
import { createX402Server } from '@dexterai/x402/server';

// Server - dynamic pricing
import { createDynamicPricing, createTokenPricing } from '@dexterai/x402/server';

// React - Access Pass hook
import { useAccessPass } from '@dexterai/x402/react';

// Chain adapters (advanced)
import { createSolanaAdapter, createEvmAdapter } from '@dexterai/x402/adapters';

// Utilities
import { toAtomicUnits, fromAtomicUnits } from '@dexterai/x402/utils';

Utilities

import { toAtomicUnits, fromAtomicUnits } from '@dexterai/x402/utils';

// Convert dollars to atomic units (for API calls)
toAtomicUnits(0.05, 6);  // '50000'
toAtomicUnits(1.50, 6);  // '1500000'

// Convert atomic units back to dollars (for display)
fromAtomicUnits('50000', 6);   // 0.05
fromAtomicUnits(1500000n, 6);  // 1.5

Server SDK

Express Middleware — NEW!

One-liner payment protection for any Express endpoint:

import express from 'express';
import { x402Middleware } from '@dexterai/x402/server';

const app = express();

app.get('/api/protected',
  x402Middleware({
    payTo: 'YourSolanaAddress...',
    amount: '0.01',  // $0.01 USD
  }),
  (req, res) => {
    // This only runs after successful payment
    res.json({ data: 'protected content' });
  }
);

Options:

  • payTo — Address to receive payments
  • amount — Price in USD (e.g., '0.01' for 1 cent)
  • network — CAIP-2 network (default: Solana mainnet)
  • description — Human-readable description
  • facilitatorUrl — Override facilitator (default: x402.dexter.cash)
  • verbose — Enable debug logging

Access Pass — Pay Once, Unlimited Requests

Replace API keys with time-limited access passes. Buyers make one payment and get a JWT token for unlimited requests during a time window.

Server:

import express from 'express';
import { x402AccessPass } from '@dexterai/x402/server';

const app = express();

// Protect all /api routes with access pass
app.use('/api', x402AccessPass({
  payTo: 'YourSolanaAddress...',
  tiers: {
    '1h':  '0.50',   // $0.50 for 1 hour
    '24h': '2.00',   // $2.00 for 24 hours
  },
  ratePerHour: '0.50',  // also accept custom durations
}));

app.get('/api/data', (req, res) => {
  // Only runs with a valid access pass
  res.json({ data: 'premium content' });
});

Client (Node.js):

import { wrapFetch } from '@dexterai/x402/client';

const x402Fetch = wrapFetch(fetch, {
  walletPrivateKey: process.env.SOLANA_PRIVATE_KEY,
  accessPass: { preferTier: '1h', maxSpend: '1.00' },
});

// First call: auto-purchases a 1-hour pass ($0.50 USDC)
const res1 = await x402Fetch('https://api.example.com/api/data');

// All subsequent calls for the next hour: uses cached JWT, zero payment
const res2 = await x402Fetch('https://api.example.com/api/data');
const res3 = await x402Fetch('https://api.example.com/api/data');

React:

import { useAccessPass } from '@dexterai/x402/react';

function Dashboard() {
  const { tiers, pass, isPassValid, purchasePass, fetch: apFetch } = useAccessPass({
    wallets: { solana: solanaWallet },
    resourceUrl: 'https://api.example.com',
  });

  return (
    <div>
      {!isPassValid && tiers?.map(t => (
        <button key={t.id} onClick={() => purchasePass(t.id)}>
          {t.label} — ${t.price}
        </button>
      ))}
      {isPassValid && <p>Pass active! {pass?.remainingSeconds}s remaining</p>}
      <button onClick={() => apFetch('/api/data')}>Fetch Data</button>
    </div>
  );
}

How it works:

  1. Client requests a protected endpoint → Server returns 402 with X-ACCESS-PASS-TIERS header
  2. Client selects a tier and pays via x402 → Server verifies, settles, issues a JWT
  3. Server returns 200 with ACCESS-PASS header containing the JWT
  4. Client caches the JWT and includes it as Authorization: Bearer <token> on all subsequent requests
  5. Server validates the JWT locally (no facilitator call) → instant response

Options:

  • payTo — Address to receive payments
  • tiers — Named duration tiers with prices (e.g., { '1h': '0.50' })
  • ratePerHour — Rate for custom durations (buyer sends ?duration=<seconds>)
  • network — CAIP-2 network (default: Solana mainnet)
  • secret — HMAC secret for JWT signing (auto-generated if not provided)
  • facilitatorUrl — Override facilitator (default: x402.dexter.cash)

Live demo →


Manual Server (Advanced)

For more control over the payment flow:

import { createX402Server } from '@dexterai/x402/server';

const server = createX402Server({
  payTo: 'YourAddress...',
  network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
});

// In your route handler
app.post('/protected', async (req, res) => {
  const paymentSig = req.headers['payment-signature'];

  if (!paymentSig) {
    const requirements = await server.buildRequirements({
      amountAtomic: '50000',  // $0.05 USDC
      resourceUrl: req.originalUrl,
    });
    res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));
    return res.status(402).json({});
  }

  const result = await server.settlePayment(paymentSig);
  if (!result.success) {
    return res.status(402).json({ error: result.errorReason });
  }

  res.json({ data: 'Your protected content' });
});

Dynamic Pricing

Generic pricing for any use case - charge by characters, bytes, API calls, or any unit you define. No external dependencies.

Works for:

  • LLM/AI endpoints (by character count)
  • Image processing (by pixel count or file size)
  • Data APIs (by record count)
  • Any service where cost scales with input
import { createX402Server, createDynamicPricing } from '@dexterai/x402/server';

const server = createX402Server({ payTo: '...', network: '...' });
const pricing = createDynamicPricing({
  unitSize: 1000,      // chars per unit
  ratePerUnit: 0.01,   // $0.01 per unit
  minUsd: 0.01,        // floor
  maxUsd: 10.00,       // ceiling
});

app.post('/api/llm', async (req, res) => {
  const { prompt } = req.body;
  const paymentSig = req.headers['payment-signature'];

  if (!paymentSig) {
    const quote = pricing.calculate(prompt);
    const requirements = await server.buildRequirements({
      amountAtomic: quote.amountAtomic,
      resourceUrl: req.originalUrl,
    });
    res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));
    res.setHeader('X-Quote-Hash', quote.quoteHash);
    return res.status(402).json({ usdAmount: quote.usdAmount });
  }

  // Validate quote hasn't changed (prevents prompt manipulation)
  const quoteHash = req.headers['x-quote-hash'];
  if (!pricing.validateQuote(prompt, quoteHash)) {
    return res.status(400).json({ error: 'Prompt changed, re-quote required' });
  }

  const result = await server.settlePayment(paymentSig);
  if (!result.success) return res.status(402).json({ error: result.errorReason });

  const response = await runLLM(prompt);
  res.json(response);
});

The client SDK automatically forwards X-Quote-Hash on retry.


Token Pricing (LLM-Accurate)

Accurate token-based pricing for LLMs. Uses tiktoken for token counting. Supports OpenAI models out of the box, plus custom rates for Anthropic, Gemini, Mistral, or any model.

import { createX402Server, createTokenPricing, MODEL_PRICING } from '@dexterai/x402/server';

const server = createX402Server({ payTo: '...', network: '...' });
const pricing = createTokenPricing({
  model: 'gpt-4o-mini',  // Uses real OpenAI rates
  // minUsd: 0.001,      // Optional floor
  // maxUsd: 50.0,       // Optional ceiling
});

app.post('/api/chat', async (req, res) => {
  const { prompt, systemPrompt } = req.body;
  const paymentSig = req.headers['payment-signature'];

  if (!paymentSig) {
    const quote = pricing.calculate(prompt, systemPrompt);
    const requirements = await server.buildRequirements({
      amountAtomic: quote.amountAtomic,
      resourceUrl: req.originalUrl,
      description: `${quote.model}: ${quote.inputTokens.toLocaleString()} tokens`,
    });
    res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));
    res.setHeader('X-Quote-Hash', quote.quoteHash);
    return res.status(402).json({
      inputTokens: quote.inputTokens,
      usdAmount: quote.usdAmount,
      model: quote.model,
      tier: quote.tier,
    });
  }

  // Validate quote hasn't changed
  const quoteHash = req.headers['x-quote-hash'];
  if (!pricing.validateQuote(prompt, quoteHash)) {
    return res.status(400).json({ error: 'Prompt changed, re-quote required' });
  }

  const result = await server.settlePayment(paymentSig);
  if (!result.success) return res.status(402).json({ error: result.errorReason });

  const response = await openai.chat.completions.create({
    model: pricing.config.model,
    messages: [{ role: 'user', content: prompt }],
    max_completion_tokens: pricing.modelInfo.maxTokens,
  });

  res.json({ 
    response: response.choices[0].message.content,
    transaction: result.transaction,
  });
});

Available Models

import { MODEL_PRICING, getAvailableModels } from '@dexterai/x402/server';

// Get all models sorted by tier and price
const models = getAvailableModels();
// → [{ model: 'gpt-5-nano', inputRate: 0.05, tier: 'fast' }, ...]

// Check pricing for a specific model
MODEL_PRICING['gpt-4o-mini'];
// → { input: 0.15, output: 0.6, maxTokens: 4096, tier: 'fast' }

Supported tiers: fast, standard, reasoning, premium, custom

Custom Models (Anthropic, Gemini, etc.)

Not using OpenAI? Pass your own rates:

// Anthropic Claude
const pricing = createTokenPricing({
  model: 'claude-3-sonnet',
  inputRate: 3.0,    // $3.00 per 1M input tokens
  outputRate: 15.0,  // $15.00 per 1M output tokens
  maxTokens: 4096,
});

// Google Gemini
const pricing = createTokenPricing({
  model: 'gemini-1.5-pro',
  inputRate: 1.25,
  outputRate: 5.0,
});

// Custom/local model with custom tokenizer
const pricing = createTokenPricing({
  model: 'llama-3-70b',
  inputRate: 0.50,
  tokenizer: (text) => llamaTokenizer.encode(text).length,
});

tiktoken's default encoding works well for most transformer models. Only use a custom tokenizer if your model has significantly different tokenization.


Sponsored Access delivers targeted resource recommendations through x402 payments. When an agent pays for an API, the facilitator can inject a recommendation for a related tool in the settlement receipt. The agent sees the recommendation and can call it immediately -- the subsequent call is tracked as a conversion with both blockchain transaction hashes as proof.

Server — Enable Recommendation Injection

Add sponsoredAccess: true to your middleware config. This reads extensions["sponsored-access"] from the facilitator's settlement response and injects _x402_sponsored into the JSON response body so the agent's LLM can see the recommendations (headers are invisible to LLMs).

import { x402Middleware } from '@dexterai/x402/server';

// Default injection: adds _x402_sponsored field to JSON response
app.get('/api/data',
  x402Middleware({
    payTo: '...', amount: '0.01',
    sponsoredAccess: true,
  }),
  (req, res) => res.json({ data: 'content' })
);
// Agent receives: { _x402_sponsored: [{ resourceUrl, description, sponsor }], data: 'content' }

// Custom injection: control where recommendations appear
app.get('/api/data',
  x402Middleware({
    payTo: '...', amount: '0.01',
    sponsoredAccess: {
      inject: (body, recs) => ({ ...body, related_tools: recs }),
      onMatch: (recs, settlement) => {
        console.log(`Matched ${recs.length} recommendations for tx ${settlement.transaction}`);
      },
    },
  }),
  (req, res) => res.json({ data: 'content' })
);

Client — Read Recommendations

import {
  wrapFetch,
  getSponsoredRecommendations,
  fireImpressionBeacon,
} from '@dexterai/x402/client';

const x402Fetch = wrapFetch(fetch, { walletPrivateKey: key });
const response = await x402Fetch('https://api.example.com/data');

// Extract typed recommendations from the payment receipt
const recs = getSponsoredRecommendations(response);
if (recs) {
  for (const rec of recs) {
    console.log(`${rec.sponsor}: ${rec.description} -- ${rec.resourceUrl}`);
  }
  // Confirm delivery to the ad network
  await fireImpressionBeacon(response);
}

React — Recommendations in Hooks

import { useX402Payment } from '@dexterai/x402/react';

function PayButton() {
  const {
    fetch,
    isLoading,
    sponsoredRecommendations,  // auto-populated after payment
  } = useX402Payment({ wallets });

  return (
    <div>
      <button onClick={() => fetch(url)} disabled={isLoading}>Pay</button>
      {sponsoredRecommendations?.map((rec, i) => (
        <a key={i} href={rec.resourceUrl}>{rec.sponsor}: {rec.description}</a>
      ))}
    </div>
  );
}

Types

All types are re-exported from @dexterai/x402-ads-types:

import type { SponsoredRecommendation, SponsoredAccessSettlementInfo } from '@dexterai/x402/client';

interface SponsoredRecommendation {
  resourceUrl: string;    // The URL to call
  description: string;    // Agent-readable description
  sponsor: string;        // Brand name
  bazaarId?: string;      // Bazaar catalog ID
  price?: string;         // Cost in atomic units
  currency?: string;      // e.g., "USDC"
}

How It Works

  1. Agent pays for an API via x402
  2. Facilitator settles payment and calls the ad network's match API
  3. If a campaign matches (by URL pattern, category, network), a recommendation is injected into SettlementResponse.extensions["sponsored-access"]
  4. Publisher middleware (with sponsoredAccess: true) injects it into the JSON response body
  5. Agent's LLM sees the recommendation and can call the suggested resource
  6. If the agent calls it, the facilitator records a conversion with both tx hashes as proof

Want your API recommended to agents across the x402 network? Create and fund campaigns through the Agent API — no signup, no accounts. Your wallet is your identity.

// All campaign management is x402-gated at x402ads.io
const x402Fetch = wrapFetch(fetch, { walletPrivateKey: key });

// Create a campaign ($0.10 USDC)
const res = await x402Fetch('https://x402ads.io/v1/agent/campaigns', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Promote My API',
    rec_resource_url: 'https://api.example.com/data',
    rec_description: 'Real-time market data',
    rec_sponsor_name: 'Example Corp',
    target_categories: ['defi', 'data'],
    bid_strategy: 'cpa',
    max_bid_amount: '50000',
    budget_daily: '5000000',
  }),
});

Full advertiser guide: docs.dexter.cash/docs/sponsored-access/for-advertisers


API Reference

createX402Client(options)

Option Type Required Description
wallets { solana?, evm? } Yes Multi-chain wallets
wallet SolanaWallet No Single Solana wallet (legacy)
preferredNetwork string No Prefer this network when multiple options available
rpcUrls Record<string, string> No RPC endpoints per network (defaults to Dexter proxy)
maxAmountAtomic string No Maximum payment cap
verbose boolean No Enable debug logging

x402AccessPass(options)

Option Type Required Description
payTo string Yes Address to receive payments
tiers Record<string, string> One of tiers or ratePerHour Named tiers (e.g., { '1h': '0.50' })
ratePerHour string One of tiers or ratePerHour USD rate for custom durations
network string No CAIP-2 network (default: Solana mainnet)
secret Buffer No HMAC secret for JWT (auto-generated)
facilitatorUrl string No Facilitator URL (default: x402.dexter.cash)
verbose boolean No Enable debug logging

useX402Payment(options)

Returns:

Property Type Description
fetch function Payment-aware fetch
isLoading boolean Payment in progress
status string 'idle' | 'pending' | 'success' | 'error'
error X402Error? Error details if failed
transactionId string? Transaction signature
transactionUrl string? Block explorer link
balances Balance[] Token balances per chain
refreshBalances function Manual refresh
reset function Clear state
accessPass object? Active pass state (tier, expiresAt, remainingSeconds)

useAccessPass(options)

Option Type Required Description
wallets { solana?, evm? } Yes Multi-chain wallets
resourceUrl string Yes The x402 resource base URL
preferredNetwork string No Prefer this network
autoConnect boolean No Auto-fetch tiers on mount (default: true)

Returns:

Property Type Description
tiers AccessPassTier[]? Available tiers from server
pass object? Active pass (jwt, tier, expiresAt, remainingSeconds)
isPassValid boolean Whether pass is active and not expired
purchasePass function Buy a pass for a tier or custom duration
isPurchasing boolean Purchase in progress
fetch function Fetch with auto pass inclusion

Development

npm run build      # Build ESM + CJS
npm run dev        # Watch mode
npm run typecheck  # TypeScript checks

License

MIT — see LICENSE


Dexter Facilitator · OpenDexter Marketplace · Live Demo · Access Pass Demo · Become a Seller