Getting Started

March 30, 2026 · View on GitHub

Prerequisites

  • Node.js 20+
  • pnpm

Install what you need

# If you're an API provider accepting agent payments
pnpm add @openagentpay/server-express @openagentpay/adapter-mock @openagentpay/core

# If you're building an agent that pays for APIs
pnpm add @openagentpay/client @openagentpay/adapter-mock @openagentpay/core

# Additional adapters (pick what you need)
pnpm add @openagentpay/adapter-x402       # USDC payments on Base
pnpm add @openagentpay/adapter-mpp        # MPP protocol (Tempo/Stripe/Lightning)
pnpm add @openagentpay/adapter-solana     # Solana SPL token payments
pnpm add @openagentpay/adapter-lightning   # Lightning Network BOLT11
pnpm add @openagentpay/adapter-credits    # prepaid credit system
pnpm add @openagentpay/adapter-upi        # UPI payments (India)
pnpm add @openagentpay/policy             # standalone spend governance
pnpm add @openagentpay/receipts           # receipt storage + query
pnpm add @openagentpay/mcp               # paid MCP tools
pnpm add @openagentpay/server-hono        # Hono instead of Express
pnpm add @openagentpay/otel-exporter      # OpenTelemetry integration

Server side: accept payments

1. Create a paywall

import express from 'express';
import { createPaywall } from '@openagentpay/server-express';
import { mock } from '@openagentpay/adapter-mock';

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

const paywall = createPaywall({
  recipient: '0xYourWalletAddress',
  adapters: [mock()],
});

2. Add it to routes

// Static price
app.get('/api/search', paywall({ price: '0.01' }), (req, res) => {
  res.json({ results: ['data1', 'data2'] });
});

// Dynamic price — function receives the request
app.post('/api/process', paywall((req) => ({
  price: (0.01 * req.body.pages).toFixed(3),
  description: `Process ${req.body.pages} pages`,
})), (req, res) => {
  res.json({ processed: true });
});

// Free endpoint — no paywall
app.get('/api/health', (req, res) => res.json({ status: 'ok' }));

app.listen(3000);

3. Test it

curl http://localhost:3000/api/search
# → 402 Payment Required
# → { "type": "payment_required", "pricing": { "amount": "0.01", "currency": "USDC", "unit": "per_request" }, "methods": [...] }

curl -H "X-PAYMENT: mock:test123" http://localhost:3000/api/search
# → 200 OK
# → { "results": ["data1", "data2"] }

Agent side: pay for APIs

import { withPayment } from '@openagentpay/client';
import { mockWallet } from '@openagentpay/adapter-mock';

const paidFetch = withPayment(fetch, {
  wallet: mockWallet(),
  policy: {
    maxPerRequest: '1.00',
    maxPerDay: '50.00',
    allowedDomains: ['localhost'],
  },
  onReceipt: (receipt) => {
    console.log(`Paid ${receipt.payment.amount} for ${receipt.request.url}`);
  },
});

const response = await paidFetch('http://localhost:3000/api/search');
const data = await response.json();

withPayment wraps native fetch. When a server returns 402, the wrapper parses the pricing, evaluates the policy, pays via the wallet adapter, retries the request with the payment proof in the X-PAYMENT header, and returns the final response. Your code just calls paidFetch(url).

Switching to real payments

Replace mock() with x402() when you're ready for production:

import { x402, x402Wallet } from '@openagentpay/adapter-x402';

// Server
const paywall = createPaywall({
  recipient: '0xYourWalletAddress',
  adapters: [x402({ network: 'base-sepolia' })],  // testnet first
});

// Client
const paidFetch = withPayment(fetch, {
  wallet: x402Wallet({ privateKey: process.env.AGENT_WALLET_KEY!, network: 'base-sepolia' }),
});

Next steps