Gate402DOCS
RepositoryDashboard
v0.1.0MITSolanax402

Gate402

Billing infrastructure for AI agents. Drop-in middleware that adds a paywall to any HTTP API or MCP server. Agents pay in USDC on Solana — no banks, no credit cards, no human intervention.

npm install gate402
For API Developers
Add a paywall to any Express API
npm install gate402-agent
For Agent Operators
Pay APIs automatically on HTTP 402
npm install create-gate402-mcp
For MCP Developers
Monetize any MCP tool call
Gate402 is MIT licensed. The npm packages are free forever. The hosted platform at gate402.dev is the commercial offering.

How it works

Every payment goes through 5 steps. Total time: under one second.

payment flow
# Step 1 — Agent calls your API
$ GET https://your-api.dev/api/data
# Step 2 — Gate402 returns HTTP 402
HTTP/1.1 402 Payment Required
{ "price": { "total": 0.001, "currency": "USDC", "network": "solana-mainnet" },
"splits": { "provider": { "wallet": "YOUR_WALLET", "amount": 0.001 } },
"endpoint": "/api/data", "instructions": "Send USDC on Solana and include tx hash in X-Payment-Payload" }
# Step 3 — Agent sends USDC on Solana (~400ms)
Transaction confirmed: 5kWq9mLP3rTxHJzUvBnCs...
# Step 4 — Agent retries with payment proof
$ GET https://your-api.dev/api/data
X-Payment-Payload: 5kWq9mLP3rTxHJzUvBnCs...
# Step 5 — Gate402 verifies on-chain and releases
Payment verified ✓ — handler executing
HTTP/1.1 200 OK
{ "data": "your response here" }

Quick start

6 steps to your first paid API call — no credit card, no bank account.

01
Create account
Sign in at gate402.dev with GitHub or email. Copy your API key from Settings → API Key.
credentials
# Your API key looks like this:
GATE402_API_KEY=7d40dc5a-c0a9-49ac-b87c-89af2267ba32
02
Install
bash
npm install gate402
03
Add 3 lines to your Express app
typescript
import { gate402 } from 'gate402'
import express from 'express'

const app = express()

app.use(gate402({
  apiKey: process.env.GATE402_API_KEY,
  serverUrl: 'https://api.gate402.dev',
  endpoints: {
    '/api/data': 0.001  // $0.001 per call
  }
}))

// Your existing handlers don't change
app.get('/api/data', (req, res) => {
  res.json({ message: 'You paid 0.001 USDC!' })
})

app.listen(3000)
04
Test in devnet (no real money)
terminal
# Without payment — returns 402
$ curl http://localhost:3000/api/data
402 Payment Required
# With demo payment — works instantly, no blockchain
$ curl http://localhost:3000/api/data \
-H "X-Payment-Payload: demo_test_001"
200 OK — { "message": "You paid 0.001 USDC!" }
05
Test with a real agent
typescript
import { Gate402Agent } from 'gate402-agent'

const agent = new Gate402Agent({
  privateKey: process.env.AGENT_WALLET_KEY,
  network: 'devnet'
})

// Detects 402, pays automatically, retries
const res = await agent.fetch('http://localhost:3000/api/data')
const data = await res.json()
// { "message": "You paid 0.001 USDC!" }
06
See it in the dashboard
Open gate402.dev/dashboard — every call appears in real time with endpoint, amount, and status (demo on devnet, verified on mainnet).

Core concepts

x402 Protocol

HTTP 402 Payment Required is a status code defined in 1991. The x402 protocol defines how to use it for machine-to-machine payments. Backed by Google, Microsoft, Stripe, Coinbase, and Cloudflare.

Fee split

During early access, Gate402 charges no platform fee. You receive 100% of every payment directly in your wallet.

Gate402 takes 0% platform fee during early access. 100% of every payment goes directly to your wallet. This will change when we exit early access — you will be notified in advance.

Demo mode

