SDK Documentation

Integrate Conx402 into your API in minutes

Quick Start

Three steps to monetize any API with x402 payments.

01 — Install

npm install conx402

02 — Register as a Provider

import { registryAbi, CHAINS } from 'conx402';

// Choose scaffold: Gaming(0), DeFi(1), AI(2), Data(3), Media(4), Identity(5)
const tx = await walletClient.writeContract({
  address: CHAINS[84532].registry,      // Base Sepolia
  abi: registryAbi,
  functionName: 'registerProvider',
  args: [
    2,                                   // AI scaffold
    'https://api.example.com',           // Your endpoint
    walletAddress,                       // Where payments go
  ],
});

// Custom pricing (optional):
const tx2 = await walletClient.writeContract({
  address: CHAINS[84532].registry,
  abi: registryAbi,
  functionName: 'registerProviderCustom',
  args: [
    2,                                   // AI scaffold
    'https://api.example.com',
    walletAddress,
    10_000n,                             // $0.01 per call
    0n,                                  // No session pricing
    0,                                   // No session duration
  ],
});

03 — Protect Your API

import { conx402Protect } from 'conx402/server';

// Hono middleware — returns 402 for unpaid requests
app.use('/api/*', conx402Protect({
  providerId: 1n,
  preferredChains: [84532, 421614],      // Base + Arb Sepolia
  defaultPrice: 10_000n,                 // $0.01 USDC (6 decimals)
  freePaths: ['/health', '/status'],     // No payment required
  receiptCache: {
    maxSize: 1000,
    ttlSeconds: 3600,                    // Cache verified receipts 1h
  },
}));

// Protected route — only reached after payment
app.get('/api/generate', (c) => {
  const receipt = c.get('conx402Receipt');
  return c.json({ data: '...', receipt });
});

04 — Consumer Pays

import { createConx402Client } from 'conx402/client';

const client = createConx402Client({
  privateKey: '0x...',                   // Consumer wallet
  preferredChainId: 84532,               // Pay on Base Sepolia
});

// Automatic: request → 402 → pay on-chain → retry → 200
const response = await client.fetch('https://api.example.com/api/generate');
const data = await response.json();

// Payment info available:
console.log(response.paymentInfo);
// { nonce, txHash, chainId, amount, providerId, blockNumber }

Server Middleware

conx402Protect() is a Hono middleware that gates API routes behind x402 USDC payments.

Configuration

OptionTypeDescription
providerIdbigintYour on-chain provider ID (required)
preferredChainsnumber[]Chain IDs where you accept payment
defaultPricebigintDefault USDC price per call (6 decimals)
freePathsstring[]Paths that bypass payment
pricingRecord<string, bigint>Per-route pricing overrides
receiptCache{ maxSize, ttlSeconds }Cache verified receipts to reduce RPC calls
onPaymentVerified(receipt) => voidCallback after successful payment verification

402 Response Format

When a consumer hits a protected route without a valid receipt, the middleware returns:

HTTP/1.1 402 Payment Required
x-conx402-payment: <base64-encoded JSON>
Content-Type: application/json

// Decoded header:
{
  "version": "1",
  "providerId": "1",
  "price": "10000",               // $0.01 USDC
  "chains": [
    {
      "chainId": 84532,
      "router": "0x24e2...",
      "pool": "0xeeFc...",
      "usdc": "0x036C..."
    }
  ],
  "payTo": "0xBe2C...",
  "endpoint": "https://api.example.com/api/generate"
}

Client Library

createConx402Client() returns an enhanced fetch that auto-handles x402 payment flows.

Flow

  1. Consumer calls client.fetch(url)
  2. Server returns 402 with payment requirements header
  3. Client parses requirements, selects chain (consumer preference or provider default)
  4. Client calls Router.pay() on-chain with generated nonce
  5. Client waits for block confirmation (+ 3s propagation delay on testnets)
  6. Client retries original request with x-conx402-receipt header
  7. Server verifies receipt on-chain, returns 200

Gateway

import { createGateway } from 'conx402';

// Server-side payment executor (requires Router.setGateway())
const gateway = createGateway({
  privateKey: '0x...',
  chainIds: [84532, 421614],
});

// Execute payment on behalf of a consumer
await gateway.pay({
  chainId: 84532,
  consumer: '0x...',
  providerId: 1n,
  amount: 10_000n,
  nonce: '0x...',
});

// Verify payment receipt
const receipt = await gateway.verifyPayment(84532, nonce);

Payment Flow

How a single API call is paid for, end-to-end.

Consumer                  Provider API              Router Contract
   │                          │                          │
   ├─── GET /api/data ───────→│                          │
   │                          ├── No receipt? ──→ 402    │
   │◄── 402 + requirements ───┤                          │
   │                          │                          │
   ├── Parse requirements     │                          │
   ├── Select chain           │                          │
   ├── Generate nonce         │                          │
   │                          │                          │
   ├── Router.pay(consumer, providerId, amount, nonce) ─→│
   │                          │         ┌── Debit Pool ──┤
   │                          │         ├── Pay Provider  │
   │                          │         ├── Pay Protocol  │
   │                          │         ├── Log Receipt   │
   │◄── tx confirmed ─────────┼─────────┘                │
   │                          │                          │
   ├─── GET /api/data ───────→│                          │
   │    + receipt nonce       ├── Verify on-chain ──────→│
   │                          │◄── Receipt valid ────────┤
   │◄── 200 + data ───────────┤                          │
   │                          │                          │

Contract Addresses

Same addresses on Base, Arb, OP Sepolia, and Avax Fuji. Eth Sepolia has different addresses.

