SDK Documentation

@relai-fi/x402

Unified x402 payment SDK for Solana, Base, Polygon, Ethereum, Avalanche, Telos EVM, and SKALE Base.

Multi-chainZero gas feesAuto-detect signingWorks out of the box

The x402 protocol enables HTTP-native micropayments. When a server returns HTTP 402 Payment Required, it includes payment details in the response. The client signs a payment, retries the request, and the server settles the payment and returns the protected content. This SDK handles the entire flow automatically — call fetch() and payments happen transparently.

Zero gas fees. The RelAI facilitator sponsors gas — users only pay for content (USDC). Works on all supported networks.

Installation

Terminal
npm install @relai-fi/x402

Quick Start — Client

Create a payment-aware fetch client. Works in the browser and Node.js:

client.ts
import { createX402Client } from '@relai-fi/x402/client';

const client = createX402Client({
  wallets: {
    solana: solanaWallet,  // @solana/wallet-adapter compatible
    evm: evmWallet,        // wagmi/viem compatible
  },
});

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

React Hook

Works with @solana/wallet-adapter-react and wagmi:

PayButton.tsx
import { useRelaiPayment } from '@relai-fi/x402/react';
import { useWallet } from '@solana/wallet-adapter-react';
import { useAccount, useSignTypedData } from 'wagmi';

function PayButton() {
  const solanaWallet = useWallet();
  const { address } = useAccount();
  const { signTypedDataAsync } = useSignTypedData();

  const {
    fetch,
    isLoading,
    status,
    transactionUrl,
    transactionNetworkLabel,
  } = useRelaiPayment({
    wallets: {
      solana: solanaWallet,
      evm: address
        ? { address, signTypedData: signTypedDataAsync }
        : undefined,
    },
  });

  return (
    <div>
      <button onClick={() => fetch('/api/protected')} disabled={isLoading}>
        {isLoading ? 'Paying...' : 'Access API'}
      </button>
      {transactionUrl && (
        <a href={transactionUrl} target="_blank">
          View on {transactionNetworkLabel}
        </a>
      )}
    </div>
  );
}

Supported Networks

All networks use USDC with 6 decimals. Gas fees are sponsored by the facilitator (RelAI or 0xGasless).

NetworkIdentifier (v1)CAIP-2 (v2)Signing Method
Solanasolanasolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpSPL transfer + fee payer · smart wallet ready
Basebaseeip155:8453EIP-3009 transferWithAuthorization
Avalancheavalancheeip155:43114EIP-3009 transferWithAuthorization
Telos EVMteloseip155:40Standard ERC-20 transfer (no EIP-3009)
Avalanche (0xGasless)avalancheeip155:43114Custom EIP-712 (A402 domain)
Polygonpolygoneip155:137EIP-3009 transferWithAuthorization
Ethereumethereumeip155:1EIP-3009 transferWithAuthorization
SKALE Baseskale-baseeip155:1187947933EIP-3009 transferWithAuthorization

0xGasless auto-detection. When the server returns relayerContract in payment requirements, the SDK automatically switches to the 0xGasless EIP-712 domain (name: "A402", verifyingContract: relayerContract) and sets validAfter: 0. No client code changes needed. Learn more →

Both formats accepted. The SDK accepts v1 simple names (base, polygon) and v2 CAIP-2 identifiers (eip155:8453, eip155:137). Use networkV1ToV2() and networkV2ToV1() to convert between them.

createX402Client(config)

Creates a fetch wrapper that automatically handles 402 Payment Required responses.

OptionTypeDefaultDescription
wallets{ solana?, evm? }{}Wallet adapters for each chain
facilitatorUrlstringRelAI facilitatorCustom facilitator endpoint
preferredNetworkRelaiNetworkPrefer this network when multiple accepts
solanaRpcUrlstringmainnet-betaSolana RPC endpoint
evmRpcUrlsRecord<string, string>Built-inRPC URLs per network name
maxAmountAtomicstringSafety cap on payment amount
verbosebooleanfalseLog payment flow to console

Wallet Interfaces

// Solana — compatible with @solana/wallet-adapter-react useWallet()
// Also works with smart wallets: Squads, Crossmint, SWIG, on-chain agents
interface SolanaWallet {
  publicKey: { toString(): string } | null;
  signTransaction: ((tx: unknown) => Promise<unknown>) | null;
}