demo mode
# Any hash starting with demo_ bypasses blockchain
$ curl /api/data -H "X-Payment-Payload: demo_any_string"
Works on devnet — blocked automatically on mainnet

Demo mode is controlled by the network setting in your Gate402 dashboard, not by NODE_ENV.

On devnet: demo_ hashes are accepted for development. On mainnet: demo_ hashes are rejected with DEMO_NOT_ALLOWED_ON_MAINNET. Switch your network in gate402.dev/settings — Gate402 blocks demo payments on mainnet automatically.

Installation

Requirements

Node.js 18+
Express 4+
Gate402 account at gate402.dev
Solana wallet (Phantom, Backpack, or any)
install
# npm
$ npm install gate402
# yarn
$ yarn add gate402
# pnpm
$ pnpm add gate402

Basic setup

typescript
import { gate402 } from 'gate402'
import express from 'express'

const app = express()

app.use(gate402({
  apiKey: process.env.GATE402_API_KEY,
  walletAddress: process.env.SOLANA_WALLET,  // optional — uses dashboard setting
  serverUrl: 'https://api.gate402.dev',
  network: 'devnet',   // 'devnet' | 'mainnet'
  endpoints: {
    '/api/search':    0.001,   // $0.001 per call
    '/api/analyze':   0.010,   // $0.010 per call
    '/api/generate':  0.050,   // $0.050 per call
  }
}))

// Your handlers don't change at all
app.get('/api/search', (req, res) => {
  res.json({ results: ['...'] })
})

app.listen(3000)
PropertyTypeReqDefaultDescription
apiKeystringYesAPI key from gate402.dev/settings
walletAddressstringNoSolana wallet to receive USDC. Falls back to dashboard setting.
serverUrlstringYesGate402 API URL for payment verification
network'devnet' | 'mainnet'No'devnet'Solana network for on-chain verification
endpointsRecord<string, number>NoMap of URL paths to prices in USDC. Omit to use managed mode.

Going to mainnet

When you're ready to receive real USDC payments, follow these steps.

01
Configure your Solana wallet
Go to gate402.dev/settings → Receiving Wallet. Add your Solana mainnet wallet address (Phantom, Backpack, or any Solana wallet).
02
Switch to mainnet
Go to gate402.dev/settings → Network → Mainnet. Or via API:
bash
curl -X PATCH https://api.gate402.dev/api/users/network \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"network": "mainnet"}'
03
Configure Helius RPC (recommended)
The public Solana RPC has rate limits. For production, use a dedicated RPC like Helius (free tier available). Go to helius.dev → create account → copy your RPC URL → paste in gate402.dev/settings.
04
Test with real USDC
typescript
import { Gate402Agent } from 'gate402-agent'

const agent = new Gate402Agent({
  privateKey: process.env.AGENT_WALLET_KEY,
  network: 'mainnet',
  limits: { maxPerCall: 0.01, maxPerDay: 1.00 }
})

const res = await agent.fetch('https://your-api.dev/api/data')
05
Monitor in dashboard
Every payment appears in gate402.dev/dashboard in real time with status "verified" (on-chain confirmed) and the tx hash on Solscan.
Always test thoroughly on devnet before switching to mainnet. Demo payments are blocked on mainnet automatically.

Endpoint pricing

Each entry in endpoints maps a URL path to a price in USDC. Prices can be as low as 0.0001 USDC ($0.0001).

typescript
app.use(gate402({
  apiKey: '...',
  walletAddress: '...',
  endpoints: {
    '/api/basic':    0.0001,  // $0.0001 — micro tier
    '/api/standard': 0.001,   // $0.001  — standard
    '/api/premium':  0.010,   // $0.01   — premium
    '/api/bulk':     0.100,   // $0.10   — bulk
  }
}))
Gate402 takes 0% platform fee during early access. 100% of each payment goes directly to your wallet. No monthly fees on the free tier.

Managed mode

Fetch prices from the dashboard automatically. Change prices without redeploying your API.