ContractAddress (shared)Eth Sepolia
registry0xACAf06fd443e9890AfCf5b5305605272E9759dc40xeeFc3DdCbf23c682782581FB9d04B03DCA332d28
pool0xeeFc3DdCbf23c682782581FB9d04B03DCA332d280x873830D10E06b6BE85337B50D6b4b76E9f79Cf1F
router0x24e26901b6eD5288D1950f78AF24e8e38Efc1a3c0xEF8249592397b9CD42F7d5a687858f9df200047c
receipts0x873830D10E06b6BE85337B50D6b4b76E9f79Cf1F0x24e26901b6eD5288D1950f78AF24e8e38Efc1a3c

Settlement Contracts

ChainSettlement AddressCCTP Domain
Base Sepolia0x39DbBa2CdAF7F668816957B023cbee1841373F5b6
Arb Sepolia0x39DbBa2CdAF7F668816957B023cbee1841373F5b3

Industry Scaffolds

Pre-configured pricing defaults by industry. Override any field at registration with registerProviderCustom().

Gaming

Scaffold 0

DeFi

Scaffold 1

AI

Scaffold 2

Data

Scaffold 3

Media

Scaffold 4

Identity

Scaffold 5

ScaffoldPer CallSessionDurationFee
Gaming$0.001$0.501h1.5%
DeFi$0.005$5.0030d1.5%
AI$0.01--1.5%
Data$0.005--1.5%
Media$0.05--2.0%
Identity$0.02--1.5%

Cross-Chain Settlement

Providers can receive USDC on a different chain than where consumers pay. Settlement uses Circle CCTP for trustless cross-chain USDC transfer.

Setup

import { settlementAbi, CHAINS } from 'conx402';

// 1. Set your provider's payTo to the Settlement contract
await walletClient.writeContract({
  address: CHAINS[84532].registry,
  abi: registryAbi,
  functionName: 'updateProvider',
  args: [providerId, endpointUrl, CHAINS[84532].settlement],
});

// 2. Configure where you want to receive USDC
await walletClient.writeContract({
  address: CHAINS[84532].settlement,
  abi: settlementAbi,
  functionName: 'setProviderConfig',
  args: [
    providerId,
    6,                              // CCTP domain 6 = Base
    '0xYourWalletOnBase',           // Receive address on Base
  ],
});

CCTP Domain IDs

Ethereum0
Avalanche1
Optimism2
Arbitrum3
Base6

Settlement Flow

  1. Consumer pays on Chain A via Router.pay()
  2. Router sends providerShare to Settlement contract
  3. Gateway calls Settlement.bridge()
  4. If same chain: direct USDC transfer (no CCTP needed)
  5. If cross-chain: Settlement calls CCTP TokenMessenger.depositForBurn()
  6. Relayer polls Circle API for attestation (~5-15 min)
  7. Relayer calls MessageTransmitter.receiveMessage() on Chain B
  8. USDC minted to provider's wallet on Chain B

Budget Controls

Consumers can set spending limits to protect against runaway costs.

import { poolAbi, CHAINS } from 'conx402';

// Deposit USDC into your pool (requires prior ERC-20 approval)
await walletClient.writeContract({
  address: CHAINS[84532].pool,
  abi: poolAbi,
  functionName: 'deposit',
  args: [1_000_000n],                   // $1.00 USDC
});

// Set daily limit ($0.50/day) and per-call limit ($0.05)
await walletClient.writeContract({
  address: CHAINS[84532].pool,
  abi: poolAbi,
  functionName: 'setBudget',
  args: [500_000n, 50_000n],            // 0 = unlimited
});

// Check balance
const balance = await publicClient.readContract({
  address: CHAINS[84532].pool,
  abi: poolAbi,
  functionName: 'balanceOf',
  args: [consumerAddress],
});

// Withdraw
await walletClient.writeContract({
  address: CHAINS[84532].pool,
  abi: poolAbi,
  functionName: 'withdraw',
  args: [500_000n],                     // $0.50 USDC
});

Budgets reset daily at 00:00 UTC. Set limits to 0 for unlimited. The Pool contract enforces limits at the contract level — overspend reverts with DailyLimitExceeded or PerCallLimitExceeded.

Error Handling

Contract reverts and their meaning.

ErrorContractCause
NonceAlreadyUsedRouterPayment nonce has already been processed
ProviderNotActiveRouterProvider is paused or does not exist
InsufficientPaymentRouterAmount below provider minimum price
InvalidGatewayRouterCaller not registered as gateway
DailyLimitExceededPoolConsumer daily spending limit reached
PerCallLimitExceededPoolSingle payment exceeds per-call limit
InsufficientBalancePoolConsumer pool balance too low
NoSessionPricingRouterProvider has no session pricing configured
FeeTooHighRegistryFee exceeds 10% (1000 bps) maximum

Events Reference

Key events emitted by the protocol contracts.

PaymentExecutedRouter

nonce (indexed), consumer (indexed), providerId (indexed), amount

DepositedPool

consumer (indexed), amount, newBalance

WithdrawnPool

consumer (indexed), amount, newBalance

ProviderRegisteredRegistry

providerId (indexed), owner (indexed), scaffold, endpointUrl

SettlementInitiatedSettlement

conx402Nonce (indexed), providerId (indexed), amount, destDomain, destAddress, cctpNonce, messageHash

PaymentProcessedReceipts

nonce (indexed), consumer (indexed), providerId (indexed), amount, providerShare, protocolFee, timestamp

Conx402 — Universal x402 Payment Infrastructure