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

# How the x402 Facilitator Works

> Technical deep-dive into x402 verification and programmable settlement

This guide explains the end-to-end x402 flow from both the subscriber (client) and resource server (API/agent) perspectives.

<Note>
  For the complete technical specification, see the [x402 Smart Accounts Extension Spec](/docs/specs/x402-smart-accounts).
</Note>

If you're new to the programmable extension concepts, see:

* [Making x402 programmable](https://nevermined.ai/blog/making-x402-programmable)
* [Building Agentic Payments with Nevermined, x402, A2A, and AP2](https://nevermined.ai/blog/building-agentic-payments-with-nevermined-x402-a2a-and-ap2)

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

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

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

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    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
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    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')
    ```
  </Tab>
</Tabs>

### Step 2: Generate x402 access token

Use the Nevermined SDK to generate an x402 access token. The supported flow is **create-first**: create a delegation once with `createDelegation` (`provider` and `currency` are required), then request access tokens by passing its `delegationId`. Reuse the delegation until it expires or is exhausted.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { Payments } from '@nevermined-io/payments'

    const payments = Payments.getInstance({
      nvmApiKey: process.env.NVM_API_KEY,
      environment: 'sandbox'
    })

    // 1. Create a delegation once (provider + currency required; 'usdc' for erc4337).
    const delegation = await payments.delegation.createDelegation({
      provider: 'erc4337',
      spendingLimitCents: 10000,
      durationSecs: 604800,
      currency: 'usdc'
    })

    // 2. Request a token by delegationId (reuse it for subsequent requests).
    const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId, {
      delegationConfig: { delegationId: delegation.delegationId }
    })
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    from payments_py import Payments, PaymentOptions
    from payments_py.x402 import (
        CreateDelegationPayload,
        DelegationConfig,
        X402TokenOptions,
    )

    payments = Payments.get_instance(
        PaymentOptions(nvm_api_key=os.environ['NVM_API_KEY'], environment='sandbox')
    )

    # 1. Create a delegation once (provider + currency required; 'usdc' for erc4337).
    delegation = payments.delegation.create_delegation(
        CreateDelegationPayload(
            provider='erc4337',
            spending_limit_cents=10000,
            duration_secs=604800,
            currency='usdc',
        )
    )

    # 2. Request a token by delegationId (reuse it for subsequent requests).
    token_res = payments.x402.get_x402_access_token(
        plan_id,
        agent_id,
        token_options=X402TokenOptions(
            delegation_config=DelegationConfig(
                delegation_id=delegation.delegation_id
            )
        ),
    )
    access_token = token_res['accessToken']
    ```
  </Tab>
</Tabs>

<Warning>
  **Inline create-on-the-fly is deprecated.** Passing creation fields (`spendingLimitCents`, `durationSecs`, `providerPaymentMethodId`, `cardId`, `currency`) directly in `delegationConfig` instead of a `delegationId` still works but emits a runtime deprecation warning. Create the delegation first, then pass only `{ delegationId }`.
</Warning>

### Step 3: Retry request with payment header

Send the x402 access token in the `payment-signature` header:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    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)
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    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'])
    ```
  </Tab>
</Tabs>

## Resource server flow (API/agent side)

<Note>
  **Recommended:** For Express.js applications, use the [`paymentMiddleware`](/docs/integrate/add-to-your-agent/express) which handles all of this automatically with one line of code.
</Note>

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

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    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' })
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    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
    ```
  </Tab>
</Tabs>

### Step 2: Verify with the facilitator

Verify the x402 token with the facilitator. The facilitator extracts `planId` and subscriber address from the token:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    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' })
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    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
    ```
  </Tab>
</Tabs>

### Step 3: Execute Workload

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

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    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 })
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # 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
    ```
  </Tab>
</Tabs>

## Complete Request Lifecycle

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor Client
    participant Agent as AI Agent
    participant Facilitator
    participant Chain as Blockchain

    Client->>Agent: GET /resource (no payment)
    Agent->>Client: 402 + payment-required header

    Client->>Client: Create delegation + generate x402 token via SDK
    Client->>Agent: POST /resource + payment-signature header

    Agent->>Facilitator: POST /verify
    Facilitator->>Facilitator: Validate signature & session keys
    Facilitator->>Chain: Check balance
    Chain-->>Facilitator: Balance OK
    Facilitator-->>Agent: Verification OK

    Agent->>Agent: Execute AI workload

    Agent->>Facilitator: POST /settle
    Facilitator->>Chain: Execute burn operation
    Chain-->>Facilitator: Tx confirmed
    Facilitator-->>Agent: Settlement OK + txHash

    Agent-->>Client: Response + payment-response header
```

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

<CardGroup cols={2}>
  <Card title="Express.js Integration" icon="node-js" href="/docs/integrate/add-to-your-agent/express">
    One-line payment protection with Express middleware
  </Card>

  <Card title="Payment Models" icon="calculator" href="/docs/integrate/patterns/payment-models">
    Configure credits, subscriptions, and dynamic pricing
  </Card>

  <Card title="x402 Integration" icon="plug" href="/docs/development-guide/nevermined-x402">
    Complete integration guide with code examples
  </Card>

  <Card title="Technical Spec" icon="file-code" href="/docs/specs/x402-smart-accounts">
    Full x402 Smart Accounts specification
  </Card>
</CardGroup>