typescript
// No endpoints config — everything fetched from dashboard
app.use(gate402({
  apiKey: process.env.GATE402_API_KEY,
  serverUrl: 'https://api.gate402.dev'
}))
Prices are cached with Redis for 60 seconds. Add endpoints at gate402.dev/dashboard → Endpoints.

Token Metering

Charge per token consumed. Ideal for LLM-powered APIs.

typescript
import { gate402, tokenMeter } from 'gate402'

// 1. Charge minimum entry fee upfront
app.use('/api/chat', gate402({
  apiKey: process.env.GATE402_API_KEY,
  endpoints: { '/api/chat': 0.001 }
}))

// 2. Measure actual tokens after execution
app.use('/api/chat', tokenMeter({
  pricePerToken: 0.000001,   // $0.000001 per token
  serverUrl: 'https://api.gate402.dev',
  apiKey: process.env.GATE402_API_KEY,
  tokenCounter: (req, res) => res.locals.tokensUsed || 0
}))

app.post('/api/chat', async (req, res) => {
  const response = await callOpenAI(req.body.message)
  res.locals.tokensUsed = response.usage.total_tokens
  res.json({ reply: response.text })
  // _billing metadata automatically added to response
})

Response with billing metadata

json
{
  "reply": "Hello! How can I help?",
  "_billing": {
    "type": "token",
    "tokensUsed": 42,
    "pricePerToken": 0.000001,
    "totalCost": 0.000042,
    "currency": "USDC",
    "settleAt": "https://api.gate402.dev/api/metering/settle"
  }
}

Compute Metering

Charge per millisecond of execution. Ideal for heavy compute APIs.

typescript
import { gate402, computeMeter } from 'gate402'

app.use('/api/process', gate402({
  apiKey: process.env.GATE402_API_KEY,
  endpoints: { '/api/process': 0.001 }
}))

app.use('/api/process', computeMeter({
  pricePerMs: 0.0000001,   // $0.0000001 per millisecond
  serverUrl: 'https://api.gate402.dev',
  apiKey: process.env.GATE402_API_KEY,
}))

app.post('/api/process', async (req, res) => {
  const result = await heavyComputation(req.body.data)
  res.json({ result })
  // _billing: { computeMs: 342, totalCost: 0.0000342 }
})

Webhooks

Receive a POST request after each confirmed payment.

Setup

Go to gate402.dev/settings → Webhooks → Add URL and webhook secret. Or via API:

bash
curl -X PATCH https://api.gate402.dev/api/users/webhook \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"webhookUrl": "https://your-app.com/webhook", "webhookSecret": "your-secret"}'

Headers sent with every webhook

bash
X-Gate402-Signature: sha256=<hmac>
X-Gate402-Event: payment.confirmed
X-Gate402-Timestamp: 2026-05-27T01:04:51.630Z
User-Agent: Gate402-Webhook/1.0

Payload

json
{
  "event": "payment.confirmed",
  "endpoint": "/api/data",
  "amount": 0.001,
  "currency": "USDC",
  "network": "mainnet",
  "txHash": "5kWq9mLP3rTxHJzUvBnCs...",
  "payerWallet": "DcL4mMaqX4FAHg4Cp1SstvMSMWytoXo93ktWycgGYABE",
  "timestamp": "2026-05-27T01:04:51.630Z"
}

Verifying signatures

typescript
import crypto from 'crypto'

app.post('/webhook', express.json(), (req, res) => {
  const sig = req.headers['x-gate402-signature']
  const secret = process.env.GATE402_WEBHOOK_SECRET

  const expected = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(req.body))
    .digest('hex')

  if (sig !== `sha256=${expected}`) {
    return res.status(401).json({ error: 'Invalid signature' })
  }

  console.log('Payment confirmed:', req.body.amount, 'USDC')
  console.log('From:', req.body.payerWallet)
  console.log('TxHash:', req.body.txHash)

  res.json({ received: true })
})
Webhooks only fire for payments through api.gate402.dev, not local MCP servers. Test with: POST /api/users/webhook/test

