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

# x402 Protocol

> Use x402 protocol for payment verification and settlement

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:erc4337`         | `eip155:84532`                    | Crypto payments  | ERC-4337 UserOps + session keys |
| `nvm:card-delegation` | `stripe` \| `braintree` \| `visa` | Fiat/credit card | Provider charge + 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()`. The `network` value within `nvm:card-delegation` is determined by which provider issued the delegation being consumed (`stripe`, `braintree`, or `visa`).

### Visa support

Visa delegations use the same `nvm:card-delegation` scheme and SDK surface as Stripe and Braintree, but two steps must happen in a **browser** before the SDK can consume them:

1. **Card enrolment** — the cardholder enrols a Visa card through VGS Collect (PCI-compliant iframe) in the Nevermined webapp. The card is bound to a Visa Agentic Token via the VGS Credential Management Platform.
2. **Delegation creation** — the cardholder approves a delegation via a WebAuthn/passkey (FIDO) device-binding ceremony embedded by Visa VTS. This produces a single-use `assuranceData` blob bound to the spending limit + duration + merchant context.

Both steps require a real DOM and a user gesture, so the SDK cannot perform them programmatically. Once a Visa delegation exists, the SDK consumes it identically to Stripe/Braintree — pass `delegation_id` to `DelegationConfig` and call `get_x402_access_token` as usual:

```python theme={null}
from payments_py.x402 import X402TokenOptions, DelegationConfig

result = subscriber_payments.x402.get_x402_access_token(
    plan_id,
    token_options=X402TokenOptions(
        scheme="nvm:card-delegation",
        network="visa",
        delegation_config=DelegationConfig(
            delegation_id="11111111-1111-1111-1111-111111111111",
        ),
    ),
)
```

`delegation_id` reuse is the **only** supported pattern for Visa — `create_delegation(provider="visa", ...)` is rejected by the backend without the browser-only `consumer_prompt` + `assurance_data` blobs the SDK has no way to produce. When a Visa creation call fails this way, `PaymentsError.code` carries the backend `BCK.VISA.0014` so consumers can branch programmatically.

## Generate Payment Permissions

### From Nevermined App

The easiest way to generate permissions is through the [Nevermined App Permissions page](https://nevermined.app/permissions/agent-permissions):

1. Navigate to the permissions page
2. Select your plan and agent
3. Configure limits (optional)
4. Generate the access token

### From SDK

```python theme={null}
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

# Create the delegation once (currency is required), then reuse it across plans.
delegation = payments.delegation.create_delegation(
    CreateDelegationPayload(
        provider="erc4337",
        spending_limit_cents=10000,  # $100
        duration_secs=604800,         # 1 week
        currency="usdc",
    )
)
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)
    )
)
```

> **Deprecated:** passing `spending_limit_cents` / `duration_secs` directly to
> `get_x402_access_token` (inline create-on-the-fly, a `DelegationConfig` with no
> `delegation_id`) emits a `DeprecationWarning` and will be removed in a future
> release. Create the delegation first as shown above, then pass only
> `delegation_id`.

### Card-Delegation Token Generation

For fiat plans using `nvm:card-delegation`, create the card delegation once
(`currency` is required), then request the token by `delegation_id`:

```python theme={null}
from payments_py.x402 import X402TokenOptions, DelegationConfig, CreateDelegationPayload

# Step 1 — create a USD card delegation (currency is required).
usd_delegation = payments.delegation.create_delegation(
    CreateDelegationPayload(
        provider="stripe",
        provider_payment_method_id="pm_1AbCdEfGhIjKlM",
        spending_limit_cents=10000,  # $100.00
        duration_secs=2592000,       # 30 days
        currency="usd",
        max_transactions=100,
    )
)

# Step 2 — request the token referencing the delegation id.
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(
            delegation_id=usd_delegation.delegation_id
        ),
    )
)
access_token = result['accessToken']

# EUR card delegation — same flow, currency="eur".
eur_delegation = payments.delegation.create_delegation(
    CreateDelegationPayload(
        provider="stripe",
        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,
    )
)
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(
            delegation_id=eur_delegation.delegation_id
        ),
    )
)
```

### Auto Scheme Resolution

Use `resolve_scheme()` to auto-detect the correct scheme from plan metadata:

```python theme={null}
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:

```python theme={null}
from payments_py.x402 import CreateDelegationPayload

# Create a crypto delegation (currency is required)
delegation = payments.delegation.create_delegation(
    CreateDelegationPayload(
        provider="erc4337",
        spending_limit_cents=10000,
        duration_secs=604800,
        currency="usdc",
    )
)
print(f"Delegation ID: {delegation.delegation_id}")

# List enrolled payment methods (every provider)
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)"

