Skip to content

x402 Protocol

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).

Generate Payment Permissions

From Nevermined App

The easiest way to generate permissions is through the Nevermined App Permissions page:

  1. Navigate to the permissions page
  2. Select your plan and agent
  3. Configure limits (optional)
  4. 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']

# Advanced: with limits
result = payments.x402.get_x402_access_token(
    plan_id="your-plan-id",
    agent_id="agent-id",
    redemption_limit=100,  # Max 100 requests
    order_limit="1000000000000000000",  # Max 1 token spend
    expiration="2025-12-31T23:59:59Z"  # Expires end of 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! Subscriber: {verification.subscriber_address}")
    print(f"Balance: {verification.balance}")
else:
    print(f"Invalid: {verification.error}")

Verification Response

Field Type Description
is_valid bool Whether verification passed
subscriber_address str Subscriber's wallet address
plan_id str Plan being used
balance int Current credit balance
error str Error message if invalid

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.tx_hash}")
    print(f"Remaining: {settlement.remaining_balance}")
else:
    print(f"Settlement failed: {settlement.error}")

Settlement Response

Field Type Description
success bool Whether settlement succeeded
credits_redeemed int Credits that were burned
tx_hash str Blockchain transaction hash
remaining_balance int Credits remaining

Payment Required Object

The X402PaymentRequired object specifies what payment is required:

from payments_py.x402.types import X402PaymentRequired, X402Scheme

payment_required = X402PaymentRequired(
    x402_version=2,
    accepts=[
        X402Scheme(
            scheme="nvm:erc4337",
            network="eip155:84532",  # Base Sepolia
            plan_id="your-plan-id"
        )
    ],
    extensions={}
)

Using the Helper

from payments_py.x402.helpers import build_payment_required

# Simpler way to build payment required
payment_required = build_payment_required(
    plan_id="your-plan-id",
    endpoint="https://api.example.com/tasks",
    agent_id="agent-123",
    http_verb="POST"
)

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.error,
            '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

┌─────────────┐                  ┌─────────────┐                  ┌─────────────┐
│  Subscriber │                  │    Agent    │                  │  Nevermined │
└──────┬──────┘                  └──────┬──────┘                  └──────┬──────┘
       │                                │                                │
       │ GET /api/process               │                                │
       │ (no token)                     │                                │
       │ ─────────────────────────────► │                                │
       │                                │                                │
       │ ◄───────────────────────────── │                                │
       │ 402 Payment Required           │                                │
       │ {paymentRequired: {...}}       │                                │
       │                                │                                │
       │ get_x402_access_token()        │                                │
       │ ─────────────────────────────────────────────────────────────► │
       │                                │                                │
       │ ◄───────────────────────────────────────────────────────────── │
       │ {accessToken: "..."}           │                                │
       │                                │                                │
       │ GET /api/process               │                                │
       │ payment-signature: <token>     │                                │
       │ ─────────────────────────────► │                                │
       │                                │                                │
       │                                │ verify_permissions()           │
       │                                │ ─────────────────────────────► │
       │                                │                                │
       │                                │ ◄───────────────────────────── │
       │                                │ {isValid: true}                │
       │                                │                                │
       │                                │ [process request]              │
       │                                │                                │
       │                                │ settle_permissions()           │
       │                                │ ─────────────────────────────► │
       │                                │                                │
       │                                │ ◄───────────────────────────── │
       │                                │ {success: true}                │
       │                                │                                │
       │ ◄───────────────────────────── │                                │
       │ 200 OK {result: ...}           │                                │
       │                                │                                │

Best Practices

  1. Always verify before processing: Don't do expensive work without verification

  2. Only settle on success: Don't burn credits if processing fails

  3. Use agent_request_id: Include request IDs for tracking and debugging

  4. Handle 402 responses: Return proper payment required responses with scheme info

  5. Cache verifications carefully: Tokens can be used multiple times until limits are reached

Error Codes

Error Description Resolution
invalid_token Token is malformed Generate a new token
expired_token Token has expired Generate a new token
insufficient_balance Not enough credits Order more credits
invalid_plan Plan ID mismatch Use correct plan ID
invalid_agent Agent ID mismatch Use correct agent ID

Next Steps