Installation

install
$ npm install gate402-agent

Complete example

javascript
// agent.mjs — complete example
import { Gate402Agent, SpendingLimitError } from 'gate402-agent'

const agent = new Gate402Agent({
  privateKey: process.env.AGENT_WALLET_PRIVATE_KEY,
  network: 'mainnet',  // 'devnet' for testing
  debug: true,
  limits: {
    maxPerCall:  0.01,   // Max $0.01 per single call
    maxPerHour:  1.00,   // Max $1.00 per hour
    maxPerDay:  10.00,   // Max $10.00 per day
    blockedEndpoints: ['/api/premium'],
    allowedEndpoints: ['/api/search', '/api/analyze'],
  }
})

try {
  // Detects 402, pays automatically, retries — zero extra code
  const res = await agent.fetch('https://your-api.dev/api/data')
  const data = await res.json()

  console.log('Response:', data)
  console.log('Stats:', agent.getStats())
  // {
  //   totalCalls: 1,
  //   successfulPayments: 1,
  //   totalSpent: 0.001,
  //   walletAddress: 'DcL4mMaq...'
  // }

} catch (e) {
  if (e instanceof SpendingLimitError) {
    console.log('Limit exceeded:', e.message)
    console.log('Code:', e.code) // SPENDING_LIMIT_EXCEEDED
  }
}

Breaking it down

privateKeyYour Solana wallet private key — this wallet holds USDC and signs payments.
network'devnet' for testing (free USDC from faucet), 'mainnet' for production (real USDC).
limitsProtect your agent from unexpected costs. Set per-call, per-hour, and per-day caps, plus endpoint allow/block lists.
agent.fetch()Drop-in replacement for fetch(). Detects HTTP 402, pays automatically, retries the request — no extra code needed.
getStats()Returns how much your agent spent, total calls, successful payments, and the wallet address.
SpendingLimitErrorThrown when a limit is exceeded. Check e.code for SPENDING_LIMIT_EXCEEDED.

Demo Fetch

Test your integration without real USDC.

typescript
// Uses demo_ hash — bypasses blockchain verification
const res = await agent.demoFetch('https://api.example.com/data')
const data = await res.json()
demoFetch only works on APIs whose account is set to devnet. APIs set to mainnet reject demo_ payments with DEMO_NOT_ALLOWED_ON_MAINNET.

Getting USDC

Devnet (free, for testing)

01
Go to faucet.circle.com
Open the Circle USDC faucet in your browser.
02
Select "Solana Devnet"
Choose Solana Devnet from the network dropdown.
03
Paste your agent wallet address and click Send
Copy your wallet address from agent.getStats().walletAddress and paste it in the faucet.
devnet faucet
# Get your agent wallet address
$ const agent = new Gate402Agent({ privateKey: "..." })
agent.getStats().walletAddress
DcL4mMaqX4FAHg4Cp1SstvMSMWytoXo93ktWycgGYABE
# Then go to faucet.circle.com → Solana Devnet → paste address
You receive 10 USDC instantly — free

Mainnet (production)

Mainnet uses real USDC. Always test on devnet first.
01
Buy USDC on Coinbase, Kraken, or Binance
Purchase USDC on any major exchange.
02
Withdraw to your Solana wallet address
Send USDC to the Solana address from your Gate402Agent.
03
Change network to 'mainnet' in agent config
Set network: 'mainnet' in your Gate402Agent constructor.

Add to existing MCP

If you already have an MCP server, add Gate402 middleware in front of the HTTP transport. All tool calls are gated.

typescript
import express from 'express'
import { gate402 } from 'gate402'
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'
import { z } from 'zod'

const app = express()
app.use(express.json())

// Gate402 — all MCP traffic gated at /mcp
app.use('/mcp', gate402({
  apiKey:        process.env.GATE402_API_KEY!,
  walletAddress: process.env.SOLANA_WALLET!,
  endpoints: { '/mcp': 0.001 }
}))

