Documentation Index Fetch the complete documentation index at: https://beta-docs.nevermind.ai/llms.txt
Use this file to discover all available pages before exploring further.
This guide provides a deep dive into integrating Nevermined. We’ll cover the core components you’ll interact with and the different patterns you can use to add a payment layer to your AI agents.
Core Integration Concepts
Before diving into integration patterns, it’s important to understand the fundamental components of the Nevermined SDK.
1. The Payments Client
The Payments Client is your primary interface for all interactions with the Nevermined protocol. It’s a singleton instance that abstracts away the complexities of blockchain interactions.
Key Responsibilities:
Agent & Plan Management : Handles the registration and configuration of your agents and their payment plans.
Access Validation : Verifies subscriber permissions through bearer token validation.
Credit Redemption : Manages the lifecycle of credits, from issuance to consumption.
Query Routing : Facilitates communication between subscribers and agents.
Modular Architecture:
const payments = Payments . getInstance ({ nvmApiKey , environment })
payments . agents // Agent registration and management
payments . plans // Plan creation and ordering
payments . requests // Request validation and tracking
payments . a2a // Agent-to-Agent communication
2. AI Agents
In Nevermined, an AI Agent is your monetizable service. It’s defined by its metadata, its API endpoints, and the payment plans associated with it.
Agent Structure:
interface Agent {
agentId : string ;
metadata : {
name : string ;
description ?: string ;
tags : string [];
dateCreated : Date ;
image ?: string ;
};
api : {
endpoints : Array <{ [ method : string ] : string }>; // Protected endpoints
openEndpoints ?: string []; // Public, non-monetized endpoints
};
plans : PaymentPlan [];
}
3. The Credits System
Credits are the unit of consumption in Nevermined, providing a flexible way to bill for AI services.
Credit Lifecycle:
Purchase : A subscriber buys a plan and receives credits.
Validation : On each API call, the system checks if the subscriber has sufficient credits.
Consumption : After a successful request, the specified number of credits is redeemed (burned).
Tracking : The subscriber’s balance is updated in real-time.
Expiration : For time-based plans, access expires after the duration ends.
4. Access Control
Nevermined uses bearer token authentication to secure your agent’s endpoints, ensuring only authorized subscribers can access them.
How it Works:
Plan Purchase
A subscriber purchases a plan through the SDK or Web App.
Token Generation
Upon purchase, the subscriber receives a bearer token via getAgentAccessToken().
Request Authentication
The subscriber includes the token in the Authorization header of their requests.
Validation
The Nevermined Proxy or your agent validates the token, checks credit balance, and authorizes access.
Service Delivery
If validated, your agent processes the request and credits are automatically deducted.
Integration Patterns
Choose the integration pattern that best fits your application’s architecture.
Direct Integration Integrate the SDK directly into your agent’s code for full control.
Proxy Integration No code changes - Nevermined Proxy handles payments.
Middleware Pattern Create a reusable authentication layer for Node.js applications.
Direct Integration
This pattern is best for new services or when you want maximum control over the request lifecycle.
Example: Protecting an Express.js Endpoint
app . post ( '/api/query' , async ( req , res ) => {
try {
// Extract bearer token from Authorization header
const authHeader = req . headers [ 'authorization' ];
if ( ! authHeader ?. startsWith ( 'Bearer ' )) {
return res . status ( 401 ). json ({ error: 'Unauthorized' });
}
const token = authHeader . substring ( 7 );
// 1. Validate access token
const validation = await payments . requests . isValidRequest (
token ,
req . body
);
if ( ! validation . isValid ) {
// Return available payment plans
return res . status ( 402 ). json ({
error: 'Payment Required' ,
plans: validation . plans
});
}
// 2. Process the AI request
const result = await processAIRequest ( req . body . prompt );
// 3. Credits are automatically redeemed by the proxy
// For direct integration without proxy, you can manually track:
// await payments.requests.logRequest(token, { usage: 1 });
// 4. Return response
res . json ( result );
} catch ( error ) {
console . error ( 'Request failed:' , error );
res . status ( 500 ). json ({ error: 'Internal server error' });
}
});
Proxy Integration
For existing services that can’t be modified, use the Nevermined Proxy. The proxy handles all payment validation automatically.
How It Works:
Register your existing API endpoints with Nevermined
Subscribers access your service through the proxy URL
The proxy validates tokens and manages credits
Your service receives forwarded requests without modification
Example Registration:
const { agentId , planId } = await payments . agents . registerAgentAndPlan (
{
name: 'My Existing Service' ,
tags: [ 'ai' , 'proxy' ]
},
{
endpoints: [
{ POST: 'https://my-api.com/process' },
{ GET: 'https://my-api.com/status' }
],
openEndpoints: [ 'https://my-api.com/health' ]
},
planMetadata ,
priceConfig ,
creditsConfig
);
// Your service is now accessible at:
// https://proxy.nevermined.app/api/v1/agents/{agentId}/invoke
Middleware Pattern
For Node.js applications, create reusable middleware to protect multiple routes.
Example: Express.js Auth Middleware
export function createNeverminedAuth ( payments : Payments ) {
return async ( req : Request , res : Response , next : NextFunction ) => {
try {
const authHeader = req . headers [ 'authorization' ];
if ( ! authHeader ?. startsWith ( 'Bearer ' )) {
return res . status ( 401 ). json ({ error: 'Missing bearer token' });
}
const token = authHeader . substring ( 7 );
const validation = await payments . requests . isValidRequest ( token , req . body );
if ( ! validation . isValid ) {
return res . status ( 402 ). json ({
error: 'Payment required' ,
plans: validation . plans
});
}
// Attach validation info to request
( req as any ). nevermined = {
token ,
planId: validation . planId ,
subscriberAddress: validation . subscriberAddress
};
next ();
} catch ( error ) {
console . error ( 'Auth error:' , error );
res . status ( 500 ). json ({ error: 'Authentication failed' });
}
};
}
// Usage
const auth = createNeverminedAuth ( payments );
app . post ( '/api/query' , auth , handleQuery );
app . post ( '/api/generate' , auth , handleGenerate );
Direct Integration
Best for new services or when you want full control over the payment flow.
Step 1: Install Dependencies
npm install @nevermined-io/payments express
npm install --save-dev @types/express typescript
Step 2: Initialize Nevermined
import { Payments } from '@nevermined-io/payments'
import express from 'express'
const app = express ()
app . use ( express . json ())
const payments = Payments . getInstance ({
nvmApiKey: process . env . NVM_API_KEY ,
environment: 'production'
})
Step 3: Create Protected Endpoint
app . post ( '/api/query' , async ( req , res ) => {
try {
// Extract bearer token
const authHeader = req . headers [ 'authorization' ]
if ( ! authHeader ?. startsWith ( 'Bearer ' )) {
return res . status ( 401 ). json ({ error: 'Unauthorized - Bearer token required' })
}
const token = authHeader . substring ( 7 )
// Validate access
const validation = await payments . requests . isValidRequest ( token , req . body )
if ( ! validation . isValid ) {
return res . status ( 402 ). json ({
error: 'Payment required' ,
message: 'Please purchase a plan to access this service' ,
plans: validation . plans // Available payment plans
})
}
// Process the AI request
const result = await processAIRequest ( req . body . prompt )
// For direct integration, optionally track usage:
// await payments.requests.logRequest(token, {
// creditsUsed: 1,
// requestData: { prompt: req.body.prompt.substring(0, 100) }
// })
// Return response
res . json ({
result ,
usage: {
creditsUsed: 1 ,
planId: validation . planId
}
})
} catch ( error ) {
console . error ( 'Request failed:' , error )
res . status ( 500 ). json ({ error: 'Internal server error' })
}
})
async function processAIRequest ( prompt : string ) {
// Your AI logic here
return { response: `Processed: ${ prompt } ` }
}
Step 4: Add Health Check
app . get ( '/health' , ( req , res ) => {
res . json ({
status: 'healthy' ,
service: 'ai-agent' ,
version: '1.0.0'
})
})
Step 5: Register Your Agent
import {
getERC20PriceConfig ,
getFixedCreditsConfig
} from '@nevermined-io/payments'
async function setupAgent () {
const agentMetadata = {
name: 'My AI Service' ,
tags: [ 'ai' , 'nlp' ],
dateCreated: new Date (),
description: 'Advanced AI processing service'
}
const agentApi = {
endpoints: [
{ POST: 'https://api.myservice.com/api/query' }
],
openEndpoints: [
'https://api.myservice.com/health'
]
}
const planMetadata = {
name: 'Basic Plan' ,
description: '100 queries with advanced AI processing' ,
dateCreated: new Date ()
}
const priceConfig = getERC20PriceConfig (
10_000_000 n , // 10 USDC (6 decimals)
USDC_ADDRESS ,
process . env . BUILDER_ADDRESS
)
const creditsConfig = getFixedCreditsConfig ( 100 n , 1 n )
const { agentId , planId } = await payments . agents . registerAgentAndPlan (
agentMetadata ,
agentApi ,
planMetadata ,
priceConfig ,
creditsConfig
)
console . log ( 'Agent registered:' , agentId )
console . log ( 'Plan created:' , planId )
console . log ( 'Access via proxy:' , `https://proxy.nevermined.app/api/v1/agents/ ${ agentId } ` )
}
Middleware Pattern
Create reusable authentication middleware for Express/Node.js applications.
Create Auth Middleware
import { Request , Response , NextFunction } from 'express'
import { Payments } from '@nevermined-io/payments'
interface NeverminedRequest extends Request {
nevermined ?: {
planId : string
agentId : string
subscriberAddress : string
}
}
export function createNeverminedAuth ( payments : Payments ) {
return ( agentId : string ) => {
return async (
req : NeverminedRequest ,
res : Response ,
next : NextFunction
) => {
try {
const { planId , subscriberAddress } = req . body
const signature = req . headers [ 'x-nvm-query-signature' ] as string
if ( ! planId || ! subscriberAddress || ! signature ) {
return res . status ( 400 ). json ({
error: 'Missing required payment parameters'
})
}
const isValid = await payments . isValidRequest (
planId ,
agentId ,
subscriberAddress ,
signature
)
if ( ! isValid ) {
const paymentCard = await payments . getAgentPaymentCard ( agentId )
return res . status ( 402 ). json ({
error: 'Payment required' ,
paymentCard
})
}
// Attach payment info to request
req . nevermined = { planId , agentId , subscriberAddress }
next ()
} catch ( error ) {
console . error ( 'Auth middleware error:' , error )
res . status ( 500 ). json ({ error: 'Authentication failed' })
}
}
}
}
Use Middleware
const app = express ()
const payments = Payments . getInstance ({
nvmApiKey: process . env . NVM_API_KEY ,
environment: 'testing'
})
const auth = createNeverminedAuth ( payments )
// Apply to all protected routes
app . use ( '/api/*' , auth )
// Or apply to specific routes
app . post ( '/api/query' , auth , async ( req , res ) => {
// Access validation info from req.nevermined
const { token , planId } = req . nevermined
// Process request...
const result = await processQuery ( req . body )
// Optionally log usage for direct integration
// await payments.requests.logRequest(token, { creditsUsed: 1 })
res . json ( result )
})
Advanced Middleware Features
export function createAdvancedAuth ( payments : Payments , options ?: {
checkBalance ?: boolean
minCredits ?: bigint
rateLimitPerMinute ?: number
}) {
const requestCounts = new Map < string , number []>()
return async ( req : NeverminedRequest , res : Response , next : NextFunction ) => {
try {
// Extract and validate token
const authHeader = req . headers [ 'authorization' ]
if ( ! authHeader ?. startsWith ( 'Bearer ' )) {
return res . status ( 401 ). json ({ error: 'Unauthorized' })
}
const token = authHeader . substring ( 7 )
const validation = await payments . requests . isValidRequest ( token , req . body )
if ( ! validation . isValid ) {
return res . status ( 402 ). json ({
error: 'Payment required' ,
plans: validation . plans
})
}
// Check balance if requested
if ( options ?. checkBalance ) {
const balance = await payments . getPlanBalance (
req . body . planId ,
req . body . subscriberAddress
)
if ( balance . credits < ( options . minCredits || 1 n )) {
return res . status ( 402 ). json ({
error: 'Insufficient credits' ,
required: options . minCredits ?. toString () || '1' ,
available: balance . credits . toString ()
})
}
res . setHeader ( 'X-Credits-Remaining' , balance . credits . toString ())
}
// Rate limiting per subscriber
if ( options ?. rateLimitPerMinute ) {
const now = Date . now ()
const minute = 60 * 1000
const key = req . body . subscriberAddress
const requests = requestCounts . get ( key ) || []
const recentRequests = requests . filter ( t => now - t < minute )
if ( recentRequests . length >= options . rateLimitPerMinute ) {
return res . status ( 429 ). json ({
error: 'Rate limit exceeded' ,
retryAfter: minute - ( now - recentRequests [ 0 ])
})
}
recentRequests . push ( now )
requestCounts . set ( key , recentRequests )
}
req . nevermined = {
planId: req . body . planId ,
agentId ,
subscriberAddress: req . body . subscriberAddress
}
next ()
} catch ( error ) {
console . error ( 'Auth error:' , error )
res . status ( 500 ). json ({ error: 'Authentication failed' })
}
}
}
}
// Usage
const advancedAuth = createAdvancedAuth ( payments , {
checkBalance: true ,
minCredits: 5 n ,
rateLimitPerMinute: 60
})
app . post ( '/api/advanced-query' , advancedAuth ( agentId ), handler )
Proxy Integration
For existing services that can’t be modified, use the Nevermined Proxy.
How It Works
Register Your Service
Register your existing endpoints with Nevermined
Proxy Handles Payments
All payment validation happens at the proxy layer
No Code Changes
Your service continues to work as before
Setup Process
async function setupProxyIntegration () {
const payments = Payments . getInstance ({
nvmApiKey: process . env . NVM_API_KEY ,
environment: 'production'
})
// Register your existing service
const metadata = {
name: 'Legacy AI Service' ,
tags: [ 'ai' , 'legacy' , 'proxy' ],
dateCreated: new Date (),
description: 'Existing AI service with payment layer'
}
const api = {
endpoints: [
// Your actual service endpoints
{ POST: 'https://legacy-api.example.com/process' },
{ GET: 'https://legacy-api.example.com/status' }
],
openEndpoints: [
// Public endpoints (no payment required)
'https://legacy-api.example.com/health'
]
}
const price = getERC20PriceConfig ( 15_000_000 n , USDC_ADDRESS , builderAddress )
const credits = getFixedCreditsConfig ( 100 n , 1 n )
const { agentId , planId } = await payments . registerAgentAndPlan (
metadata ,
api ,
price ,
credits
)
console . log ( `
Proxy Integration Complete!
Your service is now accessible at:
https://proxy.nevermined.app/agent/ ${ agentId }
The proxy handles:
- Payment validation
- Credit redemption
- Access control
- Usage tracking
` )
}
Client Usage
Subscribers access your proxied service:
// For subscribers
async function useProxiedService () {
const payments = Payments . getInstance ({
nvmApiKey: process . env . NVM_API_KEY ,
environment: 'production'
})
// Purchase plan
await payments . orderPlan ( planId )
// Get access credentials
const options = await payments . getAgentHTTPOptions ( planId , agentId )
// Make requests through proxy
const response = await fetch ( options . neverminedProxyUri , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'Authorization' : `Bearer ${ options . accessToken } `
},
body: JSON . stringify ({
// Your original API payload
data: 'process this'
})
})
const result = await response . json ()
console . log ( result )
}
Error Handling
Implement comprehensive error handling for all integration patterns:
import { PaymentsError } from '@nevermined-io/payments'
// Express error handler
app . use (( error : Error , req : Request , res : Response , next : NextFunction ) => {
if ( error instanceof PaymentsError ) {
const statusMap = {
'INVALID_API_KEY' : 401 ,
'INSUFFICIENT_BALANCE' : 402 ,
'INVALID_PLAN_CONFIG' : 400 ,
'NETWORK_ERROR' : 503 ,
'VALIDATION_ERROR' : 400
}
const status = statusMap [ error . code ] || 500
return res . status ( status ). json ({
error: error . message ,
code: error . code ,
details: error . details
})
}
// Handle other errors
console . error ( 'Unexpected error:' , error )
res . status ( 500 ). json ({
error: 'Internal server error' ,
message: process . env . NODE_ENV === 'development' ? error . message : undefined
})
})
Request Tracking
Track and manage the lifecycle of subscriber requests for analytics, debugging, and compliance purposes.
Request Lifecycle Management
The SDK provides methods to track requests from initialization through completion:
// Initialize a new request when subscriber starts
const requestId = await payments . requests . initializeSubscriberRequest (
planId ,
agentId ,
subscriberAddress ,
{
method: 'POST' ,
endpoint: '/api/query' ,
timestamp: new Date (),
metadata: {
feature: 'advanced-analysis' ,
version: '1.0'
}
}
)
// Update request progress (optional)
await payments . requests . updateSubscriberRequest (
requestId ,
{
status: 'processing' ,
progress: 50 ,
checkpoint: 'Model inference started'
}
)
// Finish request and record results
await payments . requests . finishSubscriberRequest (
requestId ,
{
status: 'completed' ,
creditsUsed: 5 n ,
responseTime: 1250 , // milliseconds
metadata: {
tokensGenerated: 450 ,
modelUsed: 'gpt-4'
}
}
)
Integration with Request Handler
Add request tracking to your API endpoints:
app . post ( '/api/query' , authenticate ( agentId ), async ( req , res ) => {
let requestId : string | null = null
try {
// Initialize request tracking
requestId = await payments . requests . initializeSubscriberRequest (
req . nevermined . planId ,
req . nevermined . agentId ,
req . nevermined . subscriberAddress ,
{
method: req . method ,
endpoint: req . path ,
timestamp: new Date (),
metadata: {
prompt: req . body . prompt ,
parameters: req . body . parameters
}
}
)
// Update progress during processing
await payments . requests . updateSubscriberRequest ( requestId , {
status: 'processing' ,
progress: 25 ,
checkpoint: 'Request validated'
})
// Process AI request
const startTime = Date . now ()
const result = await processAIRequest ( req . body . prompt )
const responseTime = Date . now () - startTime
// Finish request tracking
await payments . requests . finishSubscriberRequest ( requestId , {
status: 'completed' ,
creditsUsed: 1 n ,
responseTime ,
metadata: {
outputLength: result . response . length ,
success: true
}
})
res . json ({
result ,
requestId ,
usage: {
creditsUsed: 1 ,
responseTime
}
})
} catch ( error ) {
// Track failed requests
if ( requestId ) {
await payments . requests . finishSubscriberRequest ( requestId , {
status: 'failed' ,
creditsUsed: 0 n ,
error: error . message ,
metadata: {
errorType: error . constructor . name
}
})
}
res . status ( 500 ). json ({ error: 'Processing failed' })
}
})
Analytics and Monitoring
Use request tracking data for insights:
// Get request history for a subscriber
async function getSubscriberUsageAnalytics (
subscriberAddress : string ,
planId : string
) {
// This would integrate with your analytics backend
// The request tracking provides the raw data
return {
totalRequests: 156 ,
successRate: 0.98 ,
averageResponseTime: 850 ,
creditsConsumed: 312 ,
popularFeatures: {
'basic-query' : 89 ,
'advanced-analysis' : 45 ,
'batch-processing' : 22
},
timeDistribution: {
// Requests by hour of day
}
}
}
Best Practices for Request Tracking
Performance Considerations
Don’t store sensitive user data in request metadata
Implement data retention policies
Allow users to request deletion of their tracking data
Comply with GDPR and other privacy regulations
Always finish requests even if they fail
Track different types of failures separately
Use request IDs for debugging and support
Implement retry logic for tracking failures
Request Tracking Middleware
Create reusable middleware for automatic request tracking:
export function createRequestTracker ( payments : Payments ) {
return ( agentId : string ) => {
return async ( req : NeverminedRequest , res : Response , next : NextFunction ) => {
if ( ! req . nevermined ) {
return next ()
}
const requestId = await payments . requests . initializeSubscriberRequest (
req . nevermined . planId ,
agentId ,
req . nevermined . subscriberAddress ,
{
method: req . method ,
endpoint: req . path ,
timestamp: new Date ()
}
)
// Store request ID for later use
req . nevermined . requestId = requestId
// Override res.json to track completion
const originalJson = res . json . bind ( res )
res . json = function ( data : any ) {
payments . requests . finishSubscriberRequest ( requestId , {
status: 'completed' ,
creditsUsed: req . nevermined . creditsUsed || 1 n ,
responseTime: Date . now () - req . nevermined . startTime
}). catch ( console . error )
return originalJson ( data )
}
// Track request start time
req . nevermined . startTime = Date . now ()
next ()
}
}
}
// Usage
const requestTracker = createRequestTracker ( payments )
app . post ( '/api/query' ,
authenticate ( agentId ),
requestTracker ( agentId ),
handler
)
Testing Your Integration
Unit Tests
import { Payments } from '@nevermined-io/payments'
describe ( 'Payment Integration' , () => {
let payments : Payments
beforeEach (() => {
payments = Payments . getInstance ({
nvmApiKey: process . env . NVM_TEST_API_KEY ,
environment: 'testing'
})
})
test ( 'should validate authorized requests' , async () => {
const isValid = await payments . isValidRequest (
testPlanId ,
testAgentId ,
testSubscriber ,
validSignature
)
expect ( isValid ). toBe ( true )
})
test ( 'should reject unauthorized requests' , async () => {
const isValid = await payments . isValidRequest (
testPlanId ,
testAgentId ,
'wrong-address' ,
'invalid-signature'
)
expect ( isValid ). toBe ( false )
})
})
Integration Tests
import request from 'supertest'
import { app } from './app'
describe ( 'API Integration' , () => {
test ( 'should return 402 without payment' , async () => {
const response = await request ( app )
. post ( '/api/query' )
. send ({ prompt: 'test' })
expect ( response . status ). toBe ( 402 )
expect ( response . body ). toHaveProperty ( 'paymentCard' )
})
test ( 'should process request with valid payment' , async () => {
const response = await request ( app )
. post ( '/api/query' )
. set ( 'x-nvm-query-signature' , validSignature )
. send ({
planId: testPlanId ,
agentId: testAgentId ,
subscriberAddress: testSubscriber ,
prompt: 'test query'
})
expect ( response . status ). toBe ( 200 )
expect ( response . body ). toHaveProperty ( 'result' )
expect ( response . body . usage ). toHaveProperty ( 'creditsRemaining' )
})
})
Production Checklist
Before going live:
Next Steps
Best Practices Security and performance recommendations
Go Live Deploy your integrated service
Examples See complete implementations
Troubleshooting Common issues and solutions