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 covers the x402 payment protocol for verifying permissions and settling payments.
Overview
x402 is a payment protocol that enables:
Permission Generation : Subscribers create access tokens for agents
Permission Verification : Agents verify tokens without burning credits
Permission Settlement : Agents burn credits after completing work
The protocol is named after HTTP status code 402 (Payment Required).
Supported Schemes
Nevermined supports two x402 payment schemes:
Scheme Network Use Case Settlement nvm:erc4337eip155:84532Crypto payments ERC-4337 UserOps + session keys nvm:card-delegationstripeFiat/credit card Stripe PaymentIntent + credit burn
The scheme is determined by the plan’s pricing configuration. Plans with isCrypto: false use nvm:card-delegation; all others use nvm:erc4337. The SDK auto-detects the scheme via resolve_scheme().
Generate Payment Permissions
From Nevermined App
The easiest way to generate permissions is through the Nevermined App Permissions page :
Navigate to the permissions page
Select your plan and agent
Configure limits (optional)
Generate the access token
From SDK
from payments_py import Payments, PaymentOptions
payments = Payments.get_instance(
PaymentOptions( nvm_api_key = "nvm:subscriber-key" , environment = "sandbox" )
)
# Basic token generation
result = payments.x402.get_x402_access_token(
plan_id = "your-plan-id" ,
agent_id = "agent-id"
)
access_token = result[ 'accessToken' ]
# With delegation config (crypto — erc4337)
from payments_py.x402 import X402TokenOptions, DelegationConfig, CreateDelegationPayload
# Pattern A — auto-create delegation
result = payments.x402.get_x402_access_token(
plan_id = "your-plan-id" ,
agent_id = "agent-id" ,
token_options = X402TokenOptions(
delegation_config = DelegationConfig(
spending_limit_cents = 10000 , # $100
duration_secs = 604800 # 1 week
)
)
)
# Pattern B — explicit create + reuse across plans
delegation = payments.delegation.create_delegation(
CreateDelegationPayload(
provider = "erc4337" ,
spending_limit_cents = 10000 ,
duration_secs = 604800
)
)
result = payments.x402.get_x402_access_token(
plan_id = "your-plan-id" ,
agent_id = "agent-id" ,
token_options = X402TokenOptions(
delegation_config = DelegationConfig( delegation_id = delegation.delegation_id)
)
)
Card-Delegation Token Generation
For fiat plans using nvm:card-delegation, pass X402TokenOptions with a DelegationConfig:
from payments_py.x402 import X402TokenOptions, DelegationConfig
# USD card delegation
result = payments.x402.get_x402_access_token(
plan_id = "your-plan-id" ,
agent_id = "agent-id" ,
token_options = X402TokenOptions(
scheme = "nvm:card-delegation" ,
delegation_config = DelegationConfig(
provider_payment_method_id = "pm_1AbCdEfGhIjKlM" ,
spending_limit_cents = 10000 , # $100.00
duration_secs = 2592000 , # 30 days
currency = "usd" ,
max_transactions = 100
)
)
)
access_token = result[ 'accessToken' ]
# EUR card delegation
eur_result = payments.x402.get_x402_access_token(
plan_id = "your-plan-id" ,
agent_id = "agent-id" ,
token_options = X402TokenOptions(
scheme = "nvm:card-delegation" ,
delegation_config = DelegationConfig(
provider_payment_method_id = "pm_1AbCdEfGhIjKlM" ,
spending_limit_cents = 10000 , # €100.00 (in euro cents)
duration_secs = 2592000 , # 30 days
currency = "eur" ,
max_transactions = 100
)
)
)
Auto Scheme Resolution
Use resolve_scheme() to auto-detect the correct scheme from plan metadata:
from payments_py.x402.resolve_scheme import resolve_scheme
# Auto-detect scheme from plan metadata (cached for 5 minutes)
scheme = resolve_scheme(payments, plan_id = "your-plan-id" )
# Returns "nvm:erc4337" for crypto plans, "nvm:card-delegation" for fiat plans
# Explicit override
scheme = resolve_scheme(payments, plan_id = "your-plan-id" , explicit_scheme = "nvm:card-delegation" )
DelegationAPI
Create delegations and list enrolled payment methods:
from payments_py.x402 import CreateDelegationPayload
# Create a crypto delegation
delegation = payments.delegation.create_delegation(
CreateDelegationPayload(
provider = "erc4337" ,
spending_limit_cents = 10000 ,
duration_secs = 604800
)
)
print ( f "Delegation ID: { delegation.delegation_id } " )
# List enrolled payment methods (for card delegation)
methods = payments.delegation.list_payment_methods()
for method in methods:
print ( f " { method.brand } **** { method.last4 } (expires { method.exp_month } / { method.exp_year } )" )
# e.g., "visa ****4242 (expires 12/2027)"
PaymentMethodSummary fields:
Field Type Description idstrPayment method ID (e.g., pm_...) brandstrCard brand (e.g., visa, mastercard) last4strLast 4 digits of the card number exp_monthintCard expiration month exp_yearintCard expiration year
Token Structure
The x402 token is a base64-encoded JSON document:
{
"payload" : {
"authorization" : {
"from" : "0xSubscriberAddress" ,
"planId" : "plan-123" ,
"agentId" : "agent-456"
},
"sessionKey" : {
"address" : "0xSessionKeyAddress" ,
"permissions" : [ "order" , "burn" ],
"limits" : {
"redemptionLimit" : 100 ,
"orderLimit" : "1000000000000000000"
}
}
},
"signature" : "0x..."
}
Verify Payment Permissions
Verification checks if a subscriber has valid permissions without burning credits:
from payments_py import Payments, PaymentOptions
from payments_py.x402.helpers import build_payment_required
payments = Payments.get_instance(
PaymentOptions( nvm_api_key = "nvm:agent-key" , environment = "sandbox" )
)
# Build the 402 Payment Required specification
payment_required = build_payment_required(
plan_id = "your-plan-id" ,
endpoint = "https://your-api.com/endpoint" ,
agent_id = "your-agent-id" ,
http_verb = "POST"
)
# Verify the token
verification = payments.facilitator.verify_permissions(
payment_required = payment_required,
x402_access_token = access_token,
max_amount = "1" # Optional: max credits to verify
)
if verification.is_valid:
print ( f "Valid! Payer: { verification.payer } " )
else :
print ( f "Invalid: { verification.invalid_reason } " )
Verification Response
Field Type Description is_validboolWhether verification passed invalid_reasonstrReason for invalidity (if is_valid is false) payerstrPayer’s wallet address agent_request_idstrAgent request ID for observability tracking
Settle Payment Permissions
Settlement burns credits after successfully processing a request:
# After processing the request successfully
settlement = payments.facilitator.settle_permissions(
payment_required = payment_required,
x402_access_token = access_token,
max_amount = "1" , # Credits to burn
agent_request_id = "request-123" # Optional: for tracking
)
if settlement.success:
print ( f "Settled! Credits burned: { settlement.credits_redeemed } " )
print ( f "Transaction: { settlement.transaction } " )
print ( f "Remaining: { settlement.remaining_balance } " )
else :
print ( f "Settlement failed: { settlement.error_reason } " )
Settlement Response
Field Type Description successboolWhether settlement succeeded error_reasonstrReason for failure (if success is false) payerstrPayer’s wallet address transactionstrBlockchain transaction hash credits_redeemedstrCredits that were burned remaining_balancestrCredits remaining
Payment Required Object
The X402PaymentRequired object specifies what payment is required. The scheme and network fields vary by payment type:
from payments_py.x402.types import X402PaymentRequired, X402Resource, X402Scheme, X402SchemeExtra
# Crypto plan (nvm:erc4337)
payment_required = X402PaymentRequired(
x402_version = 2 ,
resource = X402Resource(
url = "https://your-api.com/endpoint" ,
description = "Protected endpoint" # Optional
),
accepts = [
X402Scheme(
scheme = "nvm:erc4337" ,
network = "eip155:84532" , # Base Sepolia
plan_id = "your-plan-id" ,
extra = X402SchemeExtra(
http_verb = "POST" , # HTTP method goes in extra, not resource
agent_id = "agent-123" # Optional
)
)
],
extensions = {}
)
# Fiat plan (nvm:card-delegation)
payment_required_fiat = X402PaymentRequired(
x402_version = 2 ,
resource = X402Resource(
url = "https://your-api.com/endpoint" ,
description = "Protected endpoint"
),
accepts = [
X402Scheme(
scheme = "nvm:card-delegation" ,
network = "stripe" ,
plan_id = "your-plan-id" ,
extra = X402SchemeExtra(
http_verb = "POST" ,
agent_id = "agent-123"
)
)
],
extensions = {}
)
Using the Helpers
from payments_py.x402.helpers import build_payment_required, build_payment_required_for_plans
# Single plan (scheme auto-detected from plan metadata when omitted)
payment_required = build_payment_required(
plan_id = "your-plan-id" ,
endpoint = "https://api.example.com/tasks" ,
agent_id = "agent-123" ,
http_verb = "POST"
)
# Explicit scheme override
payment_required = build_payment_required(
plan_id = "your-plan-id" ,
endpoint = "https://api.example.com/tasks" ,
agent_id = "agent-123" ,
http_verb = "POST" ,
scheme = "nvm:card-delegation" # Force fiat/Stripe scheme
)
# Multiple plans — creates one entry per plan in accepts[]
payment_required = build_payment_required_for_plans(
plan_ids = [ "plan-basic" , "plan-premium" ],
endpoint = "https://api.example.com/tasks" ,
agent_id = "agent-123" ,
http_verb = "POST"
)
For a single plan, build_payment_required_for_plans delegates to build_payment_required internally. When scheme is omitted, the network defaults to eip155:84532 (Base Sepolia). When scheme="nvm:card-delegation", the network is automatically set to stripe.
Complete Workflow Example
from payments_py import Payments, PaymentOptions
from payments_py.x402.helpers import build_payment_required
from flask import Flask, request, jsonify
app = Flask( __name__ )
# Agent's payments instance
agent_payments = Payments.get_instance(
PaymentOptions( nvm_api_key = "nvm:agent-key" , environment = "sandbox" )
)
PLAN_ID = "your-plan-id"
AGENT_ID = "your-agent-id"
@app.route ( '/api/process' , methods = [ 'POST' ])
def process_request ():
# 1. Extract x402 token from payment-signature header
token = request.headers.get( 'payment-signature' , '' )
if not token:
return jsonify({ 'error' : 'Missing payment-signature header' }), 402
# 2. Build payment requirement
payment_required = build_payment_required(
plan_id = PLAN_ID ,
endpoint = request.url,
agent_id = AGENT_ID ,
http_verb = request.method
)
# 3. Verify (doesn't burn credits)
verification = agent_payments.facilitator.verify_permissions(
payment_required = payment_required,
x402_access_token = token,
max_amount = "1"
)
if not verification.is_valid:
return jsonify({
'error' : 'Payment required' ,
'details' : verification.invalid_reason,
'paymentRequired' : payment_required.model_dump()
}), 402
# 4. Process the request
try :
result = do_expensive_work(request.json)
except Exception as e:
# Don't settle on failure
return jsonify({ 'error' : str (e)}), 500
# 5. Settle (burn credits) on success
settlement = agent_payments.facilitator.settle_permissions(
payment_required = payment_required,
x402_access_token = token,
max_amount = "1"
)
return jsonify({
'result' : result,
'creditsUsed' : settlement.credits_redeemed,
'remainingBalance' : settlement.remaining_balance
})
def do_expensive_work ( data ):
# Your processing logic
return { 'processed' : True }
if __name__ == '__main__' :
app.run( port = 8080 )
HTTP Flow
Best Practices
Always verify before processing : Don’t do expensive work without verification
Only settle on success : Don’t burn credits if processing fails
Use agent_request_id : Include request IDs for tracking and debugging
Handle 402 responses : Return proper payment required responses with scheme info
Cache verifications carefully : Tokens can be used multiple times until limits are reached
Error Codes
Error Description Resolution invalid_tokenToken is malformed Generate a new token expired_tokenToken has expired Generate a new token insufficient_balanceNot enough credits Order more credits invalid_planPlan ID mismatch Use correct plan ID invalid_agentAgent ID mismatch Use correct agent ID
Next Steps
Request Validation More validation patterns
MCP Integration x402 with MCP servers