const server = new McpServer({ name: 'my-mcp', version: '1.0.0' })

server.tool('get_weather', { city: z.string() }, async ({ city }) => ({
  content: [{ type: 'text', text: `Weather in ${city}: 28°C, sunny` }]
}))

app.post('/mcp', async (req, res) => {
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined })
  await server.connect(transport)
  await transport.handleRequest(req, res, req.body)
})

app.listen(3001, () => console.log('Gated MCP server on :3001'))

Build new MCP

Start from scratch with the Gate402 MCP template. Includes payment middleware, tool definitions, and dashboard integration.

bash
$ npx create-gate402-mcp my-mcp-server
Scaffolding MCP server with Gate402...
Created my-mcp-server/
$ cd my-mcp-server && cp .env.example .env
$ npm install && npm run dev
Gate402 MCP server running on :3001

Per-tool pricing

Charge different amounts per MCP tool by mapping tool names to prices. The middleware reads the tool name from the JSON-RPC request body.

typescript
app.use('/mcp', gate402({
  apiKey:        process.env.GATE402_API_KEY!,
  walletAddress: process.env.SOLANA_WALLET!,
  mcpToolPricing: {
    'get_weather':         0.001,
    'run_analysis':        0.050,
    'generate_report':     0.100,
    '*':                   0.001,   // fallback for unlisted tools
  }
}))

CLI generator

Generate a fully typed MCP tool definition with Gate402 pricing from a single command.

bash
$ npx gate402 generate-tool get_weather
Tool name: get_weather
Price (USDC): 0.001
Input schema (JSON): { "city": { "type": "string" } }
Generated src/tools/get_weather.ts
Registered in src/index.ts

Dashboard

The Gate402 dashboard at gate402.dev gives you a real-time view of your API usage, revenue, and callers.

Overview
Total calls, revenue, unique callers, top endpoints
Live feed
Real-time stream of every paid API call
Endpoints
Create, edit, and toggle endpoint prices
Settings
API key, wallet address, network selection

Analytics

Analytics are available at gate402.dev/dashboard. Metrics are updated in real-time as payments come in.

bash
# Pull analytics via API
curl https://api.gate402.dev/api/metrics \
  -H "Authorization: Bearer $GATE402_API_KEY"

# Response:
# {
#   "totalCalls":    142,
#   "totalRevenue":  "0.1420",
#   "uniqueCallers": 8,
#   "topEndpoint":   "/api/weather"
# }

Wallet & payouts

All payments land directly in your Solana wallet. There is no Gate402 escrow. You can withdraw or use USDC at any time through any Solana wallet (Phantom, Backpack, CLI).

During early access, Gate402 takes 0% platform fee. You receive 100% of every configured price directly in your Solana wallet. This will change when we exit early access — you will be notified in advance.

API Key Management

Your API key authenticates requests to Gate402. Never expose it in client-side code or public repositories.

Using your key

bash
# .env
GATE402_API_KEY=your-key-here
typescript
app.use(gate402({
  apiKey: process.env.GATE402_API_KEY,
  serverUrl: 'https://api.gate402.dev',
  endpoints: { '/api/data': 0.001 }
}))

Rotating your key

If your key is compromised, rotate it immediately:

01
Go to gate402.dev/settings
Navigate to Settings → API Key.
02
Click "Rotate API key"
Your old key is invalidated immediately upon rotation.
03
Update your environment variables
Replace GATE402_API_KEY in your .env and all deployment environments with the new key.

Or via API:

bash
curl -X POST https://api.gate402.dev/api/users/rotate-key \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

# Response: { "apiKey": "new-key-here" }
Your old key is invalidated the moment you rotate. Update all services before rotating in production.

API endpoints

Base URL: https://api.gate402.dev