# Restrict the result to a single provider with the optional `provider` kwarg
stripe_methods = payments.delegation.list_payment_methods(provider="stripe")
```

`list_payment_methods()` accepts an optional `provider` keyword argument
(`'stripe' | 'braintree' | 'visa' | 'erc4337'`). When set, it is forwarded as a
`?provider=` query string and only methods backed by that provider are returned.
Omit it (the default) to return methods from every provider.

`PaymentMethodSummary` fields:

| Field       | Type  | Description                             |
| ----------- | ----- | --------------------------------------- |
| `id`        | `str` | Payment method ID (e.g., `pm_...`)      |
| `brand`     | `str` | Card brand (e.g., `visa`, `mastercard`) |
| `last4`     | `str` | Last 4 digits of the card number        |
| `exp_month` | `int` | Card expiration month                   |
| `exp_year`  | `int` | Card expiration year                    |

### Token Structure

The x402 token is a base64-encoded JSON document:

```json theme={null}
{
  "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:

```python theme={null}
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_valid`         | `bool` | Whether verification passed                    |
| `invalid_reason`   | `str`  | Reason for invalidity (if `is_valid` is false) |
| `payer`            | `str`  | Payer's wallet address                         |
| `agent_request_id` | `str`  | Agent request ID for observability tracking    |

## Settle Payment Permissions

Settlement burns credits after successfully processing a request:

```python theme={null}
# 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                                |
| ------------------- | ------ | ------------------------------------------ |
| `success`           | `bool` | Whether settlement succeeded               |
| `error_reason`      | `str`  | Reason for failure (if `success` is false) |
| `payer`             | `str`  | Payer's wallet address                     |
| `transaction`       | `str`  | Blockchain transaction hash                |
| `credits_redeemed`  | `str`  | Credits that were burned                   |
| `remaining_balance` | `str`  | Credits remaining                          |

## Payment Required Object

The `X402PaymentRequired` object specifies what payment is required. The `scheme` and `network` fields vary by payment type:

```python theme={null}
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

```python theme={null}
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

```python theme={null}
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

```mermaid theme={null}
sequenceDiagram
    participant Subscriber
    participant Agent
    participant Nevermined

    Subscriber->>Agent: GET /api/process (no token)
    Agent-->>Subscriber: 402 Payment Required<br/>{paymentRequired: {...}}

    Subscriber->>Nevermined: get_x402_access_token()
    Nevermined-->>Subscriber: {accessToken: "..."}

    Subscriber->>Agent: GET /api/process<br/>payment-signature: token
    Agent->>Nevermined: verify_permissions()
    Nevermined-->>Agent: {isValid: true}

    Note over Agent: Process request

    Agent->>Nevermined: settle_permissions()
    Nevermined-->>Agent: {success: true}

    Agent-->>Subscriber: 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

<CardGroup cols={2}>
  <Card title="Request Validation" icon="arrow-right" href="/docs/api-reference/python/validation-module">
    More validation patterns
  </Card>

  <Card title="MCP Integration" icon="arrow-right" href="/docs/api-reference/python/mcp-module">
    x402 with MCP servers
  </Card>

  <Card title="OAuth 401 vs. payment-required" icon="arrow-right" href="/docs/api-reference/python/mcp-module#oauth-401-vs-payment-required">
    In-band x402 v2 MCP signaling
  </Card>
</CardGroup>