// EVM — pass address + signTypedData from wagmi
interface EvmWallet {
  address: string;
  signTypedData: (params: {
    domain: Record<string, unknown>;
    types: Record<string, unknown[]>;
    message: Record<string, unknown>;
    primaryType: string;
  }) => Promise<string>;
}

Smart Wallets (Solana)

The SDK fully supports Solana smart wallets — including Squads multisig, Crossmint, SWIG session keys, and on-chain agents. Any wallet that implements publicKey + signTransaction works out of the box. No SDK changes are needed on the client side.

How it works

Smart wallets on Solana (e.g. Squads vault PDAs, Crossmint MPC wallets) use off-curve public keys — addresses that are derived from a program, not a private key. The SDK handles this transparently: Associated Token Accounts are resolved with allowOffCurve: true, so PDA-based signers work the same as regular keypairs. The smart wallet's own signTransaction callback handles its internal signing logic (multisig CPI, MPC, session key delegation, etc.).

Squads multisig example
import { createX402Client } from '@relai-fi/x402/client';
import { useSquadsWallet } from '@squads-protocol/sdk'; // example

// Squads wallet implements publicKey + signTransaction — pass directly
const squadsWallet = useSquadsWallet();

const client = createX402Client({
  wallets: { solana: squadsWallet },
});

// Payment signed by the Squads multisig — no extra config needed
const response = await client.fetch('https://api.example.com/protected');
Crossmint example (server-side / agent)
import { createCrossmintX402Fetch } from '@relai-fi/x402/crossmint';
import { Connection } from '@solana/web3.js';

// Crossmint signs and broadcasts — no signTransaction needed
const fetch402 = createCrossmintX402Fetch({
  apiKey: process.env.CROSSMINT_API_KEY,   // sk_production_... or sk_staging_...
  wallet: process.env.CROSSMINT_WALLET,    // Crossmint smart wallet address
  connection: new Connection(process.env.SOLANA_RPC_URL),
  onPayment: (txHash) => console.log('On-chain tx:', txHash),
});

// RelAI sponsors SOL gas — wallet only needs USDC
const response = await fetch402('https://api.example.com/protected');
const data = await response.json();

Get a Crossmint API key at console.crossmint.com. Required scopes: wallets.read, wallets.create, wallets:transactions.create, wallets:transactions.sign. Full working example: crossmint-relai-x402.

Supported smart wallet providers

ProviderTypeNotes
Squads ProtocolMultisig PDAn-of-m signing via CPI — works if vault has USDC ATA
CrossmintMPC / embeddedImplements standard wallet-adapter interface
SWIGSession key delegationScoped signing with time/amount limits
On-chain agentsProgram-controlled PDAAny agent holding USDC and signing SPL transfers
Privy / TurnkeyMPC custodialServer-side signing — key never leaves MPC threshold

Requirement. The smart wallet's ATA for USDC must exist on-chain before the first payment. For Squads vaults, this means the vault PDA must have a funded USDC Associated Token Account. Regular spl-token create-account or any ATA creation UI works.

useRelaiPayment(config)

React hook wrapping createX402Client with state management. Config is the same as above.

PropertyTypeDescription
fetch(input, init?) => Promise<Response>Payment-aware fetch
isLoadingbooleanPayment in progress
status'idle' | 'pending' | 'success' | 'error'Current state
errorError | nullError details on failure
transactionIdstring | nullTx hash/signature on success
transactionUrlstring | nullBlock explorer link
transactionNetworkLabelstring | nullHuman-readable label (e.g. "Base")
connectedChains{ solana, evm }Which wallets are connected
reset() => voidReset state to idle

Server SDK (Express)

Protect any Express route with x402 micropayments:

server.js
import Relai from '@relai-fi/x402/server';

const relai = new Relai({
  network: 'base', // or 'solana', 'avalanche', 'skale-base'
});

// Protect any Express route with micropayments
app.get('/api/data', relai.protect({
  payTo: '0xYourWallet',
  price: 0.01,  // $0.01 USDC
  description: 'Premium data access',
}), (req, res) => {
  // req.payment = { verified, transactionId, payer, network, amount }
  res.json({ data: 'Protected content', payment: req.payment });
});

// Dynamic pricing
app.get('/api/premium', relai.protect({
  payTo: '0xYourWallet',
  price: (req) => req.query.tier === 'pro' ? 0.10 : 0.01,
}), handler);