GET/api/metricsAuth
Total calls, revenue, unique callers
{ totalCalls, totalRevenue, uniqueCallers, topEndpoint }
GET/api/calls/recentAuth
Last 50 API calls
[{ id, endpoint, amount, txHash, callerIp, createdAt }]
GET/api/calls/per-dayAuth
Calls grouped by day (last 30 days)
[{ date, calls, revenue }]
GET/api/endpointsAuth
Your configured endpoints
[{ id, path, priceUsdc, active, calls }]
POST/api/endpointsAuth
Create a new endpoint
{ id, path, priceUsdc, active }
DELETE/api/endpoints/:idAuth
Delete an endpoint
{ success: true }
POST/api/verify-paymentAuth
Verify a Solana tx hash
{ valid: boolean, amount, wallet }
PATCH/api/users/networkAuth
Switch network — body: { network: "devnet"|"mainnet" }
{ network, message }
PATCH/api/users/walletAuth
Update receiving wallet — body: { walletAddress }
{ walletAddress, message }
PATCH/api/users/webhookAuth
Configure webhook — body: { webhookUrl, webhookSecret }
{ webhookUrl, message }
POST/api/users/webhook/testAuth
Send a test webhook to your configured URL
{ message, url }
POST/api/users/rotate-keyAuth
Rotate API key — old key invalidated immediately
{ apiKey }
GET/api/analytics/revenueAuth
Revenue summary — query: ?period=7d|30d|90d (Pro)
{ summary: { grossRevenue, netRevenue, transactionCount }, byDay: [...] }
GET/api/analytics/top-agentsAuth
Top paying agent wallets (Pro)
{ agents: [{ wallet, calls, totalPaid }] }
GET/api/analytics/latencyAuth
Latency percentiles per endpoint (Pro)
{ latency: [{ endpoint, p50, p95, p99, avg }] }
GET/api/analytics/exportAuth
Export all transactions as CSV (Pro)
CSV file
GET/api/weather
Demo endpoint (requires 0.001 USDC payment)
{ city, temp, humidity }

Error codes

HTTPCodeDescription
402PAYMENT_REQUIREDNo payment header. Response includes price and wallet.
402PAYMENT_ALREADY_USEDThis tx hash has already been consumed (anti-replay).
402PAYMENT_INVALIDTx not found on-chain or amount does not match.
402DEMO_NOT_ALLOWED_ON_MAINNETDemo mode is disabled on mainnet. Send real USDC or switch to devnet.
401INVALID_API_KEYx-api-key header contains an invalid or unknown key.
401MISSING_API_KEYx-api-key header not present in request.
403UPGRADE_REQUIREDThis feature requires a Pro plan. Upgrade at gate402.dev/billing.
400INVALID_PATHEndpoint path must start with / and contain only valid characters.
400INVALID_PRICEpriceUsdc must be a positive number between 0.0001 and 1000.
502UPSTREAM_UNAVAILABLEOrigin API did not respond (managed mode only).
500INTERNAL_ERRORUnexpected error. Check server logs.

Troubleshooting

"Invalid API key" error

Your x-api-key header is invalid or missing. Check gate402.dev/settings for your current API key. If you rotated recently, update your environment variables.

"Demo mode not allowed on mainnet"

Your account is set to mainnet but you sent a demo_ hash. Switch to devnet for testing, or send a real Solana transaction.

"Transaction not found"

The tx hash doesn't exist on the expected network. Common causes:

Wrong network — sent mainnet tx but account is on devnet (or vice versa)
Transaction not yet finalized — wait 1–2 seconds and retry
Invalid tx hash format

"Insufficient amount: received 0, expected X"

The transaction was found but the recipient received 0 USDC. Common causes:

Sender and recipient are the same wallet — use different wallets for testing
Wrong recipient address — check your wallet in Settings
RPC rate limit — use Helius for production

Payment goes through but dashboard shows 0

Your API key might not match the endpoint owner. Make sure you're passing x-api-key in the middleware config and that it matches the key in gate402.dev/settings.

Webhook not firing

Check that your webhook URL is publicly accessible. Test with POST /api/users/webhook/test. Webhooks only fire for payments through api.gate402.dev, not local MCP servers.