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
| Option | Type | Description |
|---|---|---|
providerId | bigint | Your on-chain provider ID (required) |
preferredChains | number[] | Chain IDs where you accept payment |
defaultPrice | bigint | Default USDC price per call (6 decimals) |
freePaths | string[] | Paths that bypass payment |
pricing | Record<string, bigint> | Per-route pricing overrides |
receiptCache | { maxSize, ttlSeconds } | Cache verified receipts to reduce RPC calls |
onPaymentVerified | (receipt) => void | Callback 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
- Consumer calls
client.fetch(url) - Server returns
402with payment requirements header - Client parses requirements, selects chain (consumer preference or provider default)
- Client calls
Router.pay()on-chain with generated nonce - Client waits for block confirmation (+ 3s propagation delay on testnets)
- Client retries original request with
x-conx402-receiptheader - 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.
| Contract | Address (shared) | Eth Sepolia |
|---|---|---|
| registry | 0xACAf06fd443e9890AfCf5b5305605272E9759dc4 | 0xeeFc3DdCbf23c682782581FB9d04B03DCA332d28 |
| pool | 0xeeFc3DdCbf23c682782581FB9d04B03DCA332d28 | 0x873830D10E06b6BE85337B50D6b4b76E9f79Cf1F |
| router | 0x24e26901b6eD5288D1950f78AF24e8e38Efc1a3c | 0xEF8249592397b9CD42F7d5a687858f9df200047c |
| receipts | 0x873830D10E06b6BE85337B50D6b4b76E9f79Cf1F | 0x24e26901b6eD5288D1950f78AF24e8e38Efc1a3c |
Settlement Contracts
| Chain | Settlement Address | CCTP Domain |
|---|---|---|
| Base Sepolia | 0x39DbBa2CdAF7F668816957B023cbee1841373F5b | 6 |
| Arb Sepolia | 0x39DbBa2CdAF7F668816957B023cbee1841373F5b | 3 |
Industry Scaffolds
Pre-configured pricing defaults by industry. Override any field at registration with registerProviderCustom().
Scaffold 0
Scaffold 1
Scaffold 2
Scaffold 3
Scaffold 4
Scaffold 5
| Scaffold | Per Call | Session | Duration | Fee |
|---|---|---|---|---|
| Gaming | $0.001 | $0.50 | 1h | 1.5% |
| DeFi | $0.005 | $5.00 | 30d | 1.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
Settlement Flow
- Consumer pays on Chain A via
Router.pay() - Router sends providerShare to Settlement contract
- Gateway calls
Settlement.bridge() - If same chain: direct USDC transfer (no CCTP needed)
- If cross-chain: Settlement calls CCTP TokenMessenger.depositForBurn()
- Relayer polls Circle API for attestation (~5-15 min)
- Relayer calls
MessageTransmitter.receiveMessage()on Chain B - 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.
| Error | Contract | Cause |
|---|---|---|
NonceAlreadyUsed | Router | Payment nonce has already been processed |
ProviderNotActive | Router | Provider is paused or does not exist |
InsufficientPayment | Router | Amount below provider minimum price |
InvalidGateway | Router | Caller not registered as gateway |
DailyLimitExceeded | Pool | Consumer daily spending limit reached |
PerCallLimitExceeded | Pool | Single payment exceeds per-call limit |
InsufficientBalance | Pool | Consumer pool balance too low |
NoSessionPricing | Router | Provider has no session pricing configured |
FeeTooHigh | Registry | Fee exceeds 10% (1000 bps) maximum |
Events Reference
Key events emitted by the protocol contracts.
PaymentExecutedRouternonce (indexed), consumer (indexed), providerId (indexed), amount
DepositedPoolconsumer (indexed), amount, newBalance
WithdrawnPoolconsumer (indexed), amount, newBalance
ProviderRegisteredRegistryproviderId (indexed), owner (indexed), scaffold, endpointUrl
SettlementInitiatedSettlementconx402Nonce (indexed), providerId (indexed), amount, destDomain, destAddress, cctpNonce, messageHash
PaymentProcessedReceiptsnonce (indexed), consumer (indexed), providerId (indexed), amount, providerShare, protocolFee, timestamp
Conx402 — Universal x402 Payment Infrastructure