Documentation Index
Fetch the complete documentation index at: https://docs.monkepay.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Installation
npm install @monkepay/sdk fastify
Basic usage
import Fastify from 'fastify'
import { MonkePayFastify } from '@monkepay/sdk'
const app = Fastify()
const monkePay = MonkePayFastify({
apiKeyId: process.env.MONKEPAY_API_KEY_ID!,
apiKeySecret: process.env.MONKEPAY_API_KEY_SECRET!,
price: '0.001',
})
app.register(monkePay({
setup: (scope) => {
scope.get('/api/data', async () => ({ result: 'paid content' }))
},
}))
app.listen({ port: 3000 })
Why setup()?
Fastify hooks are scope-bound — a hook only fires on routes registered in the same plugin scope or a child scope. Routes registered at the parent app level after app.register() are in a parent scope and won’t see the hooks.
MonkePay registers two Fastify hooks internally (preHandler for payment verification, onSend for settlement). For these hooks to fire on your routes, the routes must be inside the same scope — which is exactly what setup() provides.
// ✅ Correct — route is inside the plugin scope via setup()
app.register(monkePay({
price: '0.001',
setup: (scope) => {
scope.get('/api/data', handler)
},
}))
// ❌ Wrong — route is at parent scope, hooks won't fire
app.register(monkePay({ price: '0.001' }))
app.get('/api/data', handler) // NOT gated
Per-route pricing
Register multiple plugins with different prices using setup():
const monkePay = MonkePayFastify({
apiKeyId: process.env.MONKEPAY_API_KEY_ID!,
apiKeySecret: process.env.MONKEPAY_API_KEY_SECRET!,
price: '0.001',
})
// $0.001 routes
app.register(monkePay({
price: '0.001',
setup: (scope) => {
scope.get('/api/cheap', async () => ({ result: 'cheap' }))
},
}))
// $0.10 routes
app.register(monkePay({
price: '0.10',
setup: (scope) => {
scope.get('/api/expensive', async () => ({ result: 'expensive' }))
},
}))
// One-time unlock
app.register(monkePay({
price: '1.00',
paymentMode: 'one_time',
setup: (scope) => {
scope.get('/api/report', async () => ({ result: 'report' }))
},
}))
Behind a reverse proxy
Fastify requires explicit baseUrl config when running behind a reverse proxy. Without it, x402 may construct http:// resource URLs when your API is actually https://, causing payment failures.
const monkePay = MonkePayFastify({
apiKeyId: process.env.MONKEPAY_API_KEY_ID!,
apiKeySecret: process.env.MONKEPAY_API_KEY_SECRET!,
price: '0.001',
baseUrl: 'https://api.yourcompany.com',
})
Fastify also reads X-Forwarded-Proto as a fallback. Resource URL resolution order:
baseUrl config (highest priority)
X-Forwarded-Proto request header
request.protocol
'http' (last resort)
See Reverse Proxies for details.
Notes
No streaming on payment-gated routes. Settlement happens in the onSend hook which requires the full response to be available. Streaming endpoints (reply.raw.write loops, SSE) are not supported on payment-gated routes.
Use app.register(monkePay()) not app.addHook. The adapter must be registered as a plugin to add its hooks in the correct scope.