Documentation Index Fetch the complete documentation index at: https://docs.nevermined.app/llms.txt
Use this file to discover all available pages before exploring further.
This guide explains the end-to-end x402 flow from both the subscriber (client) and resource server (API/agent) perspectives.
If you’re new to the programmable extension concepts, see:
The Nevermined x402 programmable extension
Nevermined extends x402 with the nvm:erc4337 scheme, enabling programmable settlement (credits/subscriptions/PAYG) using ERC-4337 smart accounts and session keys.
PaymentRequired Response (402)
When a server requires payment, it returns a 402 response with a payment-required header:
{
"x402Version" : 2 ,
"error" : "Payment required to access resource" ,
"resource" : {
"url" : "/api/v1/agents/80918427.../tasks" ,
"description" : "AI agent task execution"
},
"accepts" : [{
"scheme" : "nvm:erc4337" ,
"network" : "eip155:84532" ,
"planId" : "44742763076047497640080230236781474129970992727896593861997347135613135571071" ,
"extra" : {
"version" : "1" ,
"agentId" : "80918427023170428029540261117198154464497879145267720259488529685089104529015"
}
}],
"extensions" : {}
}
PaymentPayload (Client Response)
The client responds with a payment-signature header containing the x402 access token:
{
"x402Version" : 2 ,
"accepted" : {
"scheme" : "nvm:erc4337" ,
"network" : "eip155:84532" ,
"planId" : "44742763076047497640080230236781474129970992727896593861997347135613135571071" ,
"extra" : {
"version" : "1" ,
"agentId" : "80918427023170428029540261117198154464497879145267720259488529685089104529015"
}
},
"payload" : {
"signature" : "0x01845ADb2C711129d4f3966735eD98a9F09fC4cE..." ,
"authorization" : {
"from" : "0xD4f58B60330bC59cB0A07eE6A1A66ad64244eC8c" ,
"sessionKeysProvider" : "zerodev" ,
"sessionKeys" : [
{ "id" : "order" , "data" : "0x20a13d82dd9ee289..." },
{ "id" : "redeem" , "data" : "0x68e8e34d65914908..." }
]
}
},
"extensions" : {}
}
Subscriber flow (client side)
Step 1: Discover payment requirements (HTTP 402)
When calling a protected endpoint, the server returns a 402 Payment Required response with the payment-required header containing the payment requirements.
import { X402_HEADERS } from '@nevermined-io/payments/express'
// Call protected endpoint - get 402 response
const response = await fetch ( 'https://api.example.com/protected' )
if ( response . status === 402 ) {
// Decode the payment-required header
const paymentRequired = JSON . parse (
Buffer . from ( response . headers . get ( X402_HEADERS . PAYMENT_REQUIRED ) ! , 'base64' ). toString ()
)
// Extract planId and agentId from accepts array
const { planId , extra } = paymentRequired . accepts [ 0 ]
const agentId = extra ?. agentId
}
import base64
import json
import requests
response = requests.get( 'https://api.example.com/protected' )
if response.status_code == 402 :
# Decode the payment-required header
payment_required = json.loads(
base64.b64decode(response.headers.get( 'payment-required' )).decode()
)
# Extract planId and agentId from accepts array
plan_id = payment_required[ 'accepts' ][ 0 ][ 'planId' ]
agent_id = payment_required[ 'accepts' ][ 0 ].get( 'extra' , {}).get( 'agentId' )
Step 2: Generate x402 access token
Use the Nevermined SDK to generate an x402 access token. Token generation requires a delegationConfig that controls spending limits and duration. You can either auto-create a delegation inline or reuse an existing one.
import { Payments } from '@nevermined-io/payments'
const payments = Payments . getInstance ({
nvmApiKey: process . env . NVM_API_KEY ,
environment: 'sandbox'
})
// Pattern A: Auto-create a delegation inline
const { accessToken } = await payments . x402 . getX402AccessToken ( planId , agentId , {
delegationConfig: { spendingLimitCents: 10000 , durationSecs: 604800 }
})
// Pattern B: Create a delegation explicitly, then reuse its ID
const delegation = await payments . delegation . createDelegation ({
provider: 'erc4337' ,
spendingLimitCents: 10000 ,
durationSecs: 604800
})
const { accessToken : token } = await payments . x402 . getX402AccessToken ( planId , agentId , {
delegationConfig: { delegationId: delegation . delegationId }
})
import os
from payments_py import Payments, PaymentOptions
payments = Payments.get_instance(
PaymentOptions( nvm_api_key = os.environ[ 'NVM_API_KEY' ], environment = 'sandbox' )
)
# Pattern A: Auto-create a delegation inline
token_res = payments.x402.get_x402_access_token(plan_id, agent_id, {
'delegationConfig' : { 'spendingLimitCents' : 10000 , 'durationSecs' : 604800 }
})
access_token = token_res[ 'accessToken' ]
# Pattern B: Create a delegation explicitly, then reuse its ID
delegation = payments.delegation.create_delegation({
'provider' : 'erc4337' ,
'spendingLimitCents' : 10000 ,
'durationSecs' : 604800
})
token_res = payments.x402.get_x402_access_token(plan_id, agent_id, {
'delegationConfig' : { 'delegationId' : delegation[ 'delegationId' ] }
})
access_token = token_res[ 'accessToken' ]
Send the x402 access token in the payment-signature header:
import { X402_HEADERS } from '@nevermined-io/payments/express'
const result = await fetch ( 'https://api.example.com/protected' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
[ X402_HEADERS . PAYMENT_SIGNATURE ]: accessToken
},
body: JSON . stringify ({ prompt: 'Hello' })
})
// Decode the settlement receipt from payment-response header
if ( result . ok ) {
const settlement = JSON . parse (
Buffer . from ( result . headers . get ( X402_HEADERS . PAYMENT_RESPONSE ) ! , 'base64' ). toString ()
)
console . log ( 'Credits used:' , settlement . creditsRedeemed )
}
result = requests.post(
'https://api.example.com/protected' ,
headers = {
'Content-Type' : 'application/json' ,
'payment-signature' : access_token
},
json = { 'prompt' : 'Hello' }
)
# Decode the settlement receipt from payment-response header
if result.ok:
settlement = json.loads(
base64.b64decode(result.headers.get( 'payment-response' )).decode()
)
print ( 'Credits used:' , settlement[ 'creditsRedeemed' ])
Resource server flow (API/agent side)
Recommended: For Express.js applications, use the paymentMiddleware which handles all of this automatically with one line of code.
Step 1: Return 402 when payment is missing
If the payment header is not present, respond with 402 and set the payment-required header with your payment requirements:
import { X402_HEADERS } from '@nevermined-io/payments/express'
const x402Token = req . headers [ 'payment-signature' ]
if ( ! x402Token ) {
const paymentRequired = {
x402Version: 2 ,
error: 'Payment required to access resource' ,
resource: { url: req . path },
accepts: [{
scheme: 'nvm:erc4337' ,
network: 'eip155:84532' ,
planId: process . env . NVM_PLAN_ID ,
extra: {
version: '1' ,
agentId: process . env . NVM_AGENT_ID
}
}],
extensions: {}
}
res . set ( X402_HEADERS . PAYMENT_REQUIRED , Buffer . from ( JSON . stringify ( paymentRequired )). toString ( 'base64' ))
return res . status ( 402 ). json ({ error: 'Payment Required' })
}
x402_token = request.headers.get( 'payment-signature' )
if not x402_token:
payment_required = {
'x402Version' : 2 ,
'error' : 'Payment required to access resource' ,
'resource' : { 'url' : request.path},
'accepts' : [{
'scheme' : 'nvm:erc4337' ,
'network' : 'eip155:84532' ,
'planId' : os.environ[ 'NVM_PLAN_ID' ],
'extra' : {
'version' : '1' ,
'agentId' : os.environ.get( 'NVM_AGENT_ID' )
}
}],
'extensions' : {}
}
response = jsonify({ 'error' : 'Payment Required' })
response.headers[ 'payment-required' ] = base64.b64encode(
json.dumps(payment_required).encode()
).decode()
return response, 402
Step 2: Verify with the facilitator
Verify the x402 token with the facilitator. The facilitator extracts planId and subscriber address from the token:
import { Payments } from '@nevermined-io/payments'
const payments = Payments . getInstance ({
nvmApiKey: process . env . NVM_API_KEY ,
environment: 'sandbox'
})
// Verify with facilitator - extracts planId/subscriberAddress from token
const verification = await payments . facilitator . verifyPermissions ({
x402AccessToken: x402Token ,
maxAmount: BigInt ( creditsRequired )
})
if ( ! verification . isValid ) {
return res . status ( 402 ). json ({ error: 'Payment verification failed' })
}
from payments_py import Payments, PaymentOptions
payments = Payments.get_instance(
PaymentOptions( nvm_api_key = os.environ[ 'NVM_API_KEY' ], environment = 'sandbox' )
)
# Verify with facilitator - extracts planId/subscriberAddress from token
verification = payments.facilitator.verify_permissions(
x402_access_token = x402_token,
max_amount = credits_required
)
if not verification.is_valid:
return jsonify({ 'error' : 'Payment verification failed' }), 402
Step 3: Execute Workload
// Only execute after verification succeeds
const result = await processAIRequest ( req . body )
Step 4: Settle Payment
Settle after work is complete and return the settlement receipt in the payment-response header:
import { X402_HEADERS } from '@nevermined-io/payments/express'
// Settle after work is complete
const settlement = await payments . facilitator . settlePermissions ({
x402AccessToken: x402Token ,
maxAmount: BigInt ( actualCreditsUsed )
})
// Set payment-response header with settlement receipt
const settlementReceipt = {
success: true ,
creditsRedeemed: actualCreditsUsed ,
transactionHash: settlement . txHash
}
res . set ( X402_HEADERS . PAYMENT_RESPONSE , Buffer . from ( JSON . stringify ( settlementReceipt )). toString ( 'base64' ))
// Return response
res . json ({ result })
# Settle after work is complete
settlement = payments.facilitator.settle_permissions(
x402_access_token = x402_token,
max_amount = actual_credits_used
)
# Set payment-response header with settlement receipt
settlement_receipt = {
'success' : True ,
'creditsRedeemed' : actual_credits_used,
'transactionHash' : settlement.tx_hash
}
response = jsonify({ 'result' : result})
response.headers[ 'payment-response' ] = base64.b64encode(
json.dumps(settlement_receipt).encode()
).decode()
return response
Complete Request Lifecycle
Error Handling
Error HTTP Status Cause Missing payment header 402 No payment-signature header Invalid signature 402 Signature verification failed Insufficient balance 402 User needs to purchase more credits Expired session 402 Session key has expired Settlement failed 500 On-chain transaction failed
Next Steps
Express.js Integration One-line payment protection with Express middleware
Payment Models Configure credits, subscriptions, and dynamic pricing
x402 Integration Complete integration guide with code examples
Technical Spec Full x402 Smart Accounts specification