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

# Request Validation

> Verify X402 access tokens and settle payments when subscribers access your agents

# Validation of Requests

This guide explains how AI agents validate incoming requests and settle payments using the Nevermined Facilitator. Agent builders use these methods to verify subscriber access and burn credits.

> 🔐 **Never log incoming tokens.** When you extract the `payment-signature` header, treat it as a bearer secret: do not echo it in error messages, debug output, or telemetry. Configure your log/trace exporters to redact `payment-signature`, `authorization`, and `cookie` headers by default.

## Overview

The validation flow consists of:

1. **Extract Token**: Read X402 access token from request headers
2. **Build Payment Required**: Create payment requirement specification
3. **Verify Permissions**: Check if subscriber has valid access
4. **Execute Task**: Process the agent request
5. **Settle Permissions**: Burn credits after successful execution
6. **Return Response**: Send result or 402 error to subscriber

## Receiving Requests

Extract the X402 access token from request headers. The X402 v2 spec defines the `payment-signature` header:

```typescript theme={null}
import express from 'express'

const app = express()
app.use(express.json())

app.post('/api/v1/tasks', async (req, res) => {
  // Extract token from payment-signature header (X402 v2)
  const accessToken = req.headers['payment-signature'] as string

  // Alternative: Authorization header
  // const accessToken = req.headers.authorization?.replace('Bearer ', '')

  if (!accessToken) {
    return res.status(402).json({ error: 'Payment required' })
  }

  // Process request...
})
```

## Build Payment Required

Use the `buildPaymentRequired` helper to create the payment specification:

```typescript theme={null}
import { buildPaymentRequired } from '@nevermined-io/payments'

const paymentRequired = buildPaymentRequired(planId, {
  endpoint: req.url,           // Resource URL
  agentId: agentId,            // Your agent ID
  httpVerb: req.method,        // HTTP method (POST, GET, etc.)
  network: 'eip155:84532',     // Optional: blockchain network
  description: 'Task API'      // Optional: description
})
```

## Verify Permissions

Before executing the request, verify the subscriber has valid access:

```typescript theme={null}
import { Payments, EnvironmentName } from '@nevermined-io/payments'
import { buildPaymentRequired } from '@nevermined-io/payments'

const agentPayments = Payments.getInstance({
  nvmApiKey: process.env.NVM_API_KEY!,  // Builder/agent API key
  environment: 'sandbox' as EnvironmentName,
})

app.post('/api/v1/tasks', async (req, res) => {
  const accessToken = req.headers['payment-signature'] as string

  if (!accessToken) {
    return res.status(402).json({ error: 'Payment required' })
  }

  // Build payment required
  const paymentRequired = buildPaymentRequired(planId, {
    endpoint: req.url,
    agentId: agentId,
    httpVerb: req.method,
  })

  try {
    // Verify permissions
    const verification = await agentPayments.facilitator.verifyPermissions({
      paymentRequired,
      x402AccessToken: accessToken,
      maxAmount: 1n,  // Max credits to verify
    })

    if (!verification.isValid) {
      return res.status(402).json({ error: 'Insufficient credits or invalid token' })
    }

    // Verification successful, proceed with request...

  } catch (error) {
    return res.status(500).json({ error: error.message })
  }
})
```

## Settle Permissions

After successfully processing the request, burn the credits:

```typescript theme={null}
app.post('/api/v1/tasks', async (req, res) => {
  const accessToken = req.headers['payment-signature'] as string

  if (!accessToken) {
    return res.status(402).json({ error: 'Payment required' })
  }

  const paymentRequired = buildPaymentRequired(planId, {
    endpoint: req.url,
    agentId: agentId,
    httpVerb: req.method,
  })

  try {
    // Verify
    const verification = await agentPayments.facilitator.verifyPermissions({
      paymentRequired,
      x402AccessToken: accessToken,
      maxAmount: 1n,
    })

    if (!verification.isValid) {
      return res.status(402).json({ error: 'Insufficient credits' })
    }

    // Execute task
    const result = await processTask(req.body)

    // Settle permissions (burn credits)
    const settlement = await agentPayments.facilitator.settlePermissions({
      paymentRequired,
      x402AccessToken: accessToken,
      maxAmount: 1n,  // Credits to burn
    })

    // Return success with payment metadata
    return res.json({
      result,
      transaction: settlement.transaction,
      creditsUsed: settlement.creditsRedeemed,
      remainingBalance: settlement.remainingBalance,
    })

  } catch (error) {
    return res.status(500).json({ error: error.message })
  }
})
```