// Per-endpoint network override
app.get('/api/solana-data', relai.protect({
  payTo: 'SolanaWalletAddress',
  price: 0.005,
  network: 'solana',
}), handler);

req.payment

FieldTypeDescription
verifiedbooleanAlways true after settlement
transactionIdstringOn-chain transaction hash
payerstringPayer wallet address
networkstringNetwork name (e.g., base)
amountnumberPrice in USD

Stripe PayTo

Accept x402 payments directly into your Stripe Dashboard as USD - no crypto knowledge required. The stripePayTo() helper creates a Stripe PaymentIntent with a crypto deposit address per request. Payments settle on Base (USDC) and Stripe converts them to USD automatically.

3 lines of codeSettles in USDAuto Base network
server.js
import Relai, { stripePayTo } from '@relai-fi/x402/server';

const relai = new Relai({ network: 'base' });

app.get('/api/data', relai.protect({
  price: 0.01,
  payTo: stripePayTo(process.env.STRIPE_SECRET_KEY),
}), (req, res) => {
  res.json({ data: 'paid content' });
});

How It Works

1
Agent Request
GET /api/data
2
Stripe PI
Deposit address created
3
USDC on Base
Payment settled on-chain
4
Stripe Dashboard
Settled in USD

Each 402 response creates a fresh Stripe PaymentIntent with payment_method_types: ["crypto"]. The deposit address from next_action.crypto_collect_deposit_details is returned as payTo. When the client pays, Stripe auto-captures the PI and settles as USD.

stripePayTo(secretKey, options?)

ParameterTypeDefaultDescription
secretKeystring-Stripe secret key (sk_live_... or sk_test_...)
options.networkstringbaseStripe crypto deposit network

Requirements. Your Stripe account needs Crypto payins enabled. Store your STRIPE_SECRET_KEY in environment variables - never hardcode it.

Utilities

import { toAtomicUnits, fromAtomicUnits } from '@relai-fi/x402/utils';

toAtomicUnits(0.05, 6);       // '50000'   ($0.05 USDC)
toAtomicUnits(1.50, 6);       // '1500000' ($1.50 USDC)

fromAtomicUnits('50000', 6);  // 0.05
fromAtomicUnits('1500000', 6); // 1.5

Payload Conversion (v1 ↔ v2)

import { convertV1ToV2, convertV2ToV1, networkV1ToV2 } from '@relai-fi/x402/utils';

networkV1ToV2('solana');     // 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'
networkV1ToV2('base');       // 'eip155:8453'
networkV1ToV2('avalanche');  // 'eip155:43114'
networkV1ToV2('skale-base'); // 'eip155:1187947933'

const v2Payload = convertV1ToV2(v1Payload);
const v1Payload = convertV2ToV1(v2Payload);

Package Exports

// Client — browser & Node.js fetch wrapper with automatic 402 handling
import { createX402Client } from '@relai-fi/x402/client';

// React hook — state management + wallet integration
import { useRelaiPayment } from '@relai-fi/x402/react';

// Server — Express middleware for protecting endpoints
import Relai, { stripePayTo } from '@relai-fi/x402/server';

// Utilities — payload conversion, unit helpers
import {
  convertV1ToV2, convertV2ToV1,
  networkV1ToV2, networkV2ToV1,
  toAtomicUnits, fromAtomicUnits,
} from '@relai-fi/x402/utils';

// Types & constants
import {
  RELAI_NETWORKS, CHAIN_IDS, USDC_ADDRESSES,
  NETWORK_CAIP2, EXPLORER_TX_URL,
  type RelaiNetwork, type SolanaWallet,
  type EvmWallet, type WalletSet,
} from '@relai-fi/x402';

How It Works

Client                        Server                     Facilitator
  |                             |                             |
  |── GET /api/data ──────────>|                             |
  |<── 402 Payment Required ───|                             |
  |   (accepts: network, amount, asset)                      |
  |                             |                             |
  | SDK signs payment           |                             |
  | (EIP-3009/SPL)              |                             |
  |                             |                             |
  |── GET /api/data ──────────>|                             |
  |   X-PAYMENT: <signed>      |── settle ─────────────────>|
  |                             |<── tx hash ────────────────|
  |<── 200 OK + data ─────────|                             |
  |   PAYMENT-RESPONSE: <tx>   |                             |
RelAI© 2026 RelAI. Monetize APIs with x402 Protocol.