PayToll Documentation
The open-source developer toolkit for machine payments on Solana. Everything you need to monetize any API using the Machine Payments Protocol.
Note: PayToll is in active development. The MPP spec is not yet finalized — APIs and wire formats are subject to change. Follow our GitHub for updates.
Overview
PayToll is a TypeScript toolkit that wraps the Solana Foundation's @solana/mpp SDK into production-ready middleware, CLI tools, and a monitoring dashboard. It enables any Node.js API to accept payments from AI agents, applications, and humans using the HTTP 402 Payment Required flow.
Architecture
// PayToll Architecture
┌─────────────────────────────────────────┐
│ Your API Server │
│ │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ PayToll │ │ Your Handlers │ │
│ │ Middleware │→ │ (business │ │
│ │ │ │ logic) │ │
│ └──────┬──────┘ └─────────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ @solana/ │ │
│ │ mpp SDK │ │
│ └──────┬──────┘ │
│ │ │
└─────────┼───────────────────────────────┘
│
┌─────▼─────┐
│ Solana │
│ Network │
└───────────┘
Key Concepts
| Concept | Description |
Charge | One-time payment per request. Client pays, gets access to a single response. Best for discrete API calls. |
Session | Streaming payment channel. Client deposits funds upfront, consumes resources with micropayments. Best for metered/streaming access. |
402 Challenge | Server response describing what payment is needed — amount, currency, recipient. The client fulfils this to get access. |
Credential | Proof of payment sent by the client in the Authorization header. Contains the signed Solana transaction. |
Receipt | Server response header confirming payment was verified and access is granted. |
Fee Sponsorship | Server pays Solana transaction fees on behalf of the client. Client only pays the transfer amount. |
Quickstart
Get a paid API running locally in under 5 minutes.
Prerequisites
- Node.js 20+
- A Solana wallet (for receiving payments)
- Surfpool (for local testing) —
curl -fsSL https://run.surfpool.run | sh
Step 1: Install
$ npm install @paytoll/sdk
$ npx paytoll init
This creates a paytoll.config.ts file in your project root.
Step 2: Configure
// paytoll.config.ts
import { defineConfig } from '@paytoll/sdk'
export default defineConfig({
recipient: 'YOUR_SOLANA_WALLET_ADDRESS',
network: 'devnet',
endpoints: [
{
path: '/api/search',
price: '0.01',
currency: 'USDC',
mode: 'charge'
}
]
})
Step 3: Add middleware
// server.ts
import express from 'express'
import { paytoll } from '@paytoll/sdk/express'
import config from './paytoll.config'
const app = express()
app.use(paytoll(config))
app.get('/api/search', (req, res) => {
// This only runs after payment is verified
res.json({ results: ['result1', 'result2'] })
})
app.listen(3000)
Step 4: Test locally
# Terminal 1: Start Surfpool
$ surfpool start
# Terminal 2: Start your server
$ npx paytoll dev
# Terminal 3: Test with built-in client
$ npx paytoll test /api/search
Configuration Reference
| Option | Type | Default | Description |
recipient | string | — | Solana wallet address for receiving payments (required) |
network | string | 'devnet' | 'devnet' | 'mainnet-beta' |
endpoints | array | [] | Array of endpoint configurations |
feePayer | string? | — | Base58 keypair for fee sponsorship (optional) |
rpcUrl | string? | auto | Custom Solana RPC endpoint |
secretKey | string? | auto | HMAC key for signing 402 challenges |
Endpoint Configuration
| Option | Type | Description |
path | string | URL path to gate (e.g. '/api/search') |
price | string | Amount to charge (e.g. '0.01' for 1 cent) |
currency | string | 'USDC' | 'SOL' | SPL token mint address |
mode | string | 'charge' (one-time) | 'session' (streaming) |
meter | string? | For session mode: meter name (e.g. 'api_calls') |
Charge Mode
Charge mode implements one-time payments. Each request requires a separate payment. The flow is:
Client → GET /api/search
Server → 402 { amount: "0.01", currency: "USDC", recipient: "..." }
Client → [signs Solana tx, sends USDC]
Client → GET /api/search + Authorization: Payment [credential]
Server → [verifies tx on-chain] → 200 { results: [...] }
Session Mode
Session mode implements streaming payment channels using voucher-based micropayments. The client opens a channel with a deposit, then sends updated vouchers with each request. The cumulative amount increases monotonically.
Client → GET /api/stream
Server → 402 { mode: "session", deposit: "1.00", pricing: {...} }
Client → [opens channel, deposits 1.00 USDC]
Client → GET /api/stream + Authorization: Payment [voucher: 0.01]
Server → 200 { data: "chunk 1" }
Client → GET /api/stream + Authorization: Payment [voucher: 0.02]
Server → 200 { data: "chunk 2" }
...
Client → POST /close-session
Server → [settles on-chain with final voucher]
Security Model
PayToll's security model builds on Solana's cryptographic guarantees.
- Replay protection: Each transaction signature can only be used once. PayToll tracks consumed signatures to prevent replay attacks.
- HMAC challenges: 402 challenges are signed with an HMAC secret key. Clients cannot forge or modify challenges.
- On-chain verification: Every payment is verified on the Solana blockchain before access is granted. No trust assumptions.
- Transaction simulation: Transactions are simulated before broadcast to prevent wasted fees from invalid payments.
- Session nonces: Voucher-based sessions use monotonic counters that prevent double-spending within a channel.
CLI Commands
| Command | Description |
paytoll init | Create a paytoll.config.ts file in the current directory |
paytoll dev | Start dev server with Surfpool integration + dashboard |
paytoll test [path] | Simulate an agent payment against an endpoint |
paytoll status | Show revenue, active sessions, endpoint health |
paytoll build | Build for production deployment |
Examples
Working example servers you can clone and run immediately.
Weather API
A simple Express server that charges 0.01 USDC per weather lookup.
// examples/weather/server.ts
import express from 'express'
import { paytoll } from '@paytoll/sdk/express'
const app = express()
app.use('/api/weather', paytoll({
price: '0.01',
currency: 'USDC',
mode: 'charge'
}))
app.get('/api/weather/:city', async (req, res) => {
const data = await fetchWeather(req.params.city)
res.json(data)
})
app.listen(3000)
AI Inference
Session-mode endpoint for metered AI model access — pay per token.
// examples/inference/server.ts
app.use('/api/generate', paytoll({
price: '0.001',
currency: 'USDC',
mode: 'session',
meter: 'tokens'
}))
MCP Tool Monetization
Wrap any MCP server tool with PayToll to charge per tool call.
// examples/mcp-tool/server.ts
// Gate MCP tool calls via MPP's JSON-RPC transport
app.use('/mcp', paytoll({
price: '0.05',
currency: 'USDC',
mode: 'charge'
}))