## Return 402 Payment Required

When payment is required, return HTTP 402 with the `PAYMENT-REQUIRED` header:

```typescript theme={null}
import { buildPaymentRequired } from '@nevermined-io/payments'

function return402Response(res: express.Response, planId: string, agentId: string, endpoint: string) {
  const paymentRequired = buildPaymentRequired(planId, {
    endpoint,
    agentId,
    httpVerb: 'POST',
  })

  // Encode payment required as base64
  const paymentRequiredHeader = Buffer.from(
    JSON.stringify(paymentRequired)
  ).toString('base64')

  res.writeHead(402, {
    'Content-Type': 'application/json',
    'PAYMENT-REQUIRED': paymentRequiredHeader,
  })

  res.end(JSON.stringify({
    error: 'Payment required',
    message: 'Valid X402 access token required in payment-signature header',
  }))
}

// Usage
app.post('/api/v1/tasks', async (req, res) => {
  const accessToken = req.headers['payment-signature'] as string

  if (!accessToken) {
    return return402Response(res, planId, agentId, req.url)
  }

  // Verify and process...
})
```

## Complete Validation Example

```typescript theme={null}
import express from 'express'
import { Payments, EnvironmentName } from '@nevermined-io/payments'
import { buildPaymentRequired } from '@nevermined-io/payments'

const app = express()
app.use(express.json())

const agentPayments = Payments.getInstance({
  nvmApiKey: process.env.NVM_API_KEY!,
  environment: 'sandbox' as EnvironmentName,
})

const PLAN_ID = process.env.NVM_PLAN_ID!
const AGENT_ID = process.env.NVM_AGENT_ID!

app.post('/api/v1/tasks', async (req, res) => {
  // 1. Extract token
  const accessToken = req.headers['payment-signature'] as string

  if (!accessToken) {
    return return402Response(res, PLAN_ID, AGENT_ID, req.url)
  }

  // 2. Build payment required
  const paymentRequired = buildPaymentRequired(PLAN_ID, {
    endpoint: req.url,
    agentId: AGENT_ID,
    httpVerb: req.method,
  })

  try {
    // 3. Verify permissions
    const verification = await agentPayments.facilitator.verifyPermissions({
      paymentRequired,
      x402AccessToken: accessToken,
      maxAmount: 1n,
    })

    if (!verification.isValid) {
      console.log('Verification failed: insufficient credits or invalid token')
      return return402Response(res, PLAN_ID, AGENT_ID, req.url)
    }

    console.log('✓ Permissions verified')

    // 4. Execute task
    const result = await processTask(req.body)

    // 5. Settle permissions
    const settlement = await agentPayments.facilitator.settlePermissions({
      paymentRequired,
      x402AccessToken: accessToken,
      maxAmount: 1n,
    })

    console.log(`✓ Credits settled: ${settlement.creditsRedeemed}`)
    console.log(`✓ Transaction: ${settlement.transaction}`)

    // 6. Return response with payment metadata
    const paymentResponse = {
      success: settlement.success,
      network: settlement.network,
      transaction: settlement.transaction,
      creditsRedeemed: settlement.creditsRedeemed,
    }

    res.writeHead(200, {
      'Content-Type': 'application/json',
      'PAYMENT-RESPONSE': Buffer.from(JSON.stringify(paymentResponse)).toString('base64'),
    })

    res.end(JSON.stringify({ result }))

  } catch (error) {
    console.error('Request processing failed:', error)
    res.status(500).json({ error: error.message })
  }
})

function return402Response(
  res: express.Response,
  planId: string,
  agentId: string,
  endpoint: string
) {
  const paymentRequired = buildPaymentRequired(planId, {
    endpoint,
    agentId,
    httpVerb: 'POST',
  })

  const paymentRequiredHeader = Buffer.from(
    JSON.stringify(paymentRequired)
  ).toString('base64')

  res.writeHead(402, {
    'Content-Type': 'application/json',
    'PAYMENT-REQUIRED': paymentRequiredHeader,
  })

  res.end(JSON.stringify({ error: 'Payment required' }))
}

async function processTask(body: any): Promise<any> {
  // Your agent logic here
  return { response: 'Task completed' }
}

app.listen(3000, () => {
  console.log('Agent server running on port 3000')
})
```

## Dynamic Credit Burning

For agents with variable credit costs, calculate credits based on the request:

```typescript theme={null}
async function calculateCredits(request: any, response: any): Promise<bigint> {
  // Example: charge based on response length
  const responseLength = JSON.stringify(response).length
  return BigInt(Math.ceil(responseLength / 1000))  // 1 credit per KB
}

app.post('/api/v1/tasks', async (req, res) => {
  const accessToken = req.headers['payment-signature'] as string
  const paymentRequired = buildPaymentRequired(PLAN_ID, {
    endpoint: req.url,
    agentId: AGENT_ID,
    httpVerb: req.method,
  })

  // Verify with max possible credits
  const verification = await agentPayments.facilitator.verifyPermissions({
    paymentRequired,
    x402AccessToken: accessToken,
    maxAmount: 10n,  // Max credits subscriber might need
  })

  if (!verification.isValid) {
    return return402Response(res, PLAN_ID, AGENT_ID, req.url)
  }

  // Execute
  const result = await processTask(req.body)

  // Calculate actual credits used
  const creditsUsed = await calculateCredits(req.body, result)

  // Settle with actual amount
  const settlement = await agentPayments.facilitator.settlePermissions({
    paymentRequired,
    x402AccessToken: accessToken,
    maxAmount: creditsUsed,
  })

  return res.json({
    result,
    creditsUsed: settlement.creditsRedeemed,
  })
})
```

## Settle Options

The `settlePermissions` method accepts additional options:

```typescript theme={null}
const settlement = await agentPayments.facilitator.settlePermissions({
  paymentRequired,
  x402AccessToken: accessToken,
  maxAmount: 5n,               // Credits to burn
  batch: false,                // Batch settlement (optional)
  marginPercent: 5,            // Add 5% margin to calculated cost (optional)
  agentRequestId: 'req-123',   // Request ID from verification (optional)
})
```

## Best Practices

1. **Always Verify First**: Call `verifyPermissions` before executing tasks
2. **Settle After Success**: Only burn credits after successful task completion
3. **Handle Errors**: Wrap verification/settlement in try-catch blocks
4. **Return 402 Properly**: Include `PAYMENT-REQUIRED` header with payment details
5. **Log Transactions**: Record transaction hashes for audit trails
6. **Dynamic Pricing**: Calculate credits based on actual resource usage
7. **Token Validation**: Never skip verification even if token looks valid

## Related Documentation

* [Querying an Agent](./querying-an-agent) - How subscribers generate and use tokens
* [X402 Protocol](./x402) - Complete X402 specification
* [MCP Integration](./mcp-integration) - Automatic validation in MCP servers
* [A2A Integration](./a2a-integration) - Automatic validation in A2A servers

***

**Source References**:

* `src/x402/facilitator-api.ts` (buildPaymentRequired, verifyPermissions, settlePermissions)
* `tests/e2e/test_payments_e2e.test.ts` (MockAgentServer class, complete validation flow)
* `tests/e2e/test_x402_e2e.test.ts` (lines 135-150, verification examples)
