{
  "openapi": "3.1.0",
  "info": {
    "title": "Nevermined API",
    "description": "API for managing AI agents, payment plans, and x402 payments in the Nevermined ecosystem.\n\n## Prerequisites\n\nYou need a Nevermined API Key to authenticate. Get one at [nevermined.app](https://nevermined.app) under **Settings > API Keys**.\n\nSee the [5-Minute Setup Guide](/docs/integrate/quickstart/5-minute-setup) for detailed instructions.",
    "license": {
      "name": "MIT"
    },
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.sandbox.nevermined.app/api/v1",
      "description": "Sandbox (Testing) - Base Sepolia"
    },
    {
      "url": "https://api.live.nevermined.app/api/v1",
      "description": "Live (Production) - Base Mainnet"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Protocol - Agents",
      "description": "Endpoints for registering and managing AI agents"
    },
    {
      "name": "Protocol - Plans",
      "description": "Endpoints for creating and managing payment plans"
    },
    {
      "name": "Protocol - Credits",
      "description": "Endpoints for minting and redeeming credits"
    },
    {
      "name": "Protocol - Access",
      "description": "Endpoints for access tokens and request validation"
    },
    {
      "name": "X402 - Permissions",
      "description": "Endpoints for x402 delegated permissions"
    }
  ],
  "paths": {
    "/protocol/agents": {
      "post": {
        "summary": "Register Agent",
        "description": "Creates a new AI agent with API configuration.",
        "operationId": "registerAgent",
        "tags": [
          "Protocol - Agents"
        ],
        "requestBody": {
          "description": "Agent configuration",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterAgentRequest"
              },
              "example": {
                "name": "Legal Assistant",
                "description": "AI-powered legal document analysis",
                "tags": [
                  "legal",
                  "ai"
                ],
                "endpoints": [
                  {
                    "POST": "https://your-api.com/query"
                  }
                ],
                "agentDefinitionUrl": "https://your-api.com/openapi.json"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Agent created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentCreatedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing API key"
          }
        }
      },
      "get": {
        "summary": "List Agents",
        "description": "Get paginated list of agents for the current user.",
        "operationId": "listAgents",
        "tags": [
          "Protocol - Agents"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Results per page",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "description": "Sort field",
            "schema": {
              "type": "string",
              "default": "createdAt"
            }
          },
          {
            "name": "sortOrder",
            "in": "query",
            "description": "Sort direction",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of agents",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedAgents"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/protocol/agents/{agentId}": {
      "get": {
        "summary": "Get Agent",
        "description": "Retrieves agent configuration and associated plans.",
        "operationId": "getAgent",
        "tags": [
          "Protocol - Agents"
        ],
        "parameters": [
          {
            "name": "agentId",
            "in": "path",
            "description": "The agent's unique identifier (DID)",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Agent details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Agent"
                }
              }
            }
          },
          "404": {
            "description": "Agent not found"
          }
        }
      },
      "put": {
        "summary": "Update Agent",
        "description": "Modifies agent configuration and metadata.",
        "operationId": "updateAgent",
        "tags": [
          "Protocol - Agents"
        ],
        "parameters": [
          {
            "name": "agentId",
            "in": "path",
            "description": "The agent's unique identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Updated agent metadata",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAgentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Agent updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Agent not found"
          }
        }
      }
    },
    "/protocol/agents/plans": {
      "post": {
        "summary": "Register Agent and Plan",
        "description": "Creates both an agent and associated payment plan in a single request.",
        "operationId": "registerAgentAndPlan",
        "tags": [
          "Protocol - Agents"
        ],
        "requestBody": {
          "description": "Agent and plan configuration",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterAgentAndPlanRequest"
              },
              "example": {
                "agentMetadata": {
                  "name": "Legal Assistant",
                  "description": "AI-powered legal analysis",
                  "tags": [
                    "legal",
                    "ai"
                  ]
                },
                "agentApi": {
                  "endpoints": [
                    {
                      "POST": "https://your-api.com/query"
                    }
                  ],
                  "agentDefinitionUrl": "https://your-api.com/openapi.json"
                },
                "planMetadata": {
                  "name": "Starter Plan",
                  "description": "100 requests for $10"
                },
                "priceConfig": {
                  "priceType": "FIXED_PRICE",
                  "tokenAddress": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
                  "amounts": [
                    10000000
                  ],
                  "receivers": [
                    "0xYourWalletAddress"
                  ]
                },
                "creditsConfig": {
                  "creditsType": "FIXED",
                  "amount": 100,
                  "minAmount": 1
                },
                "accessLimit": "credits"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Agent and plan created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentAndPlanCreatedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/protocol/agents/{agentId}/list/{activate}": {
      "put": {
        "summary": "Activate/Deactivate Agent",
        "description": "Controls the agent's public listing status.",
        "operationId": "toggleAgentListing",
        "tags": [
          "Protocol - Agents"
        ],
        "parameters": [
          {
            "name": "agentId",
            "in": "path",
            "description": "The agent's unique identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "activate",
            "in": "path",
            "description": "true to list, false to unlist",
            "required": true,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Status updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Agent not found"
          }
        }
      }
    },
    "/protocol/agents/{agentId}/plan/{planId}": {
      "post": {
        "summary": "Add Plan to Agent",
        "description": "Associates an existing payment plan with an agent.",
        "operationId": "addPlanToAgent",
        "tags": [
          "Protocol - Agents"
        ],
        "parameters": [
          {
            "name": "agentId",
            "in": "path",
            "description": "The agent's unique identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "planId",
            "in": "path",
            "description": "The plan's unique identifier",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Plan added to agent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Agent or plan not found"
          }
        }
      },
      "delete": {
        "summary": "Remove Plan from Agent",
        "description": "Disassociates a payment plan from an agent.",
        "operationId": "removePlanFromAgent",
        "tags": [
          "Protocol - Agents"
        ],
        "parameters": [
          {
            "name": "agentId",
            "in": "path",
            "description": "The agent's unique identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "planId",
            "in": "path",
            "description": "The plan's unique identifier",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Plan removed from agent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Agent or plan not found"
          }
        }
      }
    },
    "/protocol/agents/{agentId}/plans": {
      "get": {
        "summary": "Get Agent Plans",
        "description": "Lists all payment plans associated with an agent.",
        "operationId": "getAgentPlans",
        "tags": [
          "Protocol - Agents"
        ],
        "parameters": [
          {
            "name": "agentId",
            "in": "path",
            "description": "The agent's unique identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of plans",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedPlans"
                }
              }
            }
          },
          "404": {
            "description": "Agent not found"
          }
        }
      }
    },
    "/protocol/agents/initialize/{agentId}": {
      "post": {
        "summary": "Initialize Agent Request",
        "description": "Validates access and initializes an agent request session.",
        "operationId": "initializeAgentRequest",
        "tags": [
          "Protocol - Access"
        ],
        "parameters": [
          {
            "name": "agentId",
            "in": "path",
            "description": "The agent's unique identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Request initialization data",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InitializeAgentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Request initialized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InitializeAgentResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "402": {
            "description": "Payment required - insufficient credits"
          }
        }
      }
    },
    "/protocol/agent-sub-tasks": {
      "post": {
        "summary": "Track Agent Sub Task",
        "description": "Records task completion and credit consumption for complex agent workflows.",
        "operationId": "trackAgentSubTask",
        "tags": [
          "Protocol - Access"
        ],
        "requestBody": {
          "description": "Sub-task tracking data",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TrackSubTaskRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task tracked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/protocol/plans": {
      "post": {
        "summary": "Register Plan",
        "description": "Creates a new payment plan with pricing and credit configuration.",
        "operationId": "registerPlan",
        "tags": [
          "Protocol - Plans"
        ],
        "requestBody": {
          "description": "Plan configuration",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterPlanRequest"
              },
              "example": {
                "name": "Pro Plan",
                "description": "500 requests per month",
                "priceConfig": {
                  "priceType": "FIXED_PRICE",
                  "tokenAddress": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
                  "amounts": [
                    50000000
                  ],
                  "receivers": [
                    "0xYourWalletAddress"
                  ]
                },
                "creditsConfig": {
                  "creditsType": "FIXED",
                  "amount": 500,
                  "minAmount": 1
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Plan created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanCreatedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      },
      "get": {
        "summary": "List Plans",
        "description": "Get paginated list of payment plans for the current user.",
        "operationId": "listPlans",
        "tags": [
          "Protocol - Plans"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "createdAt"
            }
          },
          {
            "name": "sortOrder",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of plans",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedPlans"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/protocol/plans/{planId}": {
      "get": {
        "summary": "Get Plan",
        "description": "Retrieves details about a specific payment plan.",
        "operationId": "getPlan",
        "tags": [
          "Protocol - Plans"
        ],
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "description": "The plan's unique identifier",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Plan details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            }
          },
          "404": {
            "description": "Plan not found"
          }
        }
      },
      "put": {
        "summary": "Update Plan",
        "description": "Modifies plan metadata.",
        "operationId": "updatePlan",
        "tags": [
          "Protocol - Plans"
        ],
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "description": "The plan's unique identifier",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "description": "Updated plan metadata",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePlanRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Plan updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Plan not found"
          }
        }
      }
    },
    "/protocol/plans/{planId}/list/{activate}": {
      "put": {
        "summary": "Activate/Deactivate Plan",
        "description": "Lists (`activate=true`) or unlists (`activate=false`) a plan. This is a flag stored by Nevermined, not an on-chain change. An unlisted plan is hidden from discovery and can no longer be purchased. Checkout, crypto order and the card-delegation x402 settle are refused with the error `BCK.PROTOCOL.0062`; the other x402 refusals (every verify, and the erc4337 settle of an auto-order or pay-as-you-go call) answer `isValid: false` / `success: false` with that error's message as the reason, without a `code` field. Subscribers keep spending the credits they already hold while their balance covers a call.",
        "operationId": "togglePlanListing",
        "tags": [
          "Protocol - Plans"
        ],
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "description": "The plan's unique identifier",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "activate",
            "in": "path",
            "description": "true to list, false to unlist",
            "required": true,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Status updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Plan not found"
          }
        }
      }
    },
    "/protocol/plans/{planId}/order": {
      "post": {
        "summary": "Order Plan",
        "description": "Purchases a payment plan. Credits are issued immediately.",
        "operationId": "orderPlan",
        "tags": [
          "Protocol - Plans"
        ],
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "description": "The plan's unique identifier",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Order successful",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderPlanResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "402": {
            "description": "Payment failed"
          },
          "404": {
            "description": "Plan not found"
          }
        }
      }
    },
    "/protocol/plans/{planId}/balance/{holderAddress}": {
      "get": {
        "summary": "Get Plan Balance",
        "description": "Retrieves credit balance for a specific account on a plan.",
        "operationId": "getPlanBalance",
        "tags": [
          "Protocol - Plans"
        ],
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "description": "The plan's unique identifier",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "holderAddress",
            "in": "path",
            "description": "Wallet address to check balance for",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Balance information",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Balance"
                }
              }
            }
          },
          "404": {
            "description": "Plan or holder not found"
          }
        }
      }
    },
    "/protocol/plans/{planId}/agents": {
      "get": {
        "summary": "Get Plan Agents",
        "description": "Lists all agents associated with a payment plan.",
        "operationId": "getPlanAgents",
        "tags": [
          "Protocol - Plans"
        ],
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "description": "The plan's unique identifier",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of agents",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedAgents"
                }
              }
            }
          },
          "404": {
            "description": "Plan not found"
          }
        }
      }
    },
    "/protocol/token/{planId}/{agentId}": {
      "get": {
        "summary": "Get Access Token",
        "description": "Generates access credentials for agent-plan interaction.",
        "operationId": "getAccessToken",
        "tags": [
          "Protocol - Access"
        ],
        "parameters": [
          {
            "name": "planId",
            "in": "path",
            "description": "The plan's unique identifier",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "agentId",
            "in": "path",
            "description": "The agent's unique identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Access token generated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccessTokenResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "402": {
            "description": "No active subscription"
          },
          "404": {
            "description": "Plan or agent not found"
          }
        }
      }
    },
    "/protocol/plans/mint": {
      "post": {
        "summary": "Mint Credits",
        "description": "Adds credits to a plan account. Builder-only operation.",
        "operationId": "mintCredits",
        "tags": [
          "Protocol - Credits"
        ],
        "requestBody": {
          "description": "Mint request",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MintCreditsRequest"
              },
              "example": {
                "planId": "did:nv:abc123...",
                "holderAddress": "0xUserAddress",
                "amount": 100
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credits minted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MintCreditsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - not plan owner"
          }
        }
      }
    },
    "/protocol/plans/mintExpirable": {
      "post": {
        "summary": "Mint Expirable Credits",
        "description": "Adds time-limited credits to a plan account.",
        "operationId": "mintExpirableCredits",
        "tags": [
          "Protocol - Credits"
        ],
        "requestBody": {
          "description": "Mint expirable request",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MintExpirableCreditsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credits minted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MintCreditsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden - not plan owner"
          }
        }
      }
    },
    "/protocol/plans/redeem": {
      "post": {
        "summary": "Redeem Credits",
        "description": "Consumes credits from a plan account after processing a request.",
        "operationId": "redeemCredits",
        "tags": [
          "Protocol - Credits"
        ],
        "requestBody": {
          "description": "Redeem request",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedeemCreditsRequest"
              },
              "example": {
                "planId": "did:nv:abc123...",
                "holderAddress": "0xUserAddress",
                "amount": 5,
                "proof": "0xSignatureProof..."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credits redeemed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedeemCreditsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "402": {
            "description": "Insufficient credits"
          }
        }
      }
    },
    "/x402/permissions": {
      "post": {
        "summary": "Create Permission",
        "description": "Creates a delegated permission and generates an x402 access token.",
        "operationId": "createPermission",
        "tags": [
          "X402 - Permissions"
        ],
        "requestBody": {
          "description": "The x402 PaymentPayload to mint a token for",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePermissionRequest"
              },
              "example": {
                "resource": {
                  "url": "https://myagent.ai/api/v1/tasks",
                  "description": "AI agent task execution"
                },
                "accepted": {
                  "scheme": "nvm:erc4337",
                  "network": "eip155:84532",
                  "planId": "44742763076047497640080230236781474129970992727896593861997347135613135571071",
                  "extra": {
                    "agentId": "80918427023170428029540261117198154464497879145267720259488529685089104529015",
                    "httpVerb": "POST"
                  }
                },
                "delegationConfig": {
                  "delegationId": "a1b2c3d4-e5f6-4a90-8bcd-ef1234567890"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Permission created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionCreatedResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "402": {
            "description": "Insufficient balance"
          }
        }
      },
      "get": {
        "summary": "List Permissions",
        "description": "Retrieves paginated list of your delegated permissions.",
        "operationId": "listPermissions",
        "tags": [
          "X402 - Permissions"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "scheme",
            "in": "query",
            "required": false,
            "description": "Filter by payment scheme. Omit to list permissions on every scheme.",
            "schema": {
              "type": "string",
              "enum": [
                "nvm:erc4337",
                "nvm:card-delegation"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedPermissions"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/x402/permissions/{permissionHash}": {
      "get": {
        "summary": "Get Permission",
        "description": "Retrieves complete information about a specific permission.",
        "operationId": "getPermission",
        "tags": [
          "X402 - Permissions"
        ],
        "parameters": [
          {
            "name": "permissionHash",
            "in": "path",
            "description": "Unique permission identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Permission details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Permission"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Permission not found"
          }
        }
      },
      "delete": {
        "summary": "Revoke Permission",
        "description": "Invalidates a delegated permission, preventing further use.",
        "operationId": "revokePermission",
        "tags": [
          "X402 - Permissions"
        ],
        "parameters": [
          {
            "name": "permissionHash",
            "in": "path",
            "description": "Unique permission identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Permission revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevokePermissionResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Permission not found"
          }
        }
      }
    },
    "/x402/verify": {
      "post": {
        "summary": "Verify Permission",
        "description": "Validates a delegated permission before processing a request.",
        "operationId": "verifyPermission",
        "tags": [
          "X402 - Permissions"
        ],
        "requestBody": {
          "description": "Verification request",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyPermissionRequest"
              },
              "example": {
                "paymentRequired": {
                  "x402Version": 2,
                  "error": "Payment required to access resource",
                  "resource": {
                    "url": "https://myagent.ai/api/v1/tasks",
                    "description": "AI agent task execution"
                  },
                  "accepts": [
                    {
                      "scheme": "nvm:erc4337",
                      "network": "eip155:84532",
                      "planId": "44742763076047497640080230236781474129970992727896593861997347135613135571071",
                      "extra": {
                        "agentId": "80918427023170428029540261117198154464497879145267720259488529685089104529015"
                      }
                    }
                  ],
                  "extensions": {}
                },
                "x402AccessToken": "eyJ4NDAyVmVyc2lvbiI6MiwicmVzb3VyY2UiOnsidXJsIjoiaHR0cHM6Ly9teWFnZW50LmFpL2FwaS92MS90YXNrcyJ9LCJhY2NlcHRlZCI6eyJzY2hlbWUiOiJudm06ZXJjNDMzNyIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MzIiLCJwbGFuSWQiOiI0NDc0Mjc2MzA3NjA0NzQ5NzY0MDA4MDIzMDIzNjc4MTQ3NDEyOTk3MDk5MjcyNzg5NjU5Mzg2MTk5NzM0NzEzNTYxMzEzNTU3MTA3MSIsImV4dHJhIjp7InZlcnNpb24iOiIxIiwiYWdlbnRJZCI6IjgwOTE4NDI3MDIzMTcwNDI4MDI5NTQwMjYxMTE3MTk4MTU0NDY0NDk3ODc5MTQ1MjY3NzIwMjU5NDg4NTI5Njg1MDg5MTA0NTI5MDE1IiwiaHR0cFZlcmIiOiJQT1NUIn19LCJwYXlsb2FkIjp7InNpZ25hdHVyZSI6IjB4MTIzNCIsImF1dGhvcml6YXRpb24iOnsiZnJvbSI6IjB4ZjM5RmQ2ZTUxYWFkODhGNkY0Y2U2YUI4ODI3Mjc5Y2ZmRmI5MjI2NiIsInNlc3Npb25LZXlzUHJvdmlkZXIiOiJ6ZXJvZGV2Iiwic2Vzc2lvbktleXMiOltdfX0sImV4dGVuc2lvbnMiOnt9fQ==",
                "maxAmount": "2"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Verification result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyPermissionResponse"
                }
              }
            }
          }
        }
      }
    },
    "/x402/settle": {
      "post": {
        "summary": "Settle Permission",
        "description": "Finalizes and records delegated permission usage after service delivery.",
        "operationId": "settlePermission",
        "tags": [
          "X402 - Permissions"
        ],
        "requestBody": {
          "description": "Settlement request",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SettlePermissionRequest"
              },
              "example": {
                "paymentRequired": {
                  "x402Version": 2,
                  "error": "Payment required to access resource",
                  "resource": {
                    "url": "/api/v1/agents/tasks",
                    "description": "AI agent task execution"
                  },
                  "accepts": [
                    {
                      "scheme": "nvm:erc4337",
                      "network": "eip155:84532",
                      "planId": "44742763076047497640080230236781474129970992727896593861997347135613135571071",
                      "extra": {
                        "version": "1"
                      }
                    }
                  ],
                  "extensions": {}
                },
                "x402AccessToken": "eyJ4NDAyVmVyc2lvbiI6MiwicmVzb3VyY2UiOnsidXJsIjoiL2FwaS92MS9hZ2VudHMvdGFza3MifSwiYWNjZXB0ZWQiOnsic2NoZW1lIjoibnZtOmVyYzQzMzciLCJuZXR3b3JrIjoiZWlwMTU1Ojg0NTMyIiwicGxhbklkIjoiNDQ3NDI3NjMwNzYwNDc0OTc2NDAwODAyMzAyMzY3ODE0NzQxMjk5NzA5OTI3Mjc4OTY1OTM4NjE5OTczNDcxMzU2MTMxMzU1NzEwNzEiLCJleHRyYSI6eyJ2ZXJzaW9uIjoiMSJ9fSwicGF5bG9hZCI6eyJzaWduYXR1cmUiOiIweDEyMzQiLCJhdXRob3JpemF0aW9uIjp7ImZyb20iOiIweGYzOUZkNmU1MWFhZDg4RjZGNGNlNmFCODgyNzI3OWNmZkZiOTIyNjYiLCJzZXNzaW9uS2V5c1Byb3ZpZGVyIjoiemVyb2RldiIsInNlc3Npb25LZXlzIjpbXX19LCJleHRlbnNpb25zIjp7fX0=",
                "maxAmount": "2",
                "agentRequestId": "arId-123e4567-e89b-12d3-a456-426614174000"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Settlement complete",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SettlePermissionResponse"
                }
              }
            }
          },
          "402": {
            "description": "Settlement failed - insufficient credits"
          }
        }
      }
    },
    "/delegation/create": {
      "post": {
        "description": "Creates a spending delegation for the authenticated user against a previously enrolled payment method. For fiat-backed delegations (stripe/braintree/visa) the response includes a signed JWT (`delegationToken`); for crypto (erc4337) flows the token is held client-side and omitted. Requires a Nevermined API key.",
        "operationId": "DelegationController_createDelegation",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDelegationDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created delegation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateDelegationResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "summary": "Create a spending delegation and return a signed JWT token",
        "tags": [
          "Delegation"
        ]
      }
    },
    "/delegation": {
      "get": {
        "description": "Returns a paginated list of the authenticated user's spending delegations. Use `accessible=true` to return only delegations the requesting API key may spend against. Requires a Nevermined API key.",
        "operationId": "DelegationController_listDelegations",
        "parameters": [
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based). Defaults to 1.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of items per page. Defaults to 10.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "accessible",
            "required": false,
            "in": "query",
            "description": "When \"true\", return only delegations accessible to the requesting API key (filters by apiKeyId, Active status, expiry, and remaining budget)",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DelegationListDto"
                }
              }
            }
          }
        },
        "summary": "List all delegations for the authenticated user",
        "tags": [
          "Delegation"
        ]
      }
    },
    "/delegation/{delegationId}": {
      "get": {
        "description": "Returns the full details of a single delegation owned by the authenticated user, including remaining budget and last-used timestamp. Requires a Nevermined API key.",
        "operationId": "DelegationController_getDelegation",
        "parameters": [
          {
            "name": "delegationId",
            "required": true,
            "in": "path",
            "description": "UUID v4 of the delegation to fetch.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "withPayer",
            "required": false,
            "in": "query",
            "description": "When \"true\", also resolve `fundingWalletAddress` — the wallet the Router crypto rail spends from (EOA / 7702 kernel, or the org wallet) — and `lockedInChannels`, what that wallet has locked in MPP payment channels. Opt-in because it adds a wallet lookup and a channel read.",
            "example": "true",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DelegationDetailsDto"
                }
              }
            }
          },
          "404": {
            "description": "Delegation not found"
          }
        },
        "summary": "Get delegation details including remaining budget",
        "tags": [
          "Delegation"
        ]
      },
      "delete": {
        "description": "Revokes a delegation owned by the authenticated user and cascade-revokes its linked permissions. For Visa delegations the linked VGS intent is cancelled best-effort. Requires a Nevermined API key.",
        "operationId": "DelegationController_revokeDelegation",
        "parameters": [
          {
            "name": "delegationId",
            "required": true,
            "in": "path",
            "description": "UUID v4 of the delegation to revoke.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The revoke result (`{ success: true }`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/_NvmAPIResultDto"
                }
              }
            }
          },
          "404": {
            "description": "Delegation not found"
          }
        },
        "summary": "Revoke a delegation",
        "tags": [
          "Delegation"
        ]
      }
    },
    "/delegation/{delegationId}/transactions": {
      "get": {
        "description": "Returns a paginated list of charge transactions recorded against a delegation owned by the authenticated user. Requires a Nevermined API key.",
        "operationId": "DelegationController_getTransactions",
        "parameters": [
          {
            "name": "delegationId",
            "required": true,
            "in": "path",
            "description": "UUID v4 of the delegation whose transactions are listed.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number (1-based). Defaults to 1.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of items per page. Defaults to 10.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DelegationTransactionListDto"
                }
              }
            }
          },
          "404": {
            "description": "Delegation not found"
          }
        },
        "summary": "List transactions for a delegation",
        "tags": [
          "Delegation"
        ]
      }
    },
    "/payment-methods": {
      "get": {
        "description": "Returns the saved payment methods for the authenticated user. Requires a Nevermined API key.",
        "operationId": "PaymentMethodsController_listPaymentMethods",
        "parameters": [
          {
            "name": "accessible",
            "required": false,
            "in": "query",
            "description": "When \"true\", return only payment methods accessible to the requesting API key (filters by allowedApiKeyIds and Active status).",
            "schema": {
              "enum": [
                "true",
                "false"
              ],
              "type": "string"
            }
          },
          {
            "name": "provider",
            "required": false,
            "in": "query",
            "description": "When set, return only payment methods backed by this provider. Omit to return methods from every provider (default).",
            "schema": {
              "enum": [
                "stripe",
                "braintree",
                "erc4337",
                "visa",
                "vgs"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of saved payment methods",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PaymentMethodSummaryDto"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - Unknown provider value"
          },
          "401": {
            "description": "Unauthorized - Invalid or missing API key"
          }
        },
        "summary": "List saved payment methods",
        "tags": [
          "Payment Methods"
        ]
      }
    },
    "/payment-methods/{paymentMethodId}": {
      "patch": {
        "description": "Updates a saved payment method, changing its friendly alias and/or the set of NVM API keys allowed to use it. Requires a Nevermined API key.",
        "operationId": "PaymentMethodsController_updatePaymentMethod",
        "parameters": [
          {
            "name": "paymentMethodId",
            "required": true,
            "in": "path",
            "description": "Identifier of the payment method to update",
            "schema": {
              "example": "pm_1Abc2Def3Ghi4Jkl",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePaymentMethodDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated payment method",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentMethodSummaryDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing API key"
          },
          "404": {
            "description": "Not Found - Payment method does not exist"
          }
        },
        "summary": "Update a payment method",
        "tags": [
          "Payment Methods"
        ]
      },
      "delete": {
        "description": "Revokes (detaches) a saved payment method so it can no longer be used for delegated payments. Requires a Nevermined API key.",
        "operationId": "PaymentMethodsController_revokePaymentMethod",
        "parameters": [
          {
            "name": "paymentMethodId",
            "required": true,
            "in": "path",
            "description": "Identifier of the payment method to revoke",
            "schema": {
              "example": "pm_1Abc2Def3Ghi4Jkl",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Payment method revoked",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "success": true
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid or missing API key"
          },
          "404": {
            "description": "Not Found - Payment method does not exist"
          }
        },
        "summary": "Revoke a payment method",
        "tags": [
          "Payment Methods"
        ]
      }
    },
    "/router/payments": {
      "post": {
        "description": "Pay any x402-compatible agent WITHOUT that agent integrating Nevermined. Returns a signed\ncredential you attach to your own request to the agent.\n\nPrerequisite: a spending **Delegation**. Create one once via `POST /api/v1/delegation/create`\nwith `provider: 'erc4337'`, a `spendingLimitCents`, and a `durationSecs`; pass its `id` as\n`delegationId` here. The Router signs with your custodial wallet and enforces the cap.\n\nSteps:\n1. Call the agent once WITHOUT payment → it replies HTTP 402 with requirements. x402 v1 puts\n   them in the JSON body `{ x402Version: 1, accepts: [...] }`; x402 v2 puts them in a base64\n   `PAYMENT-REQUIRED` response header (decode to an object). Pass what you got as `target`.\n2. POST here. The Router selects a fundable option, signs an x402 `exact` (EIP-3009) payment,\n   reserves the amount against your Delegation, and returns `credential` + `settlement`.\n3. Re-send your original request to the agent with the HTTP header named `credential.name`\n   (`PAYMENT-SIGNATURE` for v2, `X-PAYMENT` for v1) set to `credential.value`. The agent\n   verifies, settles on-chain, and returns the resource.\n\nOptionally call `POST /api/v1/router/payments/{id}/settled` with the on-chain tx to\ncomplete the audit record. Pass a stable `requestId` for idempotency (one credential per id).\nPrefer `POST /api/v1/router/route` to have the Router call the agent and pay in one step (mode B).\n\nMPP: set `protocol: \"mpp\"` and pass the raw `WWW-Authenticate: Payment …` header value as\n`target.challenge` to pay a Merchant-Payment-Protocol (tempo `charge`) merchant. The Router\nreturns an `Authorization: Payment <…>` credential (attach it under header `credential.name`).",
        "operationId": "mintX402PaymentCredential",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePaymentDto"
              },
              "examples": {
                "x402-v1": {
                  "summary": "Pay a v1 (x402-express) agent",
                  "value": {
                    "delegationId": "5e7481c3-e972-45bd-bdc5-a0b99c4de4a1",
                    "protocol": "x402",
                    "resourceUrl": "https://agent.example/paid",
                    "requestId": "order-1234",
                    "target": {
                      "x402Version": 1,
                      "accepts": [
                        {
                          "scheme": "exact",
                          "network": "base-sepolia",
                          "maxAmountRequired": "1000",
                          "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
                          "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
                          "maxTimeoutSeconds": 60,
                          "extra": {
                            "name": "USDC",
                            "version": "2"
                          }
                        }
                      ]
                    }
                  }
                },
                "mpp-tempo": {
                  "summary": "Pay an MPP (tempo charge) merchant",
                  "value": {
                    "delegationId": "5e7481c3-e972-45bd-bdc5-a0b99c4de4a1",
                    "protocol": "mpp",
                    "resourceUrl": "https://merchant.example/paid",
                    "requestId": "order-5678",
                    "target": {
                      "challenge": "Payment id=\"ch_abc\", realm=\"merchant.example\", method=\"tempo\", intent=\"charge\", request=\"<base64url>\""
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "A signed credential + settlement descriptor + record id. Attach `credential.value` under header `credential.name` to your request to the agent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MintPaymentResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, no fundable option in `target`, or a malformed 402 (BCK.ROUTER.0001).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "402": {
            "description": "Delegation budget exceeded, expired, or inactive (BCK.ROUTER.0003).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Credential barred from the Router spend rails — an OAuth-minted key (device/auth-code ceremony) may not spend here; use a plain account key (BCK.OAUTH.0030). Permanent, do not retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "409": {
            "description": "The `requestId` was already used — idempotency conflict (BCK.ROUTER.0002).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Mint an x402 payment credential for an upstream agent (mode A)",
        "tags": [
          "Router"
        ]
      },
      "get": {
        "description": "Returns your Router payments (newest first, capped at 1000) across every agent and delegation —\nthe unified audit trail. Filter by `delegationId` and by `from`/`to` (ISO-8601, inclusive on\n`createdAt`). `format=csv` returns a CSV file download instead of the JSON array.\n\nSupplying `requestId` selects a separate, exact, caller-scoped lookup instead: the list filters are\nignored and it is not subject to the 1000-row window. JSON answers it with an explicit `presence`\ndiscriminator and zero or one `payments`; a failed lookup is an error, never `absent`. With `format=csv` it\nanswers the matching row, or only the header when there is none. An older\nRouter that ignores `requestId` answers a plain array, which is never authoritative absence.",
        "operationId": "listRouterPayments",
        "parameters": [
          {
            "name": "delegationId",
            "required": false,
            "in": "query",
            "description": "Only payments spent against this delegation id. Ignored with `requestId`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "required": false,
            "in": "query",
            "description": "ISO-8601 lower bound on createdAt (inclusive). Ignored with `requestId`.",
            "schema": {
              "example": "2026-07-01T00:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": false,
            "in": "query",
            "description": "ISO-8601 upper bound on createdAt (inclusive). Ignored with `requestId`.",
            "schema": {
              "example": "2026-07-31T23:59:59Z",
              "type": "string"
            }
          },
          {
            "name": "requestId",
            "required": false,
            "in": "query",
            "description": "Exact, caller-scoped idempotency key: look up the one payment made with it. Matched exactly as you sent it, padding included. Percent-encode it (an unencoded `+` reads as a space). Selects the lookup envelope (`RouterPaymentLookupDto`); the other list filters are ignored.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "example": "order-1234"
            }
          },
          {
            "name": "format",
            "required": false,
            "in": "query",
            "description": "Response format. Defaults to JSON; `csv` returns a downloadable file.",
            "schema": {
              "enum": [
                "json",
                "csv"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A JSON array for a list; the lookup envelope (`RouterPaymentLookupDto`) with `requestId`; or a CSV file when `format=csv`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RouterPaymentRecordDto"
                      }
                    },
                    {
                      "$ref": "#/components/schemas/RouterPaymentLookupDto"
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string",
                  "description": "CSV export of the same records, served as a file download (`Content-Disposition: attachment; filename=\"router-payments.csv\"`). Columns carry the JSON field names; new ones are always appended at the end, so a consumer reading by column index is unaffected when the record grows."
                }
              }
            }
          },
          "400": {
            "description": "`from`/`to` are not valid ISO-8601 timestamps, or `requestId` is empty or repeated (BCK.ROUTER.0001).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "List / export the buyer’s unified payment record across all agents",
        "tags": [
          "Router"
        ]
      }
    },
    "/router/route": {
      "post": {
        "description": "Hand the Router the request you want made to an upstream x402 agent; it probes, pays any 402\nwith your Delegation (same engine as `POST /router/payments`), and relays the agent’s response in ONE\ncall — you never see the 402 or handle the credential yourself.\n\nPrerequisite: a Delegation `id` (`POST /api/v1/delegation/create`, provider `erc4337`).\n\nProvide the target `url` (+ optional `method`, `headers`, `body`). A free (non-402) upstream is\nrelayed unchanged with `paid=false` and no `payment`. `requestId` is REQUIRED (the Router pays automatically, so a retry must be idempotency-keyed); a duplicate returns 409 with the original paymentId (from API 1.48, the first purchase's retained result instead — see `GET /router/payments/{id}/result`).\n\nFor LARGE or STREAMED responses, a transparent streaming variant exists at `ALL /api/v1/router/proxy`:\npoint your HTTP client at it with headers `X-Router-Target-Url` and `X-Router-Delegation-Id`\n(optional `X-Router-Upstream-Authorization` for the agent’s own auth). Method/body/headers pass\nthrough and the response streams back, with payment metadata in `X-Router-Payment-Id`,\n`X-Router-Payment-Status`, and `X-Router-Tx-Hash` response headers. Use this JSON endpoint when\nyou want a single structured result instead.",
        "operationId": "routeAndPayRequest",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProxyRequestDto"
              },
              "examples": {
                "get-paid-resource": {
                  "summary": "GET a paid resource through the Router",
                  "value": {
                    "delegationId": "5e7481c3-e972-45bd-bdc5-a0b99c4de4a1",
                    "url": "https://agent.example/paid",
                    "method": "GET",
                    "requestId": "order-1234"
                  }
                },
                "poll-async-result": {
                  "summary": "Read an async result you already paid for (free, wallet-authenticated)",
                  "description": "Same slug as the paid call, the catalog template filled with the job id that call returned. The Router answers the service's wallet challenge (MPP zero-amount proof or x402 Sign-In-With-X) with your own wallet and returns `paid: false`.",
                  "value": {
                    "delegationId": "5e7481c3-e972-45bd-bdc5-a0b99c4de4a1",
                    "slug": "merit-systems-mpp-2",
                    "path": "/api/jobs/3f6c2a9e-7b41-4d0e-9a52-c18e5f07d6b3",
                    "method": "GET",
                    "requestId": "lighthouse-image-v1-poll-1"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The relayed upstream response (`status` + `body`) plus the `payment` that was made.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouteResultDto"
                },
                "examples": {
                  "poll-async-result": {
                    "summary": "Async result read: relayed free, nothing charged",
                    "value": {
                      "status": 200,
                      "body": {
                        "status": "complete",
                        "…": "the delivered result, exactly as the service returned it"
                      },
                      "paid": false
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "API version 1.48 or later: the payment was made but the merchant had not answered within 45 s. The call keeps running on the Router (bounded at 120 s) and its result will be readable by you for 24 hours at `resultUrl` (also the `Location` header). Poll `GET /api/v1/router/payments/{id}/result`; do not repeat the call — a retry with the same `requestId` returns this same answer, then the result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutePendingDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, no fundable option, or an upstream/402 error (BCK.ROUTER.0001); or every option advertised an EIP-712 domain its own settlement token does not sign under (BCK.ROUTER.0012); or the payment credential would overwrite a header you asked the Router to forward, with no `credentialHeader` naming where the service wants it instead (BCK.ROUTER.0021 — refused before minting, so no money moved). A request-body validation failure arrives as BCK.HTTP.400 instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "402": {
            "description": "Delegation budget exceeded, expired, or inactive (BCK.ROUTER.0003).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Credential barred from the Router spend rails — an OAuth-minted key (device/auth-code ceremony) may not spend here; use a plain account key (BCK.OAUTH.0030). Permanent, do not retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "409": {
            "description": "The `requestId` was already used — idempotency conflict (BCK.ROUTER.0002).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Route a request through the Router, paying the agent’s 402 and relaying the reply (mode B)",
        "tags": [
          "Router"
        ]
      }
    },
    "/router/commerce/route": {
      "post": {
        "description": "The OAuth counterpart of `POST /api/v1/router/route`, for a credential minted from a\n`commerce` authorization. Identical behaviour and identical response — with one difference:\n**you do not choose the delegation.** It is derived from your grant: the capped, expiring\nmandate the user approved when they connected you. Sending a `delegationId` is refused, not\nignored.\n\nThe cap, its expiry, and the user revoking the grant are the boundary on what you can spend.\nThe routing fee is drawn from that same cap, in addition to each merchant’s price.\n\nA plain (non-OAuth) API key cannot use this route — it has no grant to derive from. Use\n`POST /api/v1/router/route` and name your own `delegationId` instead.",
        "operationId": "routeAndPayRequestFromGrant",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommerceRouteRequestDto"
              },
              "examples": {
                "invoke-cataloged-service": {
                  "summary": "Invoke a cataloged service through the broker, paid from the grant",
                  "value": {
                    "slug": "webscrape-pro",
                    "path": "scrape",
                    "method": "POST",
                    "body": {
                      "url": "https://example.com"
                    },
                    "requestId": "order-1234"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Upstream response, relayed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouteResultDto"
                }
              }
            }
          },
          "202": {
            "description": "API version 1.48 or later: the payment was made but the merchant had not answered within 45 s. The call keeps running on the Router (bounded at 120 s) and its result will be readable by you for 24 hours at `resultUrl` (also the `Location` header). Poll `GET /api/v1/router/payments/{id}/result`; do not repeat the call — a retry with the same `requestId` returns this same answer, then the result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutePendingDto"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request, a `delegationId` was supplied (BCK.OAUTH.0034 — it is derived from your grant), both `url` and `slug` were given (BCK.ROUTER.0001), or the payment credential would overwrite a header you asked the Router to forward and no `credentialHeader` named where the service wants it instead (BCK.ROUTER.0021 — refused before minting).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key — or the grant behind this credential has been revoked by the user, in which case every credential minted from it is invalid: restart the authorization to obtain a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "402": {
            "description": "The spend could not be authorized or funded — no money moved. Three distinct causes: BCK.ROUTER.0003, the delegation cap is exceeded or the delegation is expired / exhausted / revoked; BCK.ROUTER.0009, the buyer wallet does not hold enough of the settlement asset on the target network (crypto rails) or the issuer declined the card (MPP-stripe); BCK.ROUTER.0011, the card needs 3-D Secure cardholder authentication, which an agent with no human at a browser cannot complete. None is safely auto-retryable — branch on `code`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not a commerce grant (BCK.OAUTH.0033), or the EIP-7702 wallet gate refused the key (BCK.ROUTER.0008).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Something the payment needs does not exist. BCK.DELEGATION.0002 — the delegation your grant is pinned to could not be resolved for this credential: it is not this account’s, or it is bound to a different grant than the one you hold. Re-run the authorization to mint a fresh mandate. You never send a delegation id on this route, so this is not a wrong-id error; an expired, exhausted or revoked delegation is a 402 (BCK.ROUTER.0003) instead. BCK.X402.0011 — the account has no Privy wallet, i.e. signup never completed. BCK.X402.0053 — the account’s embedded wallet has been deleted, so its smart account can no longer be reconstructed; Privy documents this as unrecoverable, so provision a new account. All three are permanent — retrying creates nothing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "409": {
            "description": "The `requestId` was already used — idempotency conflict (BCK.ROUTER.0002) — or the raw `url` targets a cataloged service and must be invoked by `slug` (BCK.ROUTER.0014, not retryable).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "422": {
            "description": "Organization-wallet spend refused by the group budget: the group has exhausted its budget for this period (BCK.BUDGETS.0003), or it has no budget in the settlement currency (BCK.BUDGETS.0006). Only reachable when the delegation is funded by an organization wallet — a personal delegation never returns 422.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "BCK.ROUTER.0007 — too many mode-B relays in flight for this caller at once (each holds an upstream socket and a reserved budget). Retryable: let some relays finish, then try again. A slug-addressed call (using `slug`, not `url`) has a THIRD 429 source: a per-slug invoke rate limit (BCK.HTTP.429 with `Retry-After`). Two buckets again — the per-(key, slug) ceiling is yours, but the per-slug ceiling is SHARED by everyone invoking that slug, so heavy traffic to a popular slug can limit you even when your own rate is low. Retryable after the `Retry-After` backoff. Rate limit exceeded (BCK.HTTP.429). Two buckets can produce this. The per-API-key ceiling is genuinely yours and answers with `Retry-After` (seconds) — honour it. The global bucket is NOT: these routes authenticate after it runs, so it falls back to keying on the client IP, which is currently SHARED by all external callers — you can be limited by traffic that is not yours. That one answers with `Retry-After-short` / `Retry-After-long`. Retryable after a backoff either way.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "500": {
            "description": "BCK.ROUTER.0010 — the payment handler reported a settlement amount the routing-fee arithmetic cannot use. For THAT code specifically: a credential may already have been minted while no payment record was written, so your `requestId` will NOT suppress a retry — do not blind-retry, report it. BCK.ROUTER.0013 — Nevermined holds no EIP-712 signing domain for the selected settlement token; nothing was signed, charged or reserved, and a retry fails identically until our token table is fixed, so report it rather than looping. Any other 500 is an unexpected server error, may carry no `code` at all, and the guidance above does not apply to it — treat it as unknown.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "503": {
            "description": "BCK.X402.0052 — the account’s EIP-7702 delegation designator could not be installed on the settlement network, and the Router refuses to pay from a wallet whose funds have not been swept to the signer. Reachable only where EIP-7702 is enabled. This is a transient infrastructure fault and the ONE failure on these routes an autonomous caller SHOULD retry — back off first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Route and pay using the delegation from your commerce grant (mode B, OAuth)",
        "tags": [
          "Router"
        ]
      }
    },
    "/router/quote": {
      "post": {
        "description": "Ask what `POST /route` would charge for this call, WITHOUT paying it. The Router makes the\nsame unpaid probe `/route` makes, reads the 402, selects the payment option exactly as a\npayment would (MPP first, then x402) and prices it with the Router fee — then stops. Nothing\nis signed, no credential is minted, no payment is recorded and no delegation budget is\nreserved, so no money moves. A quote is not free of consequence, though: the unpaid probe\nreaches the service (one that does not charge for the request performs it), and a quote\nspends the same per-key and per-service rate limit as a payment — so quote once per decision\nrather than repeating it.\n\nUse it to decide whether ONE call fits the allocation you planned for it: compare\n`fee.capChargedMicros` (exact) or `fee.capChargedCents` (rounded up, the figure\n`maxTotalCents` is compared against) with what you want to spend, then call `/route` with\n`maxTotalCents` set to that figure so a price change between quote and call is refused rather\nthan paid.\n\n`delegationId` is optional. With it, the quote prices the options that delegation would select\n(an organization-wallet delegation pays only in its own currency, a card delegation pays over\nMPP-stripe, a recipient allowlist is enforced); without it, the options a personal crypto\ndelegation would select. `optionSet` says which. A quote checks neither the remaining cap nor\nthe wallet balance — those are the payment’s checks (BCK.ROUTER.0003, BCK.ROUTER.0009).\n\nProvide the target `url` OR a catalog `slug` (+ optional `path`, `search`, `method`, `headers`,\n`body`), as on `/route`. A non-402 answer returns `paymentRequired: false` with the upstream\nstatus only — the upstream body and headers are never returned.",
        "operationId": "quoteRouteRequest",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequestDto"
              },
              "examples": {
                "quote-catalog-service": {
                  "summary": "Quote a cataloged service",
                  "value": {
                    "slug": "webscrape-pro",
                    "path": "scrape",
                    "method": "POST",
                    "body": {
                      "url": "x"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The quote. `paymentRequired: false` means the upstream did not ask for payment; otherwise `settlement` and `fee` are what `/route` would pay and reserve.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResultDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, no fundable option, a pay-to outside the delegation’s allowed recipients, or an upstream/402 error (BCK.ROUTER.0001); or every option advertised an EIP-712 domain its own settlement token does not sign under (BCK.ROUTER.0012); or the payment credential would overwrite a header you asked the Router to forward and no `credentialHeader` named where the service wants it instead (BCK.ROUTER.0021) — the same refusals `/route` raises before paying. A request-body validation failure arrives as BCK.HTTP.400 instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "BCK.OAUTH.0030 — an OAuth-minted key may not use the Router spend rails; use a plain account key. Permanent — do not retry. A `commerce` credential quotes on `POST /api/v1/router/commerce/quote` instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "BCK.DELEGATION.0002 — no delegation with that `delegationId` belongs to this caller; or BCK.CATALOG.0001 — no listed catalog service has that `slug`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "409": {
            "description": "The target is a cataloged Nevermined service that must be addressed by its slug, not by raw URL (BCK.ROUTER.0014).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "413": {
            "description": "BCK.ROUTER.0024 — the Router JSON request body exceeds the global 5 MiB ceiling. BCK.ROUTER.0027 — a slug-addressed request body is larger than the catalog endpoint accepts (its `maxRequestBytes` on the catalog service detail). Refused before the service is contacted: nothing was paid by this request (a retry of an already-paid `requestId` gets the 409 BCK.ROUTER.0002 instead). `params` gives `bodyBytes` and `maxRequestBytes`; shrink the body or pick a service that takes it. On `/select` it means every otherwise-payable match is below the body, reported against the largest of their limits. Not retryable unchanged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "BCK.ROUTER.0007 — too many mode-B relays in flight for this caller at once (each holds an upstream socket and a reserved budget). Retryable: let some relays finish, then try again. A slug-addressed call (using `slug`, not `url`) has a THIRD 429 source: a per-slug invoke rate limit (BCK.HTTP.429 with `Retry-After`). Two buckets again — the per-(key, slug) ceiling is yours, but the per-slug ceiling is SHARED by everyone invoking that slug, so heavy traffic to a popular slug can limit you even when your own rate is low. Retryable after the `Retry-After` backoff. Rate limit exceeded (BCK.HTTP.429). Two buckets can produce this. The per-API-key ceiling is genuinely yours and answers with `Retry-After` (seconds) — honour it. The global bucket is NOT: these routes authenticate after it runs, so it falls back to keying on the client IP, which is currently SHARED by all external callers — you can be limited by traffic that is not yours. That one answers with `Retry-After-short` / `Retry-After-long`, plus `RateLimit-Remaining: 0` and `RateLimit-Reset` at the same expiry. A per-key 429 has bare `Retry-After` and no `RateLimit-*` triple. Retryable after a backoff either way.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "500": {
            "description": "BCK.ROUTER.0013 — Nevermined holds no EIP-712 signing domain for the selected settlement token; report it rather than looping. Any other 500 is an unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "503": {
            "description": "BCK.ROUTER.0028 — the quote could not be computed because a read it depends on failed. Nothing was signed or charged; retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Quote a Router call: the fee-inclusive price, rail and network, without paying",
        "tags": [
          "Router"
        ]
      }
    },
    "/router/commerce/quote": {
      "post": {
        "description": "The OAuth counterpart of `POST /api/v1/router/quote`, for a credential minted from a\n`commerce` authorization. Identical behaviour and identical response, with one difference:\n**you do not choose the delegation.** The quote prices the options the delegation your grant\nis pinned to would select, so it is what `POST /api/v1/router/commerce/route` would charge for\nthe same call. Sending a `delegationId` is refused, not ignored.\n\nNothing is signed, no credential is minted, no payment is recorded and no budget is reserved.\nThe Router still makes the unpaid request to the service, and the quote spends the same\nper-key and per-service rate budgets as a payment. Compare `fee.capChargedCents` with what you\nwant to spend, then call `/commerce/route` with `maxTotalCents` set to that figure.\n\nA plain (non-OAuth) API key cannot use this route: it has no grant to price. Use\n`POST /api/v1/router/quote` and name your own `delegationId` instead.",
        "operationId": "quoteRouteRequestFromGrant",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommerceQuoteRequestDto"
              },
              "examples": {
                "quote-catalog-service": {
                  "summary": "Quote a cataloged service against your grant",
                  "value": {
                    "slug": "webscrape-pro",
                    "path": "scrape",
                    "method": "POST",
                    "body": {
                      "url": "x"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The quote. `paymentRequired: false` means the upstream did not ask for payment; otherwise `settlement` and `fee` are what `/commerce/route` would pay and reserve against the grant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResultDto"
                }
              }
            }
          },
          "400": {
            "description": "A `delegationId` was supplied (BCK.OAUTH.0034 — it is derived from your grant); or the refusals `/quote` makes: invalid request, no fundable option, a pay-to outside the delegation’s allowed recipients, or an upstream/402 error (BCK.ROUTER.0001), or an EIP-712 domain its settlement token does not sign under (BCK.ROUTER.0012), or a payment credential that would overwrite a header you asked the Router to forward (BCK.ROUTER.0021). A request-body validation failure arrives as BCK.HTTP.400 instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key — or the grant behind this credential has been revoked by the user, in which case every credential minted from it is invalid: restart the authorization to obtain a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "BCK.OAUTH.0033 — this credential is not a commerce grant, or its grant is not pinned to a usable delegation (re-run the authorization to mint a fresh mandate).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "BCK.DELEGATION.0002 — the delegation your grant is pinned to could not be resolved for this credential (re-run the authorization to mint a fresh mandate); or BCK.CATALOG.0001 — no listed catalog service has that `slug`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "409": {
            "description": "The target is a cataloged Nevermined service that must be addressed by its slug, not by raw URL (BCK.ROUTER.0014).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "413": {
            "description": "BCK.ROUTER.0024 — the Router JSON request body exceeds the global 5 MiB ceiling. BCK.ROUTER.0027 — a slug-addressed request body is larger than the catalog endpoint accepts (its `maxRequestBytes` on the catalog service detail). Refused before the service is contacted: nothing was paid by this request (a retry of an already-paid `requestId` gets the 409 BCK.ROUTER.0002 instead). `params` gives `bodyBytes` and `maxRequestBytes`; shrink the body or pick a service that takes it. On `/select` it means every otherwise-payable match is below the body, reported against the largest of their limits. Not retryable unchanged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "BCK.ROUTER.0007 — too many mode-B relays in flight for this caller at once (each holds an upstream socket and a reserved budget). Retryable: let some relays finish, then try again. A slug-addressed call (using `slug`, not `url`) has a THIRD 429 source: a per-slug invoke rate limit (BCK.HTTP.429 with `Retry-After`). Two buckets again — the per-(key, slug) ceiling is yours, but the per-slug ceiling is SHARED by everyone invoking that slug, so heavy traffic to a popular slug can limit you even when your own rate is low. Retryable after the `Retry-After` backoff. Rate limit exceeded (BCK.HTTP.429). Two buckets can produce this. The per-API-key ceiling is genuinely yours and answers with `Retry-After` (seconds) — honour it. The global bucket is NOT: these routes authenticate after it runs, so it falls back to keying on the client IP, which is currently SHARED by all external callers — you can be limited by traffic that is not yours. That one answers with `Retry-After-short` / `Retry-After-long`, plus `RateLimit-Remaining: 0` and `RateLimit-Reset` at the same expiry. A per-key 429 has bare `Retry-After` and no `RateLimit-*` triple. Retryable after a backoff either way.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "500": {
            "description": "BCK.ROUTER.0013 — Nevermined holds no EIP-712 signing domain for the selected settlement token; report it rather than looping. Any other 500 is an unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "503": {
            "description": "BCK.ROUTER.0028 — the quote could not be computed because a read it depends on failed. Nothing was signed or charged; retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Quote a Router call from your commerce grant, without paying (OAuth)",
        "tags": [
          "Router"
        ]
      }
    },
    "/router/subscription/quote": {
      "post": {
        "description": "Credits one call to a catalog endpoint costs with your Nevermined Subscription: the endpoint's current rate on Nevermined's rate card, whatever the merchant charges at the moment of the call. Rates change without notice; the rate in force when the call is made applies.\n\nAnswers from the rate card alone — the merchant is not contacted, nothing is reserved and nothing is charged. It refuses what `POST /router/subscription/route` would refuse before contacting the merchant (an ineligible or untariffed endpoint, no spendable subscription), but it does NOT check your credits left, the merchant's live price or Nevermined's treasury; the route checks those when the call is made. A `url` is refused (`BCK.SUBSCRIPTION_ROUTER.0005`).\n\nPersonal API keys only: the Nevermined Subscription belongs to a personal account, so call with a key that carries no organization tag and without `X-Current-Org-Id`. An OAuth-minted credential is refused (`BCK.OAUTH.0030`). Where the subscription is not available — on this deployment or to this account — it answers 404 `BCK.SUBSCRIPTION_ACCESS.0001`.",
        "operationId": "quoteSubscriptionCall",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionQuoteRequestDto"
              },
              "examples": {
                "price-an-endpoint": {
                  "summary": "Price one call to a catalog endpoint",
                  "value": {
                    "slug": "webscrape-pro",
                    "path": "scrape",
                    "method": "POST"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The credits one call to this endpoint costs, and the tariff version that sets it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionQuoteResultDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, or a field a subscription call does not accept (BCK.SUBSCRIPTION_ROUTER.0005, `params.field`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "402": {
            "description": "No subscription that can spend right now — none bought, ended, past due, or suspended by a card dispute — or a call made in an organization you are an active member of (`X-Current-Org-Id`, or that organization's API key), which never falls back to your personal subscription (BCK.SUBSCRIPTION_ROUTER.0002).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "This endpoint cannot be paid with subscription credits (BCK.SUBSCRIPTION_ROUTER.0001; `params.reason` names the failed check); or an OAuth-minted credential (BCK.OAUTH.0030).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Not available to the caller (BCK.SUBSCRIPTION_ACCESS.0001), or no listed catalog service has that `slug` (BCK.CATALOG.0001).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Per-slug throttle.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "500": {
            "description": "Nevermined-side data error: the subscription does not resolve to its credits (BCK.SUBSCRIPTION_ROUTER.0011). Nothing was charged. (An unusable rate surfaces only on the route, as BCK.SUBSCRIPTION_ROUTER.0013.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "503": {
            "description": "The subscription could not be looked up (BCK.SUBSCRIPTION_ROUTER.0012, retryable), or this deployment cannot run subscription calls (BCK.SUBSCRIPTION_ROUTER.0010).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Quote a call in subscription credits",
        "tags": [
          "Router"
        ]
      }
    },
    "/router/subscription/route": {
      "post": {
        "description": "Call a catalog service and pay it with your Nevermined Subscription credits (mode B). Like `POST /router/route`, but Nevermined pays the merchant from its treasury and your subscription is charged the endpoint's rate in credits. There is no Delegation, no wallet to fund and no routing fee.\n\n- **A slug, never a URL.** Address the service by `slug` (+ `path`, `search`, `method`, `headers`, `body`). A `url` or a `delegationId` is refused (`BCK.SUBSCRIPTION_ROUTER.0005`), not ignored.\n- **`requestId` is required** (idempotency): a retry with the same id is never paid twice.\n- **Only a merchant `2xx` keeps the credits.** They are reserved before the merchant is called and kept (`Committed`) only on a `2xx`; anything else marks them for refund and they are returned (`Refunded`). Every refusal below happens before anything is reserved, signed or charged — except a 502, which can follow a payment.\n- **Rails:** x402 `exact` and MPP `tempo` merchants only.\n\nFor large or streamed responses, the streaming form is `ALL /api/v1/router/subscription/svc/{slug}/{path}` with an `X-Router-Request-Id` header; it answers with `X-Router-Credits-Charged` and `X-Router-Credits-Status` beside the `X-Router-Payment-*` headers, and refuses `X-Router-Target-Url`, `X-Router-Target-Slug` and `X-Router-Delegation-Id`.\n\nPersonal API keys only: the Nevermined Subscription belongs to a personal account, so call with a key that carries no organization tag and without `X-Current-Org-Id`. An OAuth-minted credential is refused (`BCK.OAUTH.0030`). Where the subscription is not available — on this deployment or to this account — it answers 404 `BCK.SUBSCRIPTION_ACCESS.0001`.",
        "operationId": "routeSubscriptionCall",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionRouteRequestDto"
              },
              "examples": {
                "pay-with-credits": {
                  "summary": "Call a catalog endpoint, paid with credits",
                  "value": {
                    "slug": "webscrape-pro",
                    "path": "scrape",
                    "method": "POST",
                    "body": {
                      "url": "https://example.com"
                    },
                    "requestId": "scrape-example-com-1"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The merchant's response (`status`, and `body` only when it delivered) plus the `payment` Nevermined made and the credits it cost.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionRouteResultDto"
                }
              }
            }
          },
          "202": {
            "description": "API version 1.48 or later: the paid call outlived the synchronous wait. Poll `GET /api/v1/router/payments/{id}/result` (also the `Location` header); the credits stay `Reserved` until the call ends. A retry with the same `requestId` returns this same answer, then the result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutePendingDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request; a field a subscription call does not accept (BCK.SUBSCRIPTION_ROUTER.0005, `params.field`); the merchant asked for a rail credits do not pay (BCK.SUBSCRIPTION_ROUTER.0003, `params.rail`); or the payment credential would overwrite a header you forward, with no `credentialHeader` naming where the service wants it (BCK.ROUTER.0021).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "402": {
            "description": "No subscription that can spend right now, including a call made in an organization you are an active member of (`X-Current-Org-Id`, or that organization's API key), which never falls back to your personal subscription (BCK.SUBSCRIPTION_ROUTER.0002); or not enough credits for this call (BCK.SUBSCRIPTION_ROUTER.0009, `params.credits`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "This endpoint cannot be paid with subscription credits (BCK.SUBSCRIPTION_ROUTER.0001, `params.reason`); the treasury cannot pay this merchant (BCK.SUBSCRIPTION_TREASURY.0008); or an OAuth-minted credential (BCK.OAUTH.0030).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Not available to the caller (BCK.SUBSCRIPTION_ACCESS.0001), or no listed catalog service has that `slug` (BCK.CATALOG.0001).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "409": {
            "description": "The merchant's live price is outside the rate's bound, in another asset or network, or could not be read as a price, or — for a service paid over an MPP session — it asked to be paid at an address Nevermined has not seen it use on this endpoint (BCK.SUBSCRIPTION_ROUTER.0004, `params.reason`, e.g. `payee_unobserved`); the MPP session payment channel with this service, shared by every subscriber, is closing (BCK.ROUTER.0042, retry later with the same requestId); or the `requestId` was already used (BCK.ROUTER.0002 — from API 1.48, a retry of a finished call is answered from its retained result instead).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "413": {
            "description": "The request body is larger than this endpoint accepts (BCK.ROUTER.0027, its `maxRequestBytes`) or than the Router accepts (BCK.ROUTER.0024). Refused before the service is contacted; nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "422": {
            "description": "The merchant asked to be paid in an asset Nevermined's treasury does not pay in (BCK.SUBSCRIPTION_TREASURY.0002), or the endpoint is known to outlast the Router's paid-call limit (BCK.ROUTER.0036). Nothing was charged; pay this endpoint from a delegation, or pick another.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "The per-slug throttle or the per-user concurrency limit. A subscription has no spending limit beyond its credits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "500": {
            "description": "Nevermined-side error, nothing charged: the payment could not be prepared consistently (BCK.SUBSCRIPTION_ROUTER.0006, retryable with a new requestId), the subscription does not resolve to its credits (0011), the rate is unusable (0013), or a treasury configuration or ledger error (BCK.SUBSCRIPTION_TREASURY.0011–0013).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "502": {
            "description": "The paid call got no response from the service (BCK.ROUTER.0034), or its reply was too large to deliver (BCK.ROUTER.0025): the merchant may have been paid, and the call's credits are marked for refund. Do not retry with a new `requestId`; read the outcome from `GET /api/v1/router/payments?requestId=…` (`creditsStatus`). An unpaid probe the service failed to answer (BCK.ROUTER.0020) paid nothing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to pay, nothing charged: the reservation timed out (BCK.SUBSCRIPTION_ROUTER.0014, retry with the same requestId), an MPP session step did not answer in time (BCK.ROUTER.0043, `params.stage`, retry with the same requestId), the subscription lookup failed (0012), the deployment cannot run subscription calls (0010), or the treasury refused (a `BCK.SUBSCRIPTION_TREASURY.*` code, e.g. 0003 when its float cannot cover the payment). `retryable` says whether retrying can help.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Call a catalog service, paid with subscription credits",
        "tags": [
          "Router"
        ]
      }
    },
    "/router/subscription/balance": {
      "get": {
        "description": "Your Nevermined Subscription credits left, when they reset, and your plan. Charges nothing. Called in an organization you are an active member of (`X-Current-Org-Id`, or that organization's API key), it reads that organization's subscription instead, never your personal one; organizations can no longer subscribe, so that is `subscribed: false` unless the organization still holds one bought earlier. `subscribed: false` carries no other field; every other field is present exactly when `subscribed` is true.\n\nPersonal API keys only: the Nevermined Subscription belongs to a personal account, so call with a key that carries no organization tag and without `X-Current-Org-Id`. An OAuth-minted credential is refused (`BCK.OAUTH.0030`). Where the subscription is not available — on this deployment or to this account — it answers 404 `BCK.SUBSCRIPTION_ACCESS.0001`.",
        "operationId": "getSubscriptionBalance",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The caller's credits, reset date and plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionBalanceDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "An OAuth-minted credential (BCK.OAUTH.0030).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Not available to the caller (BCK.SUBSCRIPTION_ACCESS.0001).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "503": {
            "description": "The balance could not be read (BCK.SUBSCRIPTION_MCP.0001) — never answered as zero. Retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Read your subscription credits",
        "tags": [
          "Router"
        ]
      }
    },
    "/router/payments/{id}/settled": {
      "post": {
        "description": "After you attached a mode-A credential and the agent settled on-chain, report the settlement\n`txHash` (from the agent’s `PAYMENT-RESPONSE` / `X-PAYMENT-RESPONSE` header) so the payment\nrecord moves `Issued → Settled`. Idempotent: re-reporting the same txHash is a no-op; a\ndifferent txHash, or a record not in `Issued`, is rejected. NOT needed for mode B\n(`/route` and `/proxy` self-complete the record from the upstream settlement header).",
        "operationId": "reportPaymentSettlement",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "The `paymentId` returned by `POST /router/payments`.",
            "schema": {
              "example": "b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SettlePaymentDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The record is now `Settled`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SettleResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "No payment record with that id for this caller (BCK.ROUTER.0004).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "409": {
            "description": "Record not settleable — already settled with a different tx, or not in `Issued` (BCK.ROUTER.0005).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Report the on-chain settlement tx for a minted payment (completes the record)",
        "tags": [
          "Router"
        ]
      }
    },
    "/router/payments/{id}/result": {
      "get": {
        "description": "Every paid call made through `POST /router/route` (and its `commerce` counterpart) is kept for you, the paying user, for 24 hours after it ends, so a response lost on the way back — a client timeout, a dropped connection — can still be read. `/router/proxy` and `/router/svc` keep a result only when they answered `202`; a response that had already started streaming to you is never stored.\n\nA paid call whose merchant has not answered within 45 s is answered `202` with `{ paymentId, resultUrl, status: \"Pending\" }` instead of holding your connection (API 1.48 or later). Poll this endpoint:\n\n- `202`, `state: Pending` — still running.\n- `200`, `state: Ready` — `status` and `body` are the relayed response, withheld exactly as the original response would have been.\n- `200`, `state: Failed` — the call ended without a response; `failureReason` says why.\n- `404` `BCK.ROUTER.0030` — nothing to return: expired, never retained, or not a payment of yours (deliberately indistinguishable).",
        "operationId": "getRouterPaymentResult",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "The `paymentId` of the paid call.",
            "schema": {
              "example": "b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The result is `Ready`, or the call ended `Failed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterPaymentResultDto"
                }
              }
            }
          },
          "202": {
            "description": "The paid call is still running (`state: Pending`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterPaymentResultDto"
                }
              }
            }
          },
          "400": {
            "description": "`id` is not a valid uuid (BCK.HTTP.400).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "No retained result for this id under your account — expired, never retained, or not yours (BCK.ROUTER.0030).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Read a paid Router result (retained for 24 hours)",
        "tags": [
          "Router"
        ]
      }
    },
    "/router/payments/summary": {
      "get": {
        "description": "Returns the buyer’s total number of Router payment requests (uncapped — unlike the 1000-row\nlist endpoint) plus a per-bucket time-series for a dashboard chart. Filter by `from`/`to`\n(ISO-8601, inclusive on `createdAt`); choose the bucket size with `granularity` (day/week/month).",
        "operationId": "routerPaymentsSummary",
        "parameters": [
          {
            "name": "from",
            "required": false,
            "in": "query",
            "description": "ISO-8601 lower bound on createdAt (inclusive).",
            "schema": {
              "example": "2026-07-01T00:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "to",
            "required": false,
            "in": "query",
            "description": "ISO-8601 upper bound on createdAt (inclusive).",
            "schema": {
              "example": "2026-07-31T23:59:59Z",
              "type": "string"
            }
          },
          {
            "name": "granularity",
            "required": false,
            "in": "query",
            "description": "Bucket size. Defaults to day.",
            "schema": {
              "enum": [
                "day",
                "week",
                "month"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Total + time-series.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentsSummaryDto"
                }
              }
            }
          },
          "400": {
            "description": "`from`/`to` are not valid ISO-8601 timestamps (BCK.ROUTER.0001).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid Nevermined API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouterErrorResponseDto"
                }
              }
            }
          }
        },
        "summary": "Aggregate count + time-series of the buyer’s payment requests",
        "tags": [
          "Router"
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "string"
          },
          "statusCode": {
            "type": "integer"
          }
        }
      },
      "SuccessResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          }
        }
      },
      "RegisterAgentRequest": {
        "type": "object",
        "required": [
          "name",
          "endpoints",
          "agentDefinitionUrl"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Agent display name"
          },
          "description": {
            "type": "string",
            "description": "Agent description"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Searchable tags"
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "API endpoints (HTTP verb and URL)"
          },
          "agentDefinitionUrl": {
            "type": "string",
            "description": "URL to OpenAPI spec, MCP Manifest, or A2A agent card"
          }
        }
      },
      "UpdateAgentRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "AgentCreatedResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "agentId": {
            "type": "string",
            "description": "The agent's unique identifier (DID)"
          }
        }
      },
      "AgentAndPlanCreatedResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "agentId": {
            "type": "string"
          },
          "planId": {
            "type": "string"
          }
        }
      },
      "RegisterAgentAndPlanRequest": {
        "type": "object",
        "required": [
          "agentMetadata",
          "agentApi",
          "planMetadata",
          "priceConfig",
          "creditsConfig"
        ],
        "properties": {
          "agentMetadata": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "tags": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "agentApi": {
            "type": "object",
            "properties": {
              "endpoints": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "agentDefinitionUrl": {
                "type": "string"
              }
            }
          },
          "planMetadata": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "description": {
                "type": "string"
              }
            }
          },
          "priceConfig": {
            "$ref": "#/components/schemas/PriceConfig"
          },
          "creditsConfig": {
            "$ref": "#/components/schemas/CreditsConfig"
          },
          "accessLimit": {
            "type": "string",
            "enum": [
              "credits",
              "time"
            ]
          }
        }
      },
      "Agent": {
        "type": "object",
        "properties": {
          "agentId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "agentDefinitionUrl": {
            "type": "string"
          },
          "plans": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "planId": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                }
              }
            }
          },
          "isListed": {
            "type": "boolean"
          },
          "owner": {
            "type": "string"
          }
        }
      },
      "PaginatedAgents": {
        "type": "object",
        "properties": {
          "agents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Agent"
            }
          },
          "totalCount": {
            "type": "integer"
          },
          "page": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        }
      },
      "RegisterPlanRequest": {
        "type": "object",
        "required": [
          "name",
          "priceConfig",
          "creditsConfig"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "priceConfig": {
            "$ref": "#/components/schemas/PriceConfig"
          },
          "creditsConfig": {
            "$ref": "#/components/schemas/CreditsConfig"
          }
        }
      },
      "UpdatePlanRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        }
      },
      "PlanCreatedResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "planId": {
            "type": "string"
          }
        }
      },
      "Plan": {
        "type": "object",
        "properties": {
          "planId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "priceConfig": {
            "$ref": "#/components/schemas/PriceConfig"
          },
          "creditsConfig": {
            "$ref": "#/components/schemas/CreditsConfig"
          },
          "isListed": {
            "type": "boolean"
          },
          "owner": {
            "type": "string"
          }
        }
      },
      "PaginatedPlans": {
        "type": "object",
        "properties": {
          "plans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Plan"
            }
          },
          "totalCount": {
            "type": "integer"
          },
          "page": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        }
      },
      "PriceConfig": {
        "type": "object",
        "properties": {
          "priceType": {
            "type": "string",
            "enum": [
              "FIXED_PRICE",
              "FREE"
            ]
          },
          "tokenAddress": {
            "type": "string",
            "description": "ERC20 token address (e.g., USDC)"
          },
          "amounts": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Price amounts (6 decimals for USDC)"
          },
          "receivers": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Wallet addresses to receive payment"
          }
        }
      },
      "CreditsConfig": {
        "type": "object",
        "properties": {
          "creditsType": {
            "type": "string",
            "enum": [
              "FIXED",
              "EXPIRABLE"
            ]
          },
          "amount": {
            "type": "integer",
            "description": "Number of credits"
          },
          "minAmount": {
            "type": "integer",
            "description": "Minimum credits per request"
          },
          "durationOfThePlan": {
            "type": "integer",
            "description": "Duration in seconds (for EXPIRABLE)"
          }
        }
      },
      "OrderPlanResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "txHash": {
            "type": "string"
          },
          "balance": {
            "$ref": "#/components/schemas/Balance"
          }
        }
      },
      "Balance": {
        "type": "object",
        "properties": {
          "isSubscriber": {
            "type": "boolean"
          },
          "total": {
            "type": "integer"
          },
          "used": {
            "type": "integer"
          },
          "remaining": {
            "type": "integer"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AccessTokenResponse": {
        "type": "object",
        "properties": {
          "accessToken": {
            "type": "string"
          },
          "neverminedProxyUri": {
            "type": "string"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InitializeAgentRequest": {
        "type": "object",
        "properties": {
          "accessToken": {
            "type": "string"
          },
          "requestData": {
            "type": "object"
          }
        }
      },
      "InitializeAgentResponse": {
        "type": "object",
        "properties": {
          "requestId": {
            "type": "string"
          },
          "isValid": {
            "type": "boolean"
          },
          "balance": {
            "$ref": "#/components/schemas/Balance"
          }
        }
      },
      "TrackSubTaskRequest": {
        "type": "object",
        "properties": {
          "requestId": {
            "type": "string"
          },
          "taskName": {
            "type": "string"
          },
          "creditsUsed": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "metadata": {
            "type": "object"
          }
        }
      },
      "MintCreditsRequest": {
        "type": "object",
        "required": [
          "planId",
          "holderAddress",
          "amount"
        ],
        "properties": {
          "planId": {
            "type": "string"
          },
          "holderAddress": {
            "type": "string"
          },
          "amount": {
            "type": "integer"
          }
        }
      },
      "MintExpirableCreditsRequest": {
        "type": "object",
        "required": [
          "planId",
          "holderAddress",
          "amount",
          "expirationBlock"
        ],
        "properties": {
          "planId": {
            "type": "string"
          },
          "holderAddress": {
            "type": "string"
          },
          "amount": {
            "type": "integer"
          },
          "expirationBlock": {
            "type": "integer"
          }
        }
      },
      "MintCreditsResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "txHash": {
            "type": "string"
          }
        }
      },
      "RedeemCreditsRequest": {
        "type": "object",
        "required": [
          "planId",
          "holderAddress",
          "amount",
          "proof"
        ],
        "properties": {
          "planId": {
            "type": "string"
          },
          "holderAddress": {
            "type": "string"
          },
          "amount": {
            "type": "integer"
          },
          "proof": {
            "type": "string"
          }
        }
      },
      "RedeemCreditsResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "creditsRedeemed": {
            "type": "integer"
          },
          "remainingBalance": {
            "type": "integer"
          }
        }
      },
      "X402Resource": {
        "type": "object",
        "description": "x402 protected-resource descriptor (`X402ResourceDto`). The same shape appears in the server's 402 `paymentRequired` and in the payload you mint at `POST /x402/permissions`.",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1,
            "description": "URL of the protected resource or endpoint.",
            "example": "https://myagent.ai/api/v1/tasks"
          },
          "description": {
            "type": "string",
            "description": "Human-readable description of the resource.",
            "example": "AI agent task execution"
          },
          "mimeType": {
            "type": "string",
            "description": "Expected response MIME type.",
            "example": "application/json"
          }
        },
        "required": [
          "url"
        ]
      },
      "X402SchemeExtra": {
        "type": "object",
        "description": "Scheme-specific extra fields (`X402SchemeExtraDto`). Every field is optional. On `tokenVersion` 3, `agentId` and `httpVerb` are signed as the empty string when omitted — which binds the token to that value being ABSENT rather than to any value. `version` is not part of the v3 signed struct at all, so omitting it binds nothing.",
        "properties": {
          "version": {
            "type": "string",
            "description": "Scheme version.",
            "example": "1"
          },
          "agentId": {
            "type": "string",
            "description": "Agent id the token is bound to, used for endpoint validation.",
            "example": "80918427023170428029540261117198154464497879145267720259488529685089104529015"
          },
          "httpVerb": {
            "type": "string",
            "description": "HTTP verb the token is bound to, used for endpoint validation.",
            "example": "POST"
          }
        }
      },
      "X402Scheme": {
        "type": "object",
        "description": "One x402 payment scheme (`X402SchemeDto`). The same shape appears in `accepts[]` on the server's 402 response and in `accepted` on the payload you mint — the client echoes back the scheme it selected.",
        "properties": {
          "scheme": {
            "type": "string",
            "description": "Payment scheme identifier. Selects the settlement rail: `nvm:erc4337` settles on-chain, `nvm:card-delegation` settles against an enrolled card.",
            "enum": [
              "nvm:erc4337",
              "nvm:card-delegation"
            ],
            "example": "nvm:erc4337"
          },
          "network": {
            "type": "string",
            "description": "Settlement RAIL, not the environment. Card flows send `stripe`, `braintree` or `visa`; crypto flows send `eip155:<chainId>`. An environment name such as `sandbox` or `live` is rejected by request validation.",
            "pattern": "^(?:stripe|braintree|visa|eip155:\\d+)$",
            "example": "eip155:84532"
          },
          "planId": {
            "type": "string",
            "description": "Plan id (256-bit identifier). Required for BOTH schemes — a mint without it is refused with `BCK.X402.0030` (HTTP 402). Request validation does not enforce it for either scheme: the DTO marks it optional, so the rejection comes from the scheme handler rather than from the `ValidationPipe`.",
            "example": "44742763076047497640080230236781474129970992727896593861997347135613135571071"
          },
          "extra": {
            "description": "Extra scheme-specific fields.",
            "allOf": [
              {
                "$ref": "#/components/schemas/X402SchemeExtra"
              }
            ]
          }
        },
        "required": [
          "scheme",
          "network"
        ]
      },
      "X402PaymentRequired": {
        "type": "object",
        "description": "The server's 402 PaymentRequired response (`X402PaymentRequiredDto`), echoed back to the facilitator on verify and settle. The facilitator checks that the `accepted` scheme carried inside the token matches one of `accepts`.",
        "properties": {
          "x402Version": {
            "type": "integer",
            "description": "x402 protocol version.",
            "example": 2
          },
          "error": {
            "type": "string",
            "description": "Human-readable error message from the 402 response.",
            "example": "Payment required to access resource"
          },
          "resource": {
            "description": "Protected resource information.",
            "allOf": [
              {
                "$ref": "#/components/schemas/X402Resource"
              }
            ]
          },
          "accepts": {
            "type": "array",
            "description": "Payment schemes the server accepts for this resource.",
            "items": {
              "$ref": "#/components/schemas/X402Scheme"
            }
          },
          "extensions": {
            "type": "object",
            "description": "Extensions object. Empty (`{}`) for `nvm:erc4337`, but the field itself must be present — it is validated as required.",
            "additionalProperties": true,
            "example": {}
          }
        },
        "required": [
          "x402Version",
          "resource",
          "accepts",
          "extensions"
        ]
      },
      "X402DelegationConfig": {
        "type": "object",
        "description": "Delegation options for `POST /x402/permissions` (`DelegationConfigDto`), used by both schemes. The supported flow is create-first: create the delegation with `POST /delegation/create`, then send only `delegationId` here. The inline create-on-the-fly fields are deprecated — they still work and emit a runtime warning.",
        "properties": {
          "delegationId": {
            "type": "string",
            "format": "uuid",
            "description": "Existing delegation to reuse — the supported create-first path. Must be a v4 UUID; a non-v4 id is a request-format error, not an authentication or not-found failure.",
            "example": "a1b2c3d4-e5f6-4a90-8bcd-ef1234567890"
          },
          "apiKeyId": {
            "type": "string",
            "description": "Nevermined API key id (`skId`) to restrict this delegation to. Omit so that any key can use it. Applied only when THIS request creates the delegation inline; it is ignored when you pass `delegationId`, where the restriction is read off the stored delegation instead.",
            "example": "sk-abc123"
          },
          "providerPaymentMethodId": {
            "type": "string",
            "deprecated": true,
            "description": "DEPRECATED — inline create-on-the-fly. Stripe payment method id from SetupIntent confirmation. Prefer create-first, then pass only `delegationId`.",
            "example": "pm_1Abc2Def3Ghi4Jkl"
          },
          "cardId": {
            "type": "string",
            "deprecated": true,
            "description": "DEPRECATED — inline create-on-the-fly. Payment-method entity UUID, an alternative to `providerPaymentMethodId`. Prefer create-first, then pass only `delegationId`.",
            "example": "a1b2c3d4-e5f6-4a90-8bcd-ef1234567890"
          },
          "spendingLimitCents": {
            "type": "integer",
            "minimum": 1,
            "deprecated": true,
            "description": "DEPRECATED — inline create-on-the-fly. Maximum spend in the smallest currency unit.",
            "example": 10000
          },
          "durationSecs": {
            "type": "integer",
            "minimum": 1,
            "deprecated": true,
            "description": "DEPRECATED — inline create-on-the-fly. Lifetime of the delegation in seconds.",
            "example": 604800
          },
          "currency": {
            "type": "string",
            "deprecated": true,
            "description": "DEPRECATED — inline create-on-the-fly. ISO 4217 lowercase currency code. Has no default: it is required when creating inline.",
            "enum": [
              "usd",
              "eur",
              "usdc",
              "eurc"
            ],
            "example": "usd"
          },
          "maxTransactions": {
            "type": "integer",
            "minimum": 1,
            "deprecated": true,
            "description": "DEPRECATED — inline create-on-the-fly. Maximum number of transactions allowed. Omit for unlimited.",
            "example": 50
          }
        }
      },
      "CreatePermissionRequest": {
        "type": "object",
        "description": "Body of `POST /x402/permissions` (`GenerateX402TokenDto`) — an x402 PaymentPayload. `accepted` is the only field REQUEST VALIDATION requires: the handler runs a `ValidationPipe`, so a body without it is rejected with 400 before any handler logic runs. It is not the only field the API requires — every scheme handler additionally demands `delegationConfig` and `accepted.planId`, refusing the mint with `BCK.X402.0030` (HTTP 402). There is no settlement-account field: for card schemes the seller's Stripe Connect account is resolved server-side from the plan and is never supplied by the caller.",
        "properties": {
          "resource": {
            "description": "The protected resource this token is minted for. Optional — but on `tokenVersion` 3 an omitted `resource.url` is signed as the empty string, which binds the token to having no resource rather than to any resource.",
            "allOf": [
              {
                "$ref": "#/components/schemas/X402Resource"
              }
            ]
          },
          "accepted": {
            "description": "The payment scheme you selected: `nvm:erc4337` for crypto settlement, `nvm:card-delegation` for card settlement.",
            "allOf": [
              {
                "$ref": "#/components/schemas/X402Scheme"
              }
            ]
          },
          "delegationConfig": {
            "description": "Delegation configuration. REQUIRED by both schemes despite being absent from `required` above — the DTO marks it optional and the scheme handler refuses the mint with `BCK.X402.0030` (HTTP 402). `nvm:erc4337` needs either `delegationId` or the inline triple `spendingLimitCents` + `durationSecs` + `currency`; `nvm:card-delegation` accepts an empty object `{}`, which selects a card automatically (deprecated).",
            "allOf": [
              {
                "$ref": "#/components/schemas/X402DelegationConfig"
              }
            ]
          },
          "tokenVersion": {
            "type": "integer",
            "description": "EIP-712 struct version to sign. A version 3 signature carries a one-time nonce, so the token is SINGLE-USE — it is consumed by its first settle and a second settle is refused with `BCK.X402.0059`; mint one token per paid request. A version 3 token is also bound to whichever of `accepted.extra.agentId`, `resource.url` and `accepted.extra.httpVerb` you send on this request, since each is signed as the empty string when omitted. Version 2 is the legacy shape: reusable and unbound, so a captured token replays until the permission or delegation is revoked — ask for it only if you cannot mint per request. When this field is omitted the server signs version 3, except for clients whose pinned API version is below 1.34 — that pin comes from the `Nevermined-Version` header, or, when no header is sent, from the version stamped on the API key when it was minted — which keep receiving version 2.",
            "enum": [
              2,
              3
            ],
            "example": 3
          }
        },
        "required": [
          "accepted"
        ]
      },
      "PermissionCreatedResponse": {
        "type": "object",
        "description": "Response of `POST /x402/permissions`. The endpoint returns the access token and nothing else — it does not return the permission hash, so read the new permission back off `GET /x402/permissions`.",
        "properties": {
          "accessToken": {
            "type": "string",
            "description": "x402 access token (JWT) for permission-based access. Used by the subscriber to call agent endpoints without exposing their private keys. The permission details (plan id, agent id, subscriber address, expiry, limits) are embedded in the token.",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwbGFuSWQiOiI0MzI5ODQzMjk4NDMyOSIsImFnZW50SWQiOiIxMjM0NTY3ODkwMTIzNDU2Nzg5MCJ9.x402_signature_hash"
          }
        },
        "required": [
          "accessToken"
        ]
      },
      "Permission": {
        "type": "object",
        "description": "Full details of one permission, as returned by `GET /x402/permissions/{permissionHash}`. The four delegation budget fields are present whenever the permission is linked to a delegation — on BOTH schemes, not `nvm:card-delegation` only: `PermissionEntity.createErc4337Delegation` sets `delegationId`, `spendingLimitCents` and `currency` alongside the `nvm:erc4337` scheme, and the builder gates them on `delegationId`, never on `scheme`.",
        "properties": {
          "permissionHash": {
            "type": "string",
            "description": "Unique identifier of the permission (hash)",
            "example": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
          },
          "scheme": {
            "type": "string",
            "description": "Payment scheme identifier",
            "enum": [
              "nvm:erc4337",
              "nvm:card-delegation"
            ],
            "example": "nvm:erc4337"
          },
          "planId": {
            "type": "string",
            "description": "No permission the API creates today carries one: the single write path (delegation.service.ts — permissionRepository.save) hardcodes `planId = null` as plan-agnostic, and the response builder maps `planId ?? undefined`, which `JSON.stringify` drops — so the key is ALWAYS absent, on both schemes. Retained because the column exists and older rows may carry a value."
          },
          "planName": {
            "type": "string",
            "description": "Name of the payment plan. Always the empty string on permissions the API creates today \u2014 the write path hardcodes `planName` to it. The key is always emitted, which is why it stays in `required`.",
            "example": ""
          },
          "status": {
            "type": "string",
            "description": "Current status of the permission",
            "enum": [
              "Active",
              "Revoked",
              "Expired",
              "Exhausted"
            ],
            "example": "Active"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Date when the permission was created",
            "example": "2025-05-09T12:00:00Z"
          },
          "lastUsedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Date when the permission was last used, or `null` if never used",
            "example": "2025-05-09T14:00:00Z"
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Date when the permission expires, or `null` if it does not expire",
            "example": "2025-12-20T00:00:00Z"
          },
          "redemptionLimit": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Maximum number of redemptions allowed, or `null` for unlimited",
            "example": 10
          },
          "delegationId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Linked delegation id. Present on any delegation-linked permission, on both schemes",
            "example": "a1b2c3d4-e5f6-4a90-8bcd-ef1234567890"
          },
          "spendingLimitCents": {
            "type": [
              "string",
              "null"
            ],
            "description": "Spending limit in cents. Present on any delegation-linked permission, on both schemes",
            "example": "10000"
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "Currency code. Present on any delegation-linked permission, on both schemes",
            "example": "usd"
          },
          "remainingBudgetCents": {
            "type": [
              "string",
              "null"
            ],
            "description": "Remaining budget in cents. Present on any delegation-linked permission, on both schemes",
            "example": "9000"
          }
        },
        "required": [
          "permissionHash",
          "scheme",
          "planName",
          "status",
          "createdAt",
          "lastUsedAt",
          "expiresAt",
          "redemptionLimit"
        ]
      },
      "PermissionSummary": {
        "type": "object",
        "description": "One entry of the `GET /x402/permissions` list. This is a summary — it carries no delegation budget figures; fetch `GET /x402/permissions/{permissionHash}` for those.",
        "properties": {
          "permissionHash": {
            "type": "string",
            "description": "Unique identifier of the permission (hash)",
            "example": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
          },
          "scheme": {
            "type": "string",
            "description": "Payment scheme identifier",
            "enum": [
              "nvm:erc4337",
              "nvm:card-delegation"
            ],
            "example": "nvm:erc4337"
          },
          "planId": {
            "type": "string",
            "description": "No permission the API creates today carries one: the single write path (delegation.service.ts — permissionRepository.save) hardcodes `planId = null` as plan-agnostic, and the response builder maps `planId ?? undefined`, which `JSON.stringify` drops — so the key is ALWAYS absent, on both schemes. Retained because the column exists and older rows may carry a value."
          },
          "planName": {
            "type": "string",
            "description": "Name of the payment plan. Always the empty string on permissions the API creates today \u2014 the write path hardcodes `planName` to it. The key is always emitted, which is why it stays in `required`.",
            "example": ""
          },
          "redemptionLimit": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Maximum number of redemptions allowed, or `null` for unlimited",
            "example": 10
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Date when the permission was created",
            "example": "2025-05-09T12:00:00Z"
          },
          "status": {
            "type": "string",
            "description": "Current status of the permission",
            "enum": [
              "Active",
              "Revoked",
              "Expired",
              "Exhausted"
            ],
            "example": "Active"
          },
          "delegationId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Linked delegation id, or `null` for permissions that are not delegation-linked",
            "example": "a1b2c3d4-e5f6-4a90-8bcd-ef1234567890"
          }
        },
        "required": [
          "permissionHash",
          "scheme",
          "planName",
          "redemptionLimit",
          "createdAt",
          "status",
          "delegationId"
        ]
      },
      "PaginatedPermissions": {
        "type": "object",
        "description": "A page of permissions, as returned by `GET /x402/permissions`.",
        "properties": {
          "permissions": {
            "type": "array",
            "description": "List of permissions associated with the subscriber",
            "items": {
              "$ref": "#/components/schemas/PermissionSummary"
            }
          },
          "totalResults": {
            "type": "integer",
            "description": "Total number of permissions found",
            "example": 137
          },
          "page": {
            "type": "integer",
            "description": "Current page number",
            "example": 1
          },
          "offset": {
            "type": "integer",
            "description": "Number of results per page",
            "example": 20
          }
        },
        "required": [
          "permissions",
          "totalResults",
          "page",
          "offset"
        ]
      },
      "RevokePermissionResponse": {
        "type": "object",
        "description": "Result of `DELETE /x402/permissions/{permissionHash}`. On success the endpoint returns `{ \"success\": true }` and populates none of the optional fields; a failure is reported as an error response, not as `success: false`.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Indicates whether the API call was successful",
            "example": true
          },
          "message": {
            "type": "string",
            "description": "Message describing the result of the API call"
          },
          "txHash": {
            "type": "string",
            "description": "Blockchain transaction hash associated with the API call"
          },
          "httpStatus": {
            "type": "integer",
            "description": "HTTP status code of the API response"
          },
          "data": {
            "type": "object",
            "description": "Additional parameters related to the API call",
            "additionalProperties": true
          },
          "when": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the API call was made"
          }
        },
        "required": [
          "success"
        ]
      },
      "VerifyPermissionRequest": {
        "type": "object",
        "description": "Body of `POST /x402/verify` (`SettlePermissionsDto`). `paymentRequired` and `x402AccessToken` are both required and the handler runs a `ValidationPipe`, so a body missing either is rejected with 400 before verification runs. `planId`, `subscriberAddress`, `agentId`, the endpoint and the HTTP verb are read out of the token — never sent alongside it. The settle-only fields `agentRequestId`, `batch` and `marginPercent` pass validation here but the verify handler does not read them: `agentRequestId` is generated by this endpoint and returned in the response, for you to pass back on settle.",
        "properties": {
          "paymentRequired": {
            "description": "The server's 402 PaymentRequired response. The facilitator validates that the scheme accepted inside the token matches one of `accepts`.",
            "allOf": [
              {
                "$ref": "#/components/schemas/X402PaymentRequired"
              }
            ]
          },
          "x402AccessToken": {
            "type": "string",
            "description": "The x402 payment payload, base64-encoded — the token minted by `POST /x402/permissions`.",
            "example": "eyJ4NDAyVmVyc2lvbiI6MiwicmVzb3VyY2UiOnsidXJsIjoiaHR0cHM6Ly9teWFnZW50LmFpL2FwaS92MS90YXNrcyJ9LCJhY2NlcHRlZCI6eyJzY2hlbWUiOiJudm06ZXJjNDMzNyIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MzIiLCJwbGFuSWQiOiI0NDc0Mjc2MzA3NjA0NzQ5NzY0MDA4MDIzMDIzNjc4MTQ3NDEyOTk3MDk5MjcyNzg5NjU5Mzg2MTk5NzM0NzEzNTYxMzEzNTU3MTA3MSIsImV4dHJhIjp7InZlcnNpb24iOiIxIiwiYWdlbnRJZCI6IjgwOTE4NDI3MDIzMTcwNDI4MDI5NTQwMjYxMTE3MTk4MTU0NDY0NDk3ODc5MTQ1MjY3NzIwMjU5NDg4NTI5Njg1MDg5MTA0NTI5MDE1IiwiaHR0cFZlcmIiOiJQT1NUIn19LCJwYXlsb2FkIjp7InNpZ25hdHVyZSI6IjB4MTIzNCIsImF1dGhvcml6YXRpb24iOnsiZnJvbSI6IjB4ZjM5RmQ2ZTUxYWFkODhGNkY0Y2U2YUI4ODI3Mjc5Y2ZmRmI5MjI2NiIsInNlc3Npb25LZXlzUHJvdmlkZXIiOiJ6ZXJvZGV2Iiwic2Vzc2lvbktleXMiOltdfX0sImV4dGVuc2lvbnMiOnt9fQ=="
          },
          "maxAmount": {
            "type": "string",
            "description": "Maximum credits to verify against the payer's balance. Send it as a decimal STRING: the value is parsed with `BigInt()` and then required to be a bigint, so a JSON number is rejected with 400.",
            "example": "2"
          }
        },
        "required": [
          "paymentRequired",
          "x402AccessToken"
        ]
      },
      "VerifyPermissionResponse": {
        "type": "object",
        "description": "x402 facilitator verify response. `isValid` carries the answer; everything else is contextual. Fields after `network` are Nevermined extensions to the x402 facilitator spec.",
        "properties": {
          "isValid": {
            "type": "boolean",
            "description": "Whether the payment authorization is valid",
            "example": true
          },
          "invalidReason": {
            "type": "string",
            "description": "Reason for invalidity. Only present when `isValid` is `false`.",
            "example": "Insufficient balance"
          },
          "payer": {
            "type": "string",
            "description": "Address of the payer's wallet",
            "example": "0xD4f58B60330bC59cB0A07eE6A1A66ad64244eC8c"
          },
          "network": {
            "type": "string",
            "description": "Network identifier (CAIP-2 chain id, or the payment provider for fiat rails)",
            "example": "eip155:84532"
          },
          "agentRequestId": {
            "type": "string",
            "description": "Agent request id for observability tracking (Nevermined extension). Pass it back on `POST /x402/settle`.",
            "example": "arId-123e4567-e89b-12d3-a456-426614174000"
          },
          "urlMatching": {
            "type": "string",
            "description": "URL pattern that matched the endpoint. Only present when endpoint validation is performed.",
            "example": "/api/v1/agents/:agentId/tasks"
          },
          "agentRequest": {
            "type": "object",
            "description": "Agent request context for observability (Nevermined extension)",
            "additionalProperties": true,
            "example": {
              "agentRequestId": "arId-123e4567-e89b-12d3-a456-426614174000",
              "agentName": "AI Assistant Agent",
              "agentId": "80918427023170428029540261117198154464497879145267720259488529685089104529015",
              "balance": {
                "planId": "44742763076047497640080230236781474129970992727896593861997347135613135571071",
                "planName": "Premium AI Access",
                "planType": "credits",
                "holderAddress": "0xD4f58B60330bC59cB0A07eE6A1A66ad64244eC8c",
                "balance": "98",
                "creditsContract": "0x1234567890abcdef1234567890abcdef12345678",
                "isSubscriber": true,
                "pricePerCredit": 0.001
              },
              "urlMatching": "/api/v1/agents/:agentId/tasks",
              "verbMatching": "POST",
              "batch": false
            }
          }
        },
        "required": [
          "isValid"
        ]
      },
      "SettlePermissionRequest": {
        "type": "object",
        "description": "Body of `POST /x402/settle` — the same `SettlePermissionsDto` as verify. `paymentRequired` and `x402AccessToken` are both required and the handler runs a `ValidationPipe`, so a body missing either is rejected with 400 before settlement runs. `planId`, `subscriberAddress`, `agentId`, the endpoint and the HTTP verb are read out of the token — never sent alongside it.",
        "properties": {
          "paymentRequired": {
            "description": "The server's 402 PaymentRequired response. The facilitator validates that the scheme accepted inside the token matches one of `accepts`.",
            "allOf": [
              {
                "$ref": "#/components/schemas/X402PaymentRequired"
              }
            ]
          },
          "x402AccessToken": {
            "type": "string",
            "description": "The x402 payment payload, base64-encoded — the token minted by `POST /x402/permissions`.",
            "example": "eyJ4NDAyVmVyc2lvbiI6MiwicmVzb3VyY2UiOnsidXJsIjoiaHR0cHM6Ly9teWFnZW50LmFpL2FwaS92MS90YXNrcyJ9LCJhY2NlcHRlZCI6eyJzY2hlbWUiOiJudm06ZXJjNDMzNyIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MzIiLCJwbGFuSWQiOiI0NDc0Mjc2MzA3NjA0NzQ5NzY0MDA4MDIzMDIzNjc4MTQ3NDEyOTk3MDk5MjcyNzg5NjU5Mzg2MTk5NzM0NzEzNTYxMzEzNTU3MTA3MSIsImV4dHJhIjp7InZlcnNpb24iOiIxIiwiYWdlbnRJZCI6IjgwOTE4NDI3MDIzMTcwNDI4MDI5NTQwMjYxMTE3MTk4MTU0NDY0NDk3ODc5MTQ1MjY3NzIwMjU5NDg4NTI5Njg1MDg5MTA0NTI5MDE1IiwiaHR0cFZlcmIiOiJQT1NUIn19LCJwYXlsb2FkIjp7InNpZ25hdHVyZSI6IjB4MTIzNCIsImF1dGhvcml6YXRpb24iOnsiZnJvbSI6IjB4ZjM5RmQ2ZTUxYWFkODhGNkY0Y2U2YUI4ODI3Mjc5Y2ZmRmI5MjI2NiIsInNlc3Npb25LZXlzUHJvdmlkZXIiOiJ6ZXJvZGV2Iiwic2Vzc2lvbktleXMiOltdfX0sImV4dGVuc2lvbnMiOnt9fQ=="
          },
          "maxAmount": {
            "type": "string",
            "description": "Maximum credits to redeem during settlement. Send it as a decimal STRING: the value is parsed with `BigInt()` and then required to be a bigint, so a JSON number is rejected with 400.",
            "example": "2"
          },
          "agentRequestId": {
            "type": "string",
            "maxLength": 128,
            "pattern": "^[a-zA-Z0-9\\-_]{1,128}$",
            "description": "Agent request id for observability, as returned by `POST /x402/verify`. Bounded to 1–128 URL-safe ASCII characters because the value is interpolated into a Helicone filter and persisted to `agent_tasks`.",
            "example": "arId-123e4567-e89b-12d3-a456-426614174000"
          },
          "batch": {
            "type": "boolean",
            "description": "Whether this settles a batch — several LLM calls made under one `agentRequestId`.",
            "example": true
          },
          "marginPercent": {
            "type": "number",
            "minimum": 0,
            "maximum": 10,
            "description": "Margin percentage used for credit calculation. Mutually exclusive with `maxAmount` when `agentRequestId` is supplied.",
            "example": 0.2
          }
        },
        "required": [
          "paymentRequired",
          "x402AccessToken"
        ]
      },
      "SettlePermissionResponse": {
        "type": "object",
        "description": "Settlement receipt. `success` alone tells you the settle worked; what to check in addition depends on `billingModel`. On `credits` plans the payment shows up as `creditsRedeemed > 0`. On `pay-as-you-go` plans there is no credit balance, so `creditsRedeemed` and `remainingBalance` are always the string \"0\" even on a charge that succeeded — the proof there is `success: true` plus a non-empty `orderTx` (fiat rails) or `transaction` (crypto rails). Do not gate on `creditsRedeemed` without reading `billingModel` first: on a card rail that reports a real charge as a decline and invites a retry of a payment that already went through. Note the credit fields are strings, so \"0\" is truthy while Number(\"0\") > 0 is false.",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether settlement was successful."
          },
          "errorReason": {
            "type": "string",
            "description": "Reason for settlement failure (only present if success is false)."
          },
          "payer": {
            "type": "string",
            "description": "Address of the payer's wallet."
          },
          "transaction": {
            "type": "string",
            "description": "Blockchain transaction hash. On crypto pay-as-you-go plans this is also the reference for the per-request charge."
          },
          "network": {
            "type": "string",
            "description": "Network identifier. The discriminator is the rail, not the billing model: a CAIP-2 chain id (eip155:84532) for crypto rails under both billing models, or the settling payment provider (stripe, braintree, visa) for fiat card-delegation rails.",
            "example": "eip155:84532"
          },
          "billingModel": {
            "type": "string",
            "enum": [
              "credits",
              "pay-as-you-go"
            ],
            "description": "Which billing model this settle was priced under. Present regardless of `success` — check `success` before treating it as evidence of a charge. Read it before the two credit fields."
          },
          "creditsRedeemed": {
            "type": "string",
            "description": "Number of credits redeemed. Always \"0\" for pay-as-you-go plans, which hold no credit balance — including on a settle that charged the buyer successfully.",
            "example": "2"
          },
          "remainingBalance": {
            "type": "string",
            "description": "Subscriber's remaining credit balance. Always \"0\" for pay-as-you-go plans — the per-request charge is referenced by `orderTx` (fiat) or `transaction` (crypto), not here.",
            "example": "98"
          },
          "orderTx": {
            "type": "string",
            "description": "Reference for the order or per-request charge, if one occurred. On fiat pay-as-you-go this is the per-request charge (a Stripe PaymentIntent id, a Braintree transaction id); crypto pay-as-you-go reports its on-chain order in `transaction` instead. On credits plans it is set only when the settle had to order credits first (auto top-up).",
            "example": "pi_3TUrvfBYvSRKcV420xCBjHb1"
          }
        }
      },
      "DelegationDetailsDto": {
        "type": "object",
        "properties": {
          "delegationId": {
            "type": "string",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          },
          "provider": {
            "type": "string",
            "example": "stripe"
          },
          "providerPaymentMethodId": {
            "type": "string",
            "example": "pm_1Abc2Def3Ghi4Jkl"
          },
          "status": {
            "type": "string",
            "example": "Active",
            "enum": [
              "Active",
              "Revoked",
              "Expired",
              "Exhausted"
            ]
          },
          "spendingLimitCents": {
            "type": "string",
            "example": "10000"
          },
          "amountSpentCents": {
            "type": "string",
            "example": "2500"
          },
          "remainingBudgetCents": {
            "type": "string",
            "example": "7500"
          },
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "transactionCount": {
            "type": "number",
            "example": 12
          },
          "expiresAt": {
            "format": "date-time",
            "type": "string",
            "example": "2026-02-20T00:00:00Z"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "example": "2026-02-13T10:30:00Z"
          },
          "apiKeyId": {
            "type": [
              "string",
              "null"
            ],
            "example": "sk-abc123",
            "description": "NVM API Key ID restricted to this delegation. Null = any key can use it."
          },
          "planId": {
            "type": [
              "string",
              "null"
            ],
            "example": "123456789"
          },
          "maxTransactions": {
            "type": [
              "integer",
              "null"
            ],
            "example": 50
          },
          "lastUsedAt": {
            "type": [
              "string",
              "null"
            ],
            "example": "2026-02-13T14:00:00Z",
            "format": "date-time"
          },
          "fundingWalletAddress": {
            "type": [
              "string",
              "null"
            ],
            "example": "0x1234abcd5678ef901234abcd5678ef901234abcd",
            "description": "On-chain wallet the Router crypto rail spends from for this delegation — the caller EOA (7702 kernel address for migrated wallets) for a personal delegation, or the organization wallet for an org-wallet delegation. Fund/check THIS wallet before a Router call. Only returned when requested with `?withPayer=true`. `null` on card-backed delegations (a card is not funded from a wallet), or on a crypto delegation whose payer wallet could not be resolved."
          },
          "lockedInChannels": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/LockedChannelFundsDto"
            },
            "description": "Money `fundingWalletAddress` has deposited into MPP session payment channels and can still get back, per chain and token. It has left the wallet but it is not spent and it is not charged to this budget: it comes back to the wallet when the channel closes. It is the FUNDING WALLET’s total, not a per-delegation figure: every delegation spending from the same wallet reports the same channels. Only returned when requested with `?withPayer=true`. An empty list means nothing is locked, and is what a card-backed delegation reports. `null` means it could not be determined (the funding wallet could not be resolved, or the read failed), which is not the same as nothing locked."
          }
        },
        "required": [
          "delegationId",
          "provider",
          "providerPaymentMethodId",
          "status",
          "spendingLimitCents",
          "amountSpentCents",
          "remainingBudgetCents",
          "currency",
          "transactionCount",
          "expiresAt",
          "createdAt",
          "planId",
          "maxTransactions",
          "lastUsedAt"
        ]
      },
      "LockedChannelFundsDto": {
        "type": "object",
        "description": "Money a wallet has locked in MPP session payment channels, on one chain, in one token.",
        "properties": {
          "network": {
            "type": "integer",
            "example": 4217,
            "description": "Chain id the payment channels are on"
          },
          "token": {
            "type": "string",
            "example": "0x20C000000000000000000000b9537d11c60E8b50",
            "description": "The token the channels hold in escrow, EIP-55 checksummed"
          },
          "symbol": {
            "type": [
              "string",
              "null"
            ],
            "example": "USDC.e",
            "description": "Ticker, or null when the token is not one Nevermined recognises"
          },
          "decimals": {
            "type": [
              "integer",
              "null"
            ],
            "example": 6,
            "description": "Decimal scale of `atomic`, or null when unknown"
          },
          "atomic": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]*)$",
            "example": "6000000",
            "description": "Atomic (integer) amount locked, as a string: over every payment channel on this chain and token that is not closed yet, the deposit minus the payments its vouchers already authorized."
          },
          "formatted": {
            "type": [
              "string",
              "null"
            ],
            "example": "6",
            "description": "Human-readable amount; null exactly when `decimals` is"
          }
        },
        "required": [
          "network",
          "token",
          "symbol",
          "decimals",
          "atomic",
          "formatted"
        ]
      },
      "DelegationTransactionListDto": {
        "type": "object",
        "properties": {
          "totalResults": {
            "type": "number"
          },
          "page": {
            "type": "number"
          },
          "offset": {
            "type": "number"
          },
          "transactions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DelegationTransactionDto"
            }
          }
        },
        "required": [
          "totalResults",
          "page",
          "offset",
          "transactions"
        ]
      },
      "_NvmAPIResultDto": {
        "type": "object",
        "description": "Generic API result envelope (`NvmAPIResultDto`). Only `success` is guaranteed — every other field is optional on the DTO, and `DELETE /delegation/{delegationId}`, its only consumer here, returns `{ success: true }` and nothing else.",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true,
            "description": "Indicates whether the API call was successful"
          },
          "message": {
            "type": "string",
            "example": "Successfully minted",
            "description": "Message describing the result of the API call"
          },
          "txHash": {
            "type": "string",
            "example": "0x923812b3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef12345678",
            "description": "Blockchain transaction hash associated with the API call"
          },
          "httpStatus": {
            "type": "integer",
            "example": 200,
            "description": "HTTP status code of the API response"
          },
          "data": {
            "type": "object",
            "description": "Additional parameters related to the API call",
            "additionalProperties": true
          },
          "when": {
            "format": "date-time",
            "type": "string",
            "description": "Timestamp when the API call was made"
          }
        },
        "required": [
          "success"
        ]
      },
      "CreateDelegationResponseDto": {
        "type": "object",
        "properties": {
          "delegationId": {
            "type": "string",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          },
          "delegationToken": {
            "type": [
              "string",
              "null"
            ],
            "example": "eyJhbGciOiJIUzI1NiIsInR...",
            "description": "Signed JWT for fiat-backed delegations (stripe/braintree). Omitted for crypto (erc4337) flows where the signed delegation is held client-side."
          }
        },
        "required": [
          "delegationId"
        ]
      },
      "PaymentMethodSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "pm_1Abc2Def3Ghi4Jkl"
          },
          "type": {
            "type": "string",
            "example": "card"
          },
          "last4": {
            "type": "string",
            "example": "4242"
          },
          "brand": {
            "type": "string",
            "example": "visa"
          },
          "expMonth": {
            "type": [
              "integer",
              "null"
            ],
            "example": 12
          },
          "expYear": {
            "type": [
              "integer",
              "null"
            ],
            "example": 2027
          },
          "alias": {
            "type": [
              "string",
              "null"
            ],
            "example": "My Card"
          },
          "provider": {
            "type": "string",
            "example": "stripe",
            "enum": [
              "stripe",
              "braintree",
              "erc4337",
              "visa",
              "vgs"
            ]
          },
          "status": {
            "type": "string",
            "example": "Active",
            "enum": [
              "Active",
              "Revoked"
            ]
          },
          "allowedApiKeyIds": {
            "type": [
              "array",
              "null"
            ],
            "example": [
              "sk-abc123",
              "sk-def456"
            ],
            "description": "NVM API Key IDs allowed to use this card. Null = any key can use it.",
            "items": {
              "type": "string"
            }
          },
          "orgId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Organization this card belongs to (null = personal)."
          }
        },
        "required": [
          "id",
          "type",
          "last4",
          "brand",
          "expMonth",
          "expYear",
          "alias",
          "provider",
          "status"
        ]
      },
      "DelegationTransactionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "tx-uuid"
          },
          "delegationId": {
            "type": "string",
            "example": "delegation-uuid"
          },
          "providerTransactionId": {
            "type": "string",
            "example": "pi_1Abc2Def3Ghi4Jkl"
          },
          "amountCents": {
            "type": "string",
            "example": "1000"
          },
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "status": {
            "type": "string",
            "example": "succeeded"
          },
          "failureReason": {
            "type": [
              "string",
              "null"
            ],
            "example": null
          },
          "feeCents": {
            "type": "string",
            "description": "Nevermined\u2019s routing fee for this charge, in cents, already INCLUDED in `amountCents`. Charged only on payments routed to an external merchant via the Router \u2014 `\"0\"` on every other row (a plan purchase on the card rails, or a crypto settlement), where no routing fee applies. On a Router row `amountCents - feeCents` is the merchant leg \u2014 **unless `feeReleased` is `true`, in which case `amountCents` is ALREADY net of the fee and must not be reduced again**. On other rows the identity does not hold either, because a plan purchase can carry a platform margin taken out of the price that this figure does not describe. **`\"0\"` does not by itself mean no fee applied:** the two figures round in opposite directions, so a fee too small to add a cent to the reserve still shows here as `\"0\"`. Read `feeAtomic` to answer that.",
            "example": "0"
          },
          "feeAtomic": {
            "type": "string",
            "description": "The same fee in the settlement asset\u2019s smallest unit, rounded down. This is the field that answers \u201Cdid a routing fee apply at all\u201D \u2014 `\"0\"` means none did. The asset it is denominated in is the one the Router settled, reported on `GET /router/payments`; it is NOT the same unit as `amountCents`.",
            "example": "0"
          },
          "feeBps": {
            "type": "number",
            "description": "The fee rate applied, in basis points over a 10,000 denominator. Pinned per row, so a later rate change never rewrites what an old payment was charged. `0` when no fee applied.",
            "example": 0
          },
          "feeReleased": {
            "type": "boolean",
            "description": "True when the routing fee was not collected and has been credited back to your cap. Non-delivery by the merchant is one cause; a fee leg that was refused, or expired before it could be broadcast, is another \u2014 so this does NOT mean the merchant failed you. **`amountCents` has ALREADY been reduced by `feeCents` when this is true**, so do not subtract the fee a second time. It does not follow that the remainder is the merchant leg: a payment whose merchant reserve was also released has that deducted from the same field and reads `status: 'failed'`. A fee too small to reserve a cent is released without ever setting this flag, so a row may read `false` here while `GET /router/payments` reports the fee `Released`; `amountCents` is untouched in that case, so the arithmetic above still holds.",
            "example": false
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "example": "2026-02-13T14:00:00Z"
          }
        },
        "required": [
          "id",
          "delegationId",
          "providerTransactionId",
          "amountCents",
          "currency",
          "status",
          "failureReason",
          "feeCents",
          "feeAtomic",
          "feeBps",
          "feeReleased",
          "createdAt"
        ]
      },
      "DelegationListDto": {
        "type": "object",
        "properties": {
          "totalResults": {
            "type": "number"
          },
          "page": {
            "type": "number"
          },
          "offset": {
            "type": "number"
          },
          "delegations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DelegationSummaryDto"
            }
          }
        },
        "required": [
          "totalResults",
          "page",
          "offset",
          "delegations"
        ]
      },
      "CreateDelegationDto": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "description": "Payment provider type. `stripe` / `braintree` / `visa` / `erc4337` select that rail directly. `vgs` (epic #1958) is a unified VGS-Collect card: a PLAN-AGNOSTIC, portable delegation whose settlement PSP (`stripe` / `braintree`) is resolved AT CHARGE TIME from the plan being paid (`fiatPaymentProvider`), so ONE card can charge ANY PSP (#2082). `delegation.provider` is persisted as the portable `vgs` marker (the concrete charge never sees it — the handler derives the real PSP at settle). Requires a portable `providerPaymentMethodId` (the `CRD…` cardId, which must carry an active network token); `planId` is OPTIONAL — pass it only to BIND the delegation to a single plan.",
            "example": "stripe",
            "enum": [
              "stripe",
              "erc4337",
              "braintree",
              "visa",
              "vgs"
            ]
          },
          "providerPaymentMethodId": {
            "type": "string",
            "description": "Payment-method identifier from the provider. For stripe / braintree this is the saved card; for visa this is the VGS agentic-token id returned by enrollment; for vgs this is the `CRD…` canonical cardId from the unified VGS-Collect enroll. Required for these providers; ignored for erc4337.",
            "example": "pm_1Abc2Def3Ghi4Jkl"
          },
          "consumerPrompt": {
            "type": "string",
            "description": "Visa-only: human-readable approval prompt shown to the user during VGS Agentic Auth device binding. Required when provider=visa.",
            "example": "Allow up to USD 50.00 over 5 transactions"
          },
          "assuranceData": {
            "type": "object",
            "description": "Visa-only: opaque assurance payload from the VGS Agentic Auth browser SDK after FIDO/OTP device binding. Required when provider=visa."
          },
          "spendingLimitCents": {
            "type": "number",
            "description": "Maximum spending limit in cents (smallest currency unit)",
            "example": 10000
          },
          "durationSecs": {
            "type": "number",
            "description": "Duration of the delegation in seconds",
            "example": 604800
          },
          "currency": {
            "type": "string",
            "description": "Currency code (lowercase)",
            "example": "usd",
            "enum": [
              "usd",
              "eur",
              "usdc",
              "eurc"
            ]
          },
          "planId": {
            "type": "string",
            "description": "Plan ID to restrict this delegation to. Optional and plan-agnostic by default (#1534), INCLUDING provider=vgs (#2082): a unified VGS card delegation is plan-agnostic and the settlement PSP is resolved at charge time from the plan being paid, so one card can charge any PSP — pass planId only to BIND it to a single plan. EXCEPT provider=visa (the VGS mandate is merchant-scoped, derived from the plan owner; see BCK.VISA.0015), where it is REQUIRED.",
            "example": "123456789"
          },
          "maxTransactions": {
            "type": "number",
            "description": "Maximum number of transactions allowed (null = unlimited)",
            "example": 50
          },
          "apiKeyId": {
            "type": "string",
            "description": "Optional API Key ID (skId) to restrict this delegation to",
            "example": "sk-abc123"
          },
          "allowedRecipients": {
            "description": "erc4337 / Router only: restrict spend to these pay-to addresses — the Router rejects payments to any other recipient. Omit for no recipient restriction.",
            "example": [
              "0x209693Bc6afc0C5328bA36FaF03C514EF312287C"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "organizationWalletId": {
            "type": "string",
            "description": "erc4337 / Router only (#2230): fund this delegation from an ORGANIZATION wallet instead of the caller's own wallet. The caller must be an admin of the wallet's org. Spend is always capped by the SPENDING member's group budget (the wallet must be shared with the member's group).",
            "example": "orw-abc123"
          }
        },
        "required": [
          "provider",
          "spendingLimitCents",
          "durationSecs",
          "currency"
        ]
      },
      "UpdatePaymentMethodDto": {
        "type": "object",
        "properties": {
          "alias": {
            "type": "string",
            "description": "Optional friendly name for the card",
            "example": "My Production Card"
          },
          "allowedApiKeyIds": {
            "description": "Array of NVM API Key skId values allowed to use this card. Null or empty = any key can use it.",
            "example": [
              "sk-abc123",
              "sk-def456"
            ],
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "orgId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Move the card to an org (a valid orgId) or to personal (null). Omit to leave unchanged."
          }
        }
      },
      "DelegationSummaryDto": {
        "type": "object",
        "properties": {
          "delegationId": {
            "type": "string",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          },
          "provider": {
            "type": "string",
            "example": "stripe"
          },
          "providerPaymentMethodId": {
            "type": "string",
            "example": "pm_1Abc2Def3Ghi4Jkl"
          },
          "status": {
            "type": "string",
            "example": "Active",
            "enum": [
              "Active",
              "Revoked",
              "Expired",
              "Exhausted"
            ]
          },
          "spendingLimitCents": {
            "type": "string",
            "example": "10000"
          },
          "amountSpentCents": {
            "type": "string",
            "example": "2500"
          },
          "remainingBudgetCents": {
            "type": "string",
            "example": "7500"
          },
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "transactionCount": {
            "type": "number",
            "example": 12
          },
          "expiresAt": {
            "format": "date-time",
            "type": "string",
            "example": "2026-02-20T00:00:00Z"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "example": "2026-02-13T10:30:00Z"
          },
          "apiKeyId": {
            "type": [
              "string",
              "null"
            ],
            "example": "sk-abc123",
            "description": "NVM API Key ID restricted to this delegation. Null = any key can use it."
          }
        },
        "required": [
          "delegationId",
          "provider",
          "providerPaymentMethodId",
          "status",
          "spendingLimitCents",
          "amountSpentCents",
          "remainingBudgetCents",
          "currency",
          "transactionCount",
          "expiresAt",
          "createdAt"
        ]
      },
      "CreatePaymentDto": {
        "type": "object",
        "properties": {
          "delegationId": {
            "type": "string",
            "description": "Delegation id (the caller's individual spend authority) to spend against — created via POST /api/v1/delegation/create and validated as the caller’s own erc4337 delegation."
          },
          "protocol": {
            "type": "string",
            "description": "Payment protocol. `x402` (EIP-3009 exact) or `mpp` (Merchant Payment Protocol, tempo `charge`). The `target` shape depends on this: x402 → `{ accepts, x402Version? }`; mpp → `{ challenge }`.",
            "enum": [
              "x402",
              "mpp"
            ],
            "example": "x402"
          },
          "target": {
            "type": "object",
            "description": "The upstream's payment requirements, taken verbatim from its HTTP 402 response — its shape depends on `protocol`.\n\n• **x402** — pass `{ accepts: [...], x402Version? }`: `accepts` is the array of payment options the agent advertised (each with `scheme`, `network`, `asset`, `payTo`, `maxAmountRequired`/`amount`, and `extra.name`/`extra.version` for the EIP-712 domain). Get it from the 402 body (x402 v1: `{ x402Version: 1, accepts }`) or by base64-decoding the `PAYMENT-REQUIRED` response header (x402 v2). `x402Version` defaults to 2 when omitted — set it to 1 for x402-express agents.\n\n• **mpp** — pass `{ challenge }`, where `challenge` is the raw `WWW-Authenticate: Payment …` header value from the merchant’s 402, verbatim — e.g. `Payment id=\"…\", method=\"tempo\", intent=\"charge\", request=\"<base64url>\"`. The Router decodes it, verifies the buyer wallet can fund the tempo `charge`, signs a native Tempo transaction, and returns an `Authorization: Payment <…>` credential (the ledger scheme is `charge`). The tempo payment currency (token address) must be on the operator’s per-chain allowlist.",
            "example": {
              "x402Version": 1,
              "accepts": [
                {
                  "scheme": "exact",
                  "network": "base-sepolia",
                  "maxAmountRequired": "1000",
                  "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
                  "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
                  "maxTimeoutSeconds": 60,
                  "extra": {
                    "name": "USDC",
                    "version": "2"
                  }
                }
              ]
            }
          },
          "resourceUrl": {
            "type": "string",
            "description": "Upstream resource URL (absolute http(s)) — recorded with the payment."
          },
          "requestId": {
            "type": "string",
            "description": "Caller correlation / idempotency id — at most one credential is minted per requestId; recorded."
          }
        },
        "required": [
          "delegationId",
          "protocol",
          "target"
        ]
      },
      "ProxyRequestDto": {
        "type": "object",
        "properties": {
          "delegationId": {
            "type": "string",
            "description": "The caller's erc4337 delegation to spend against (as in `POST /router/payments`)."
          },
          "url": {
            "type": "string",
            "description": "Absolute upstream URL to route to (http/https only). Provide EXACTLY ONE of `url` or `slug`.",
            "example": "https://agent.example/api/resource"
          },
          "slug": {
            "type": "string",
            "description": "Catalog slug to invoke via the opaque Router broker. The Router resolves the slug to the real upstream server-side, so the URL is never exposed. Mutually exclusive with `url`. Required to read the result of an async service (an endpoint the catalog marks `auth: \"siwx\"`): the Router answers its wallet challenge only on a slug call.",
            "example": "webscrape-pro"
          },
          "path": {
            "type": "string",
            "description": "Optional subpath appended to the resolved upstream when `slug` is used (e.g. `scrape` → the merchant's `/scrape`). Path segments only — it may not change the host: `//`, `..`, a backslash and an embedded absolute URL are rejected (400). A catalog template placeholder (`{jobId}`, `:id`) must be filled with a real value, or the call is refused (400). Ignored when `url` is used.",
            "example": "scrape"
          },
          "search": {
            "type": "string",
            "description": "Query string for a slug-routed request, without the leading ?. It is attached to the resolved merchant URL without changing its host. Use this instead of embedding ? in path. With a raw url, include the query in url; sending search with url is refused.",
            "example": "flight_iata=AA217&limit=1",
            "maxLength": 2048
          },
          "method": {
            "type": "string",
            "description": "HTTP method to use for the upstream request.",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE"
            ],
            "default": "GET"
          },
          "headers": {
            "type": "object",
            "description": "Headers to forward to the upstream (e.g. the agent’s own auth). Optional."
          },
          "body": {
            "description": "Request body to forward (JSON). Optional."
          },
          "protocol": {
            "type": "string",
            "description": "Payment protocol. On the mode-B proxy (`/route`, `/proxy`) this is ADVISORY: the Router AUTO-DETECTS the protocol from the upstream 402 — `WWW-Authenticate: Payment` → `mpp`; `accepts` / `PAYMENT-REQUIRED` → `x402` — and the DETECTED protocol is authoritative for the mint (a mismatching hint here does not change what gets paid).",
            "enum": [
              "x402",
              "mpp"
            ],
            "default": "x402"
          },
          "requestId": {
            "type": "string",
            "description": "Idempotency key — REQUIRED for routed payments (mode B). The Router makes the upstream call and pays automatically, so a retry after a dropped connection must not double-spend. At most one payment is minted per (caller, requestId); a duplicate returns 409 (BCK.ROUTER.0002) carrying the original paymentId, rather than paying again.",
            "example": "order-1234"
          }
        },
        "required": [
          "delegationId",
          "requestId"
        ]
      },
      "CommerceRouteRequestDto": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Absolute upstream URL to route to (http/https only). Provide EXACTLY ONE of `url` or `slug` — both together is a 400. Use `slug` for a cataloged service (the opaque broker); `url` is for an off-catalog target you already hold the URL of.",
            "example": "https://agent.example/api/resource"
          },
          "slug": {
            "type": "string",
            "description": "Catalog slug to invoke via the opaque Router broker. The Router resolves the slug to the real upstream server-side, so the URL is never exposed. Mutually exclusive with `url`.",
            "example": "webscrape-pro"
          },
          "path": {
            "type": "string",
            "description": "Optional subpath appended to the resolved upstream when `slug` is used (e.g. `scrape` → the merchant's `/scrape`). Path segments only — it may not change the host, so `//`, `@`, `..` and an embedded absolute URL are rejected (400). Ignored when `url` is used.",
            "example": "scrape"
          },
          "method": {
            "type": "string",
            "description": "HTTP method to use for the upstream request.",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE"
            ],
            "default": "GET"
          },
          "headers": {
            "type": "object",
            "description": "Headers to forward to the upstream (e.g. the agent’s own auth). Optional."
          },
          "body": {
            "description": "Request body to forward (JSON). Optional."
          },
          "protocol": {
            "type": "string",
            "description": "Payment protocol. On the mode-B proxy (`/route`, `/proxy`) this is ADVISORY: the Router AUTO-DETECTS the protocol from the upstream 402 — `WWW-Authenticate: Payment` → `mpp`; `accepts` / `PAYMENT-REQUIRED` → `x402` — and the DETECTED protocol is authoritative for the mint (a mismatching hint here does not change what gets paid).",
            "enum": [
              "x402",
              "mpp"
            ],
            "default": "x402"
          },
          "requestId": {
            "type": "string",
            "description": "Idempotency key — REQUIRED for routed payments (mode B). The Router makes the upstream call and pays automatically, so a retry after a dropped connection must not double-spend. At most one payment is minted per (caller, requestId); a duplicate returns 409 (BCK.ROUTER.0002) carrying the original paymentId, rather than paying again.",
            "example": "order-1234"
          }
        },
        "required": [
          "requestId"
        ]
      },
      "QuoteRequestDto": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Absolute upstream URL to route to (http/https only). Provide EXACTLY ONE of `url` or `slug` — both together is a 400. Use `slug` for a cataloged service (the opaque broker); `url` is for an off-catalog target you already hold the URL of.",
            "example": "https://agent.example/api/resource"
          },
          "slug": {
            "type": "string",
            "description": "Catalog slug to invoke via the opaque Router broker. The Router resolves the slug to the real upstream server-side, so the URL is never exposed. Mutually exclusive with `url`.",
            "example": "webscrape-pro"
          },
          "path": {
            "type": "string",
            "description": "Optional subpath appended to the resolved upstream when `slug` is used (e.g. `scrape` → the merchant's `/scrape`). Path segments only — it may not change the host: `//`, `..`, a backslash and an embedded absolute URL are rejected (400). An `@` is allowed (e.g. an `/inbox/foo@bar.com` route) — it stays in the path and the composed origin is re-asserted against the resolved host. Ignored when `url` is used.",
            "example": "scrape"
          },
          "search": {
            "type": "string",
            "description": "Query string for a slug-routed request, without the leading ?. It is attached to the resolved merchant URL without changing its host. Use this instead of embedding ? in path. With a raw url, include the query in url; sending search with url is refused.",
            "example": "flight_iata=AA217&limit=1",
            "maxLength": 2048
          },
          "method": {
            "type": "string",
            "description": "HTTP method to use for the upstream request.",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE"
            ],
            "default": "GET"
          },
          "headers": {
            "type": "object",
            "description": "Headers to forward to the upstream (e.g. the agent’s own auth). Optional."
          },
          "body": {
            "description": "Request body to forward (JSON). Optional."
          },
          "credentialHeader": {
            "type": "string",
            "description": "Header the Router should carry the minted payment credential in on the paid hop. Send this only when the service needs its own auth AND a payment: the MPP rails carry their credential in `Authorization`, which is also where your merchant auth goes, and rather than silently replacing yours the Router refuses the call with BCK.ROUTER.0021. Naming the header the service documents for its credential (a separate `Payment` header is the common one) leaves your `Authorization` untouched. It is honoured on every rail, so do not set it by default: putting the credential somewhere the service does not read makes the paid hop fail.",
            "example": "Payment",
            "maxLength": 64
          },
          "delegationId": {
            "type": "string",
            "description": "The delegation you would pay with. Optional. When given, the quote prices the options that delegation would select: an organization-wallet delegation pays only in its own currency, a card delegation pays over the MPP-stripe rail, and a recipient allowlist refuses a pay-to address it would refuse. Without it, the quote prices what a personal crypto (erc4337) delegation would select. `optionSet` in the response says which was used. Either way the quote checks neither the remaining cap nor the wallet balance.",
            "example": "5e7481c3-e972-45bd-bdc5-a0b99c4de4a1"
          }
        }
      },
      "CommerceQuoteRequestDto": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Absolute upstream URL to route to (http/https only). Provide EXACTLY ONE of `url` or `slug` — both together is a 400. Use `slug` for a cataloged service (the opaque broker); `url` is for an off-catalog target you already hold the URL of.",
            "example": "https://agent.example/api/resource"
          },
          "slug": {
            "type": "string",
            "description": "Catalog slug to invoke via the opaque Router broker. The Router resolves the slug to the real upstream server-side, so the URL is never exposed. Mutually exclusive with `url`.",
            "example": "webscrape-pro"
          },
          "path": {
            "type": "string",
            "description": "Optional subpath appended to the resolved upstream when `slug` is used (e.g. `scrape` → the merchant's `/scrape`). Path segments only — it may not change the host: `//`, `..`, a backslash and an embedded absolute URL are rejected (400). An `@` is allowed (e.g. an `/inbox/foo@bar.com` route) — it stays in the path and the composed origin is re-asserted against the resolved host. Ignored when `url` is used.",
            "example": "scrape"
          },
          "search": {
            "type": "string",
            "description": "Query string for a slug-routed request, without the leading ?. It is attached to the resolved merchant URL without changing its host. Use this instead of embedding ? in path. With a raw url, include the query in url; sending search with url is refused.",
            "example": "flight_iata=AA217&limit=1",
            "maxLength": 2048
          },
          "method": {
            "type": "string",
            "description": "HTTP method to use for the upstream request.",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE"
            ],
            "default": "GET"
          },
          "headers": {
            "type": "object",
            "description": "Headers to forward to the upstream (e.g. the agent’s own auth). Optional."
          },
          "body": {
            "description": "Request body to forward (JSON). Optional."
          },
          "credentialHeader": {
            "type": "string",
            "description": "Header the Router should carry the minted payment credential in on the paid hop. Send this only when the service needs its own auth AND a payment: the MPP rails carry their credential in `Authorization`, which is also where your merchant auth goes, and rather than silently replacing yours the Router refuses the call with BCK.ROUTER.0021. Naming the header the service documents for its credential (a separate `Payment` header is the common one) leaves your `Authorization` untouched. It is honoured on every rail, so do not set it by default: putting the credential somewhere the service does not read makes the paid hop fail.",
            "example": "Payment",
            "maxLength": 64
          },
          "delegationId": {
            "type": "string",
            "description": "Must NOT be sent. The quote prices the delegation your grant is pinned to — the one the user consented to and capped. Supplying one is refused rather than ignored; use POST /api/v1/router/quote with a plain API key to price a delegation of your choice."
          }
        }
      },
      "SettlePaymentDto": {
        "type": "object",
        "properties": {
          "txHash": {
            "type": "string",
            "description": "On-chain settlement tx hash (from the upstream's PAYMENT-RESPONSE header).",
            "example": "0xfc8af37baae588b5ee4a5e9212532b3d46702ac009b64c17e8ff810fd90ac493"
          }
        },
        "required": [
          "txHash"
        ]
      },
      "PaymentCredentialDto": {
        "type": "object",
        "properties": {
          "transport": {
            "type": "string",
            "description": "How the credential is delivered to the upstream. Always `header`.",
            "enum": [
              "header"
            ],
            "example": "header"
          },
          "name": {
            "type": "string",
            "description": "The exact HTTP header name to set on your request to the upstream agent. `PAYMENT-SIGNATURE` for x402 v2, `X-PAYMENT` for x402 v1, `Authorization` for mpp.",
            "enum": [
              "PAYMENT-SIGNATURE",
              "X-PAYMENT",
              "Authorization"
            ],
            "example": "PAYMENT-SIGNATURE"
          },
          "value": {
            "type": "string",
            "description": "The header VALUE to set. For x402 a base64-encoded signed payment payload; for mpp a `Payment <base64url>` credential. Opaque — attach it verbatim; do not modify.",
            "example": "eyJ4NDAyVmVyc2lvbiI6MiwiYWNjZXB0ZWQiOnsuLi59LCJwYXlsb2FkIjp7Li4ufX0="
          }
        },
        "required": [
          "transport",
          "name",
          "value"
        ]
      },
      "SettlementDescriptorDto": {
        "type": "object",
        "properties": {
          "recipient": {
            "type": "string",
            "description": "The on-chain recipient (the agent’s `payTo` address from its 402).",
            "example": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C"
          },
          "amount": {
            "type": "string",
            "description": "Amount in the asset’s smallest unit (USDC/EURC = 6 decimals).",
            "example": "1000"
          },
          "asset": {
            "type": "string",
            "description": "For x402 the asset symbol (`USDC`/`EURC`); for mpp the tempo payment token contract address (mpp carries no on-wire symbol).",
            "example": "USDC"
          },
          "network": {
            "type": "string",
            "description": "Network the payment settles on (name or CAIP-2).",
            "example": "base-sepolia"
          },
          "approxCents": {
            "type": "string",
            "description": "The MERCHANT leg in cents (rounded up) — what this credential pays the upstream agent. This is not necessarily the whole cap charge: Nevermined’s routing fee is reserved on top, so the whole-cent total debited from the delegation is `approxCents + fee.cents` (also reported directly as `fee.capChargedCents`; the exact sub-cent debit is `fee.capChargedMicros`). With no fee configured the two are equal.",
            "example": "1"
          },
          "scheme": {
            "type": "string",
            "description": "Ledger settlement scheme: `exact` for x402 (EIP-3009), `charge` for MPP-tempo (a native Tempo transaction), `spt` for MPP-stripe (a Shared Payment Token). Omitted (defaults to `exact`) on the x402 path.",
            "enum": [
              "exact",
              "charge",
              "spt"
            ],
            "example": "exact"
          }
        },
        "required": [
          "recipient",
          "amount",
          "network",
          "approxCents"
        ]
      },
      "RouterFeeDto": {
        "type": "object",
        "properties": {
          "bps": {
            "type": "number",
            "description": "Rate applied to this payment, in basis points over a 10,000 denominator (e.g. `200` = 2%). `0` means no routing fee was charged.",
            "example": 0
          },
          "amount": {
            "type": "string",
            "description": "The fee in the settlement asset’s smallest unit — the same unit as `settlement.amount`. Reported atomically rather than in cents because cents are ceiling-rounded and cannot express a sub-cent fee.",
            "example": "0"
          },
          "cents": {
            "type": "string",
            "description": "Cents this fee added to the delegation-cap reserve, i.e. `capChargedCents - settlement.approxCents`.",
            "example": "0"
          },
          "capChargedCents": {
            "type": "string",
            "description": "The delegation-cap debit for this payment (merchant leg + routing fee), rounded UP to a whole cent. The cap itself accrues at sub-cent resolution, so the exact debit is `capChargedMicros`; this figure is the per-call price guard that `maxTotalCents` is compared against.",
            "example": "1"
          },
          "capChargedMicros": {
            "type": "string",
            "description": "The exact delegation-cap debit for this payment (merchant leg + routing fee), in micros — millionths of the currency unit, i.e. 1/10,000 of a cent. It is a ceiling of the true price at that resolution, never below it. It is the debit AT MINT: if the routing fee or the merchant leg is later found uncollected, that part is given back to the cap while this figure stays as minted.",
            "example": "2040"
          }
        },
        "required": [
          "bps",
          "amount",
          "cents",
          "capChargedCents",
          "capChargedMicros"
        ]
      },
      "MintPaymentResponseDto": {
        "type": "object",
        "properties": {
          "paymentId": {
            "type": "string",
            "description": "Router payment record id (use it with `POST /router/payments/{id}/settled`).",
            "example": "b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13"
          },
          "protocol": {
            "type": "string",
            "description": "Payment protocol used.",
            "enum": [
              "x402",
              "mpp"
            ],
            "example": "x402"
          },
          "x402Version": {
            "type": "number",
            "description": "Negotiated x402 version (1 or 2).",
            "example": 2
          },
          "credential": {
            "description": "The credential to attach to your upstream request.",
            "allOf": [
              {
                "$ref": "#/components/schemas/PaymentCredentialDto"
              }
            ]
          },
          "settlement": {
            "description": "What this credential pays.",
            "allOf": [
              {
                "$ref": "#/components/schemas/SettlementDescriptorDto"
              }
            ]
          },
          "fee": {
            "description": "Nevermined’s routing fee charged on top of the merchant leg. The credential itself pays the merchant only — the fee never rides the merchant’s authorization.",
            "allOf": [
              {
                "$ref": "#/components/schemas/RouterFeeDto"
              }
            ]
          },
          "delegationId": {
            "type": "string",
            "description": "The delegation this spend was reserved against.",
            "example": "5e7481c3-..."
          },
          "resourceUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The upstream resource URL recorded with the payment."
          },
          "requestId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your idempotency id, echoed back."
          },
          "status": {
            "type": "string",
            "description": "Record status at mint time.",
            "enum": [
              "Issued"
            ],
            "example": "Issued"
          }
        },
        "required": [
          "paymentId",
          "protocol",
          "credential",
          "settlement",
          "fee",
          "delegationId",
          "status"
        ]
      },
      "RouteResultPaymentDto": {
        "type": "object",
        "properties": {
          "resultUrl": {
            "description": "Where this paid result stays readable by you for 24 hours (GET it with the same API key), in case the response is lost on the way back to you. Relative to the API host. API 1.48 and later.",
            "type": "string",
            "example": "/api/v1/router/payments/b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13/result"
          },
          "paymentId": {
            "type": "string",
            "example": "b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13"
          },
          "settlement": {
            "$ref": "#/components/schemas/SettlementDescriptorDto"
          },
          "fee": {
            "description": "Nevermined’s routing fee for this payment. Mode B charges it exactly as mode A does, so it is disclosed here too — `settlement.approxCents` is only the merchant leg, and `fee.capChargedCents` is what the delegation cap was actually debited.",
            "allOf": [
              {
                "$ref": "#/components/schemas/RouterFeeDto"
              }
            ]
          },
          "txHash": {
            "type": [
              "string",
              "null"
            ],
            "description": "On-chain settlement tx, if the upstream reported one."
          },
          "status": {
            "type": "string",
            "enum": [
              "Issued",
              "Settled"
            ],
            "example": "Settled"
          }
        },
        "required": [
          "paymentId",
          "settlement",
          "fee",
          "status"
        ]
      },
      "RouteResultDto": {
        "type": "object",
        "properties": {
          "status": {
            "type": "number",
            "description": "The upstream HTTP status code that was relayed.",
            "example": 200
          },
          "body": {
            "description": "The upstream response body (parsed JSON when the upstream returned JSON, else a string).",
            "example": {
              "ok": true,
              "data": "the paid resource"
            }
          },
          "paid": {
            "type": "boolean",
            "description": "Whether a payment was made (false = the upstream was free).",
            "example": true
          },
          "payment": {
            "description": "The payment made, when `paid` is true.",
            "allOf": [
              {
                "$ref": "#/components/schemas/RouteResultPaymentDto"
              }
            ]
          }
        },
        "required": [
          "status",
          "body",
          "paid"
        ]
      },
      "RoutePendingDto": {
        "type": "object",
        "description": "`202` answer of the `/route` family when the paid call outlives the 45 s sync budget: the payment was made, the call is still running on the Router, and its result will be readable at `resultUrl`.",
        "properties": {
          "paymentId": {
            "type": "string",
            "example": "b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13"
          },
          "resultUrl": {
            "type": "string",
            "description": "Read the result here with the same API key. Relative to the API host.",
            "example": "/api/v1/router/payments/b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13/result"
          },
          "status": {
            "type": "string",
            "enum": [
              "Pending"
            ],
            "example": "Pending"
          }
        },
        "required": [
          "paymentId",
          "resultUrl",
          "status"
        ]
      },
      "RouterPaymentResultPaymentDto": {
        "type": "object",
        "properties": {
          "paymentId": {
            "type": "string",
            "example": "b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13"
          },
          "status": {
            "type": "string",
            "enum": [
              "Issued",
              "Settled",
              "Failed"
            ],
            "example": "Settled"
          },
          "txHash": {
            "type": [
              "string",
              "null"
            ],
            "description": "The settlement reference, once the merchant reported one."
          }
        },
        "required": [
          "paymentId",
          "status",
          "txHash"
        ]
      },
      "RouterPaymentResultDto": {
        "type": "object",
        "description": "A paid Router result retained for its payer for 24 hours.",
        "properties": {
          "paymentId": {
            "type": "string",
            "example": "b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13"
          },
          "state": {
            "type": "string",
            "enum": [
              "Pending",
              "Ready",
              "Failed"
            ],
            "example": "Ready"
          },
          "resultUrl": {
            "type": "string",
            "example": "/api/v1/router/payments/b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13/result"
          },
          "expiresAt": {
            "type": "string",
            "description": "When the result is deleted. Until then only you can read it.",
            "example": "2026-09-26T10:12:00.000Z"
          },
          "status": {
            "type": [
              "number",
              "null"
            ],
            "description": "The upstream HTTP status of the paid call. `Ready` only.",
            "example": 200
          },
          "body": {
            "description": "The relayed response body, exactly as the original call would have returned it — a cataloged service's non-2xx body is withheld (null). Its form is named by `bodyEncoding`. `Ready` only.",
            "example": {
              "ok": true
            }
          },
          "bodyEncoding": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "json",
              "text",
              "base64",
              null
            ],
            "description": "`json` — the parsed JSON value; `text` — a UTF-8 string; `base64` — binary, base64-encoded. Null when there is no body."
          },
          "contentType": {
            "type": [
              "string",
              "null"
            ],
            "description": "The upstream content type, for a streamed call (`/router/proxy`, `/router/svc`)."
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "The response headers the streamed call would have relayed. Empty for a `/router/route` result."
          },
          "failureReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why a `Failed` result has no response."
          },
          "payment": {
            "$ref": "#/components/schemas/RouterPaymentResultPaymentDto"
          }
        },
        "required": [
          "paymentId",
          "state",
          "resultUrl",
          "expiresAt",
          "payment"
        ]
      },
      "QuoteResultDto": {
        "type": "object",
        "properties": {
          "paymentRequired": {
            "type": "boolean",
            "description": "Whether the upstream asked for payment (answered 402). `false` means the call is free, or the upstream answered with an error before any payment challenge: read `upstreamStatus`, and expect every priced field below to be absent.",
            "example": true
          },
          "upstreamStatus": {
            "type": "number",
            "description": "The status the upstream answered the unpaid probe with — 402 when `paymentRequired` is true. The upstream body and headers are never returned.",
            "example": 402
          },
          "optionSet": {
            "type": "string",
            "description": "Which set of payment options was priced. `delegation`: the options the supplied `delegationId` would select. `deployment`: no delegation was supplied, so the options a personal crypto (erc4337) delegation would select on this deployment. Present when `paymentRequired` is true.",
            "enum": [
              "delegation",
              "deployment"
            ],
            "example": "deployment"
          },
          "delegationId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The delegation whose options were priced, or null for the `deployment` option set. Present when `paymentRequired` is true.",
            "example": null
          },
          "protocol": {
            "type": "string",
            "description": "The payment protocol the Router would pay with — the same auto-detection as `/route` (MPP first, then x402). Present when `paymentRequired` is true.",
            "enum": [
              "x402",
              "mpp"
            ],
            "example": "x402"
          },
          "x402Version": {
            "type": "number",
            "description": "The x402 version that would be used, on the x402 rail.",
            "example": 2
          },
          "settlement": {
            "description": "What the payment would pay the merchant: the rail (`scheme`), `network`, `asset`, the merchant leg in atomic units (`amount`) and in cents (`approxCents`). The same descriptor `/route` returns as `payment.settlement`. Present when `paymentRequired` is true.",
            "allOf": [
              {
                "$ref": "#/components/schemas/SettlementDescriptorDto"
              }
            ]
          },
          "fee": {
            "description": "The Router fee and the fee-inclusive total the payment would reserve against the delegation cap — `capChargedMicros` exactly, `capChargedCents` rounded up to a whole cent, which is the figure `maxTotalCents` is compared against. The same object `/route` returns as `payment.fee`. Present when `paymentRequired` is true.",
            "allOf": [
              {
                "$ref": "#/components/schemas/RouterFeeDto"
              }
            ]
          }
        },
        "required": [
          "paymentRequired",
          "upstreamStatus"
        ]
      },
      "SettleResponseDto": {
        "type": "object",
        "properties": {
          "paymentId": {
            "type": "string",
            "example": "b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13"
          },
          "status": {
            "type": "string",
            "enum": [
              "Settled"
            ],
            "example": "Settled"
          },
          "txHash": {
            "type": "string",
            "example": "0xfc8af37baae588b5ee4a5e9212532b3d46702ac009b64c17e8ff810fd90ac493"
          }
        },
        "required": [
          "paymentId",
          "status",
          "txHash"
        ]
      },
      "RouterPaymentLookupDto": {
        "type": "object",
        "description": "The answer to `GET /router/payments?requestId=…`: an exact, caller-scoped lookup.",
        "properties": {
          "presence": {
            "type": "string",
            "enum": [
              "present",
              "absent"
            ],
            "description": "`present` when you own the matching payment; `absent` when you have no payment recorded with that `requestId` yet — a call still in flight may record one later, so retry it with the same `requestId`, never a new one. A failed read is an error response, never `absent`.",
            "example": "present"
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RouterPaymentRecordDto"
            },
            "description": "Exactly one payment when `presence` is `present`; empty when it is `absent`.",
            "maxItems": 1
          }
        },
        "required": [
          "presence",
          "payments"
        ]
      },
      "RouterPaymentRecordDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "b1f9c2e4-3d5a-4c7e-9f21-6a8b0c4d2e13"
          },
          "createdAt": {
            "type": "string",
            "example": "2026-07-01T09:00:55.605Z"
          },
          "status": {
            "type": "string",
            "enum": [
              "Issued",
              "Settled",
              "Failed"
            ],
            "example": "Settled"
          },
          "protocol": {
            "type": "string",
            "example": "x402"
          },
          "network": {
            "type": "string",
            "example": "base-sepolia"
          },
          "asset": {
            "type": [
              "string",
              "null"
            ],
            "example": "USDC",
            "description": "The settlement asset AS PERSISTED AT MINT, which differs per rail: a symbol for x402 (`USDC`/`EURC`), the ERC-20 contract ADDRESS from the challenge for MPP-tempo, and an ISO currency code for MPP-stripe. Prefer `assetSymbol`/`assetDecimals` below for display — they normalise all three. Kept as-is for consumers that already parse it."
          },
          "assetSymbol": {
            "type": [
              "string",
              "null"
            ],
            "description": "Ticker to display for `asset`, resolved from the asset address on this row’s chain — `USDC`, `EURC`, `USDC.e`, `pathUSD`. `null` when the asset is not one we recognise there; render the (truncated) `asset` instead, never a guess.",
            "example": "USDC.e"
          },
          "assetDecimals": {
            "type": [
              "number",
              "null"
            ],
            "description": "Decimal scale of `amount` for this asset, so a client never has to assume one. `null` when the asset is unrecognised — treat `amount` as raw atomic units rather than defaulting the scale, which would render a wrong NUMBER rather than a wrong label.",
            "example": 6
          },
          "amount": {
            "type": "string",
            "description": "Amount in the asset’s smallest unit.",
            "example": "1000"
          },
          "merchantAddress": {
            "type": "string",
            "description": "The merchant's pay-to identifier: a 0x address for the crypto rails (x402 / MPP-tempo), or the seller's Stripe network business profile id (profile_…) for the MPP-stripe / SPT card rail.",
            "example": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C"
          },
          "txHash": {
            "type": [
              "string",
              "null"
            ],
            "example": "0xfc8af37b..."
          },
          "delegationId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The delegation this payment was reserved against. Null on a payment funded by a Nevermined Subscription, which spends subscription credits and no delegation.",
            "example": "5e7481c3-..."
          },
          "deliveryStatus": {
            "type": "string",
            "enum": [
              "delivered",
              "charged_not_delivered",
              "charged_unconfirmed",
              "not_charged",
              "pending"
            ],
            "description": "Buyer-visible outcome: delivered when the request settled; charged_not_delivered when it failed and the merchant charge was observed; charged_unconfirmed when the charge was observed but delivery is unconfirmed; not_charged when reconciliation proved the merchant did not charge; pending when the charge outcome is unknown. On SPT and card rails, pending is not proof of no charge.",
            "example": "delivered"
          },
          "feeFailureReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the routing fee did not collect, or — on a `feeStatus: None` row — `fee-not-quoted: <why>` when a fee rate is configured but none was quoted for this payment. Diagnostic text for a human, NOT a stable contract: branch on `feeStatus`, never on this string. Whitespace is collapsed and any embedded `scheme://…` URL is replaced with `[url]` before publication. `null` when no fee rate is configured, and while a quoted fee is still on its happy path; a non-null value does not on its own mean the fee went uncollected — `feeStatus` says what happened.",
            "example": "HTTP 400: {\"errorReason\":\"amount_too_low\",\"network\":\"eip155:84532\",\"success\":false}"
          },
          "merchantSettlementObservedAt": {
            "type": [
              "string",
              "null"
            ],
            "description": "When on-chain reconciliation OBSERVED that the merchant redeemed this payment’s authorization — the money really left your wallet. `null` on the ordinary path (a cleanly settled payment is anchored by `txHash`), and always on the MPP rails, which carry no EIP-3009 authorization. Read it together with `status`: `status` is what you GOT, this is what you PAID — `status: Failed`/`Issued` with this set means the merchant took the money and delivered nothing. A non-null `txHash` on such a row does not mean the payment succeeded.",
            "example": "2026-09-08T10:12:00.000Z"
          },
          "paymentId": {
            "type": "string",
            "description": "Alias of `id`, named explicitly for requestId reconciliation clients.",
            "example": "b1f9c2e4-8a3d-4f27-9c6b-5d2e17a04f8b"
          },
          "chargedMicros": {
            "type": [
              "string",
              "null"
            ],
            "description": "Exact amount this payment still contributes to the delegation cap, in micros (1/10,000 of a cent). A merchant or fee share that was proven uncollected and released is excluded. `null` on legacy rows that predate exact micro-reserve columns.",
            "example": "1020000"
          },
          "resultUrl": {
            "type": "string",
            "description": "Relative URL of the retained paid result. It may answer Pending while the call is running, or 404 after retention expires, for a mode-A payment, for an unknown payment, or for a payment owned by another caller. A 404 is not delivery evidence; use `deliveryStatus`.",
            "example": "/api/v1/router/payments/b1f9c2e4-8a3d-4f27-9c6b-5d2e17a04f8b/result"
          },
          "fundingSource": {
            "type": "string",
            "enum": [
              "delegation",
              "subscription"
            ],
            "description": "What paid for this payment: `delegation` (your delegation cap) or `subscription` (your Nevermined Subscription credits). On a `subscription` row Nevermined paid the merchant: `delegationId` is null, `buyer` is Nevermined's treasury, the fee fields are zero (`feeStatus` `None`) and `chargedMicros` is `\"0\"`; what it cost you is `creditsCharged` / `creditsStatus`.",
            "example": "delegation"
          },
          "creditsCharged": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The rate, in credits, of a subscription-funded payment — kept on the row when its credits are refunded, so it counts against your allowance only when `creditsStatus` is `Committed`. Null on a delegation payment.",
            "example": null
          },
          "creditsStatus": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "Reserved",
              "Committed",
              "Refunded",
              null
            ],
            "description": "Where a subscription-funded payment’s credits stand: `Committed` (delivered), `Reserved` (still running, undelivered and marked for refund, or still being decided) or `Refunded`. Null on a delegation payment.",
            "example": null
          },
          "requestId": {
            "type": [
              "string",
              "null"
            ]
          },
          "resourceUrl": {
            "type": [
              "string",
              "null"
            ],
            "example": "https://agent.example/resource"
          },
          "buyer": {
            "type": "string",
            "description": "The buyer identity that funded the payment: the on-chain payer EOA (0x…) for the crypto rails (x402 / MPP-tempo), or the Stripe customer id (cus_…) for the MPP-stripe / SPT card rail.",
            "example": "0x8D6A5233..."
          },
          "feeAtomic": {
            "type": [
              "string",
              "null"
            ],
            "description": "Nevermined’s routing fee for this payment, in the settlement asset’s smallest unit. `0` when no fee applied; `null` on rows that predate the fee.",
            "example": "0"
          },
          "feeBps": {
            "type": [
              "number",
              "null"
            ],
            "description": "Fee rate applied, in basis points over 10,000. `null` on rows that predate the fee.",
            "example": 0
          },
          "feeCents": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cents the fee added to the delegation-cap reserve. Note `amount` on this record is the MERCHANT leg in atomic units, so the combined cap charge is not derivable from this resource alone — it is recorded as `amountCents` on `GET /delegation/{delegationId}/transactions`, where that same row breaks it out as `feeCents` / `feeAtomic` / `feeBps`, with `feeReleased` marking a fee already refunded to the cap.",
            "example": "0"
          },
          "feeStatus": {
            "type": [
              "string",
              "null"
            ],
            "description": "Fee lifecycle, independent of the payment `status`.",
            "enum": [
              "None",
              "Accrued",
              "Submitted",
              "Settled",
              "Failed",
              "Released",
              null
            ],
            "example": "None"
          },
          "feeTxHash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Settlement reference for the FEE leg — distinct from `txHash`, which is the MERCHANT leg’s. The two legs are independent movements that settle separately, so reconcile them separately. Reported verbatim by the facilitator (third-party text, not validated to a `0x` shape). `null` until the fee actually settles.",
            "example": "0x9c4d6f1e..."
          },
          "feeNonce": {
            "type": [
              "string",
              "null"
            ],
            "description": "EIP-3009 nonce of the fee leg, recorded when the leg is submitted. This is the only key that ties an on-chain transfer back to this payment: `authorizationState(buyer, feeNonce)` answers definitively whether the leg was consumed, which is how a `Submitted` fee is resolved. `null` until the leg is submitted. Not spendable on its own — the signed authorization is deliberately never stored.",
            "example": "0x7b1e2c..."
          }
        },
        "required": [
          "id",
          "createdAt",
          "status",
          "protocol",
          "network",
          "amount",
          "merchantAddress",
          "delegationId",
          "buyer",
          "deliveryStatus",
          "feeFailureReason",
          "merchantSettlementObservedAt",
          "paymentId",
          "chargedMicros",
          "resultUrl",
          "fundingSource",
          "creditsCharged",
          "creditsStatus"
        ]
      },
      "PaymentsSummaryPointDto": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "description": "Start of the time bucket (ISO-8601, UTC), truncated to the requested granularity.",
            "example": "2026-07-01T00:00:00.000Z"
          },
          "value": {
            "type": "number",
            "description": "Number of payment requests issued in the bucket.",
            "example": 12
          }
        },
        "required": [
          "date",
          "value"
        ]
      },
      "PaymentsSummaryDto": {
        "type": "object",
        "properties": {
          "total": {
            "type": "number",
            "description": "Total payment requests in the period. Uncapped, unlike the (1000-row-capped) list endpoint.",
            "example": 137
          },
          "series": {
            "description": "Per-bucket counts, oldest first.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentsSummaryPointDto"
            }
          }
        },
        "required": [
          "total",
          "series"
        ]
      },
      "SubscriptionQuoteRequestDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Catalog slug of the service to call. Subscription credits pay catalog services only, addressed by slug; the Router resolves it to the merchant server-side.",
            "example": "webscrape-pro",
            "pattern": "^[A-Za-z0-9._~-]+$"
          },
          "path": {
            "type": "string",
            "maxLength": 2048,
            "description": "Subpath of the endpoint to price, as on the route (path segments only).",
            "example": "scrape"
          },
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE"
            ],
            "default": "GET",
            "description": "HTTP method of the call to price."
          }
        },
        "required": [
          "slug"
        ]
      },
      "SubscriptionQuoteResultDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "The catalog slug priced.",
            "example": "webscrape-pro"
          },
          "credits": {
            "type": "integer",
            "description": "Credits one call to this endpoint costs: its current rate on Nevermined's rate card, whatever the merchant charges at the moment of the call.",
            "example": 3
          },
          "tariffVersion": {
            "type": "integer",
            "description": "The tariff version that sets `credits`.",
            "example": 2
          }
        },
        "required": [
          "slug",
          "credits",
          "tariffVersion"
        ]
      },
      "SubscriptionRouteRequestDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Catalog slug of the service to call. Subscription credits pay catalog services only, addressed by slug; the Router resolves it to the merchant server-side.",
            "example": "webscrape-pro",
            "pattern": "^[A-Za-z0-9._~-]+$"
          },
          "path": {
            "type": "string",
            "maxLength": 2048,
            "description": "Subpath of the endpoint, appended to the resolved merchant URL (path segments only — `//`, `..`, a backslash or an embedded absolute URL is refused).",
            "example": "scrape"
          },
          "search": {
            "type": "string",
            "maxLength": 2048,
            "description": "Query string without the leading `?`, attached to the resolved merchant URL.",
            "example": "q=nevermined&limit=1"
          },
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE"
            ],
            "default": "GET",
            "description": "HTTP method to use for the upstream request."
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Headers to forward to the merchant (e.g. its own auth). Optional."
          },
          "body": {
            "description": "Request body to forward (JSON). Optional."
          },
          "credentialHeader": {
            "type": "string",
            "maxLength": 64,
            "description": "Header the payment credential rides in on the paid hop, when the merchant needs its own `Authorization` too (a separate `Payment` header is the common one). Do not set it by default.",
            "example": "Payment"
          },
          "protocol": {
            "type": "string",
            "enum": [
              "x402",
              "mpp"
            ],
            "default": "x402",
            "description": "Advisory: the Router detects the protocol from the merchant's 402."
          },
          "requestId": {
            "type": "string",
            "description": "Idempotency key — REQUIRED. At most one payment per (caller, requestId): a retry is never paid twice.",
            "maxLength": 256,
            "pattern": "\\S",
            "example": "scrape-example-com-1"
          }
        },
        "required": [
          "slug",
          "requestId"
        ]
      },
      "SubscriptionRoutePaymentDto": {
        "type": "object",
        "properties": {
          "paymentId": {
            "type": "string",
            "description": "The Router payment id."
          },
          "status": {
            "type": "string",
            "enum": [
              "Issued",
              "Settled",
              "Failed"
            ],
            "description": "The merchant leg, which Nevermined paid on your behalf."
          },
          "txHash": {
            "type": [
              "string",
              "null"
            ],
            "description": "On-chain settlement reference of the merchant leg, when the merchant returned one."
          },
          "creditsCharged": {
            "type": "integer",
            "description": "Credits this call cost (the endpoint's rate). Absent only when the ledger could not be read at answer time; `GET /router/payments` has it.",
            "example": 3
          },
          "creditsStatus": {
            "type": "string",
            "enum": [
              "Reserved",
              "Committed",
              "Refunded"
            ],
            "description": "`Committed` — the merchant delivered (2xx) and the credits stay spent. `Reserved` — the call did not deliver and its credits are marked for refund, or its outcome is still being decided. `Refunded` — returned. Absent only when the ledger could not be read at answer time."
          },
          "failureReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the call did not complete cleanly, when it did not."
          },
          "correlationId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The request correlation id."
          },
          "resultUrl": {
            "type": "string",
            "description": "Where this result stays readable for 24 h."
          }
        },
        "required": [
          "paymentId",
          "status",
          "txHash",
          "failureReason",
          "correlationId"
        ]
      },
      "SubscriptionRouteResultDto": {
        "type": "object",
        "properties": {
          "status": {
            "type": "number",
            "description": "The merchant's HTTP status."
          },
          "body": {
            "description": "The merchant's response body — `null` unless a paid call delivered (2xx), so a failed call reveals nothing about the merchant. A free answer relays only its `status` (`paid: false`), except a declared follow-up poll after a paid call."
          },
          "bodyEncoding": {
            "type": "string",
            "enum": [
              "json",
              "text",
              "base64"
            ],
            "description": "How `body` is represented. Absent when `body` is withheld."
          },
          "contentType": {
            "type": [
              "string",
              "null"
            ],
            "description": "The merchant's response media type, when `body` is relayed.",
            "example": "application/json"
          },
          "paid": {
            "type": "boolean",
            "description": "Whether a payment was made (false when the service answered free)."
          },
          "payment": {
            "$ref": "#/components/schemas/SubscriptionRoutePaymentDto"
          }
        },
        "required": [
          "status",
          "body",
          "paid"
        ]
      },
      "SubscriptionBalancePlanDto": {
        "type": "object",
        "properties": {
          "catalogId": {
            "type": "string",
            "format": "uuid",
            "description": "The subscription price subscribed to."
          },
          "planKey": {
            "type": "string",
            "description": "The plan bought.",
            "example": "standard"
          },
          "creditsPerCycle": {
            "type": "string",
            "description": "Credits each paid monthly invoice grants, as a whole-number digit string.",
            "example": "20000"
          }
        },
        "required": [
          "catalogId",
          "planKey",
          "creditsPerCycle"
        ]
      },
      "SubscriptionBalanceDto": {
        "type": "object",
        "properties": {
          "subscribed": {
            "type": "boolean",
            "description": "Whether the caller has a current Nevermined Subscription."
          },
          "creditsLeft": {
            "type": "string",
            "description": "Credits left: the unexpired remainder, as a whole-number digit string. What the Router debits.",
            "example": "1240"
          },
          "spendable": {
            "type": "boolean",
            "description": "Whether these credits can pay a call right now: the subscription is active or trialing and not suspended by a card dispute."
          },
          "resetsAt": {
            "type": [
              "string",
              "null"
            ],
            "description": "When this period's credits expire and — unless `cancelAtPeriodEnd` — the next period's are granted, on payment of its invoice (ISO 8601). `null` until the billing period is recorded.",
            "example": "2026-10-30T00:00:00.000Z"
          },
          "cancelAtPeriodEnd": {
            "type": "boolean",
            "description": "The subscription ends at `resetsAt` instead of renewing."
          },
          "status": {
            "type": "string",
            "enum": [
              "incomplete",
              "trialing",
              "active",
              "past_due",
              "unpaid",
              "paused"
            ],
            "description": "The subscription status. A `past_due` subscription keeps its credits but cannot spend them until its invoice is paid. An ended subscription answers `subscribed: false`.",
            "example": "active"
          },
          "suspended": {
            "type": "boolean",
            "description": "True while a card dispute is open, or after one was decided in the cardholder's favour: the credits cannot be spent. A dispute decided in Nevermined's favour lifts it, once no other dispute is open or was decided in the cardholder's favour."
          },
          "plan": {
            "$ref": "#/components/schemas/SubscriptionBalancePlanDto"
          }
        },
        "required": [
          "subscribed"
        ]
      },
      "RouterErrorResponseDto": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Router error code. `BCK.ROUTER.0001` invalid request / no fundable option / bad 402 (400); `BCK.ROUTER.0002` requestId already used — idempotency conflict (409); `BCK.ROUTER.0003` delegation budget exceeded, expired, or inactive (402); `BCK.ROUTER.0004` payment record not found (404); `BCK.ROUTER.0005` payment not in a settleable state (409). Framework-level `BCK.HTTP.*` codes (e.g. `BCK.HTTP.400` request validation, `BCK.HTTP.429` throttling) may also appear — see the base `code` field.",
            "example": "BCK.ROUTER.0003"
          },
          "httpStatus": {
            "type": "number",
            "description": "HTTP status code",
            "example": 500
          },
          "message": {
            "type": "string",
            "description": "Error message for Router operations.",
            "example": "delegation budget exceeded, expired, or inactive",
            "examples": [
              "delegationId is required — create one via POST /api/v1/delegation/create",
              "no fundable option; offered: [...]",
              "upstream pay-to address is not in this delegation's allowed recipients",
              "delegation budget exceeded, expired, or inactive",
              "payment already settled with a different txHash"
            ]
          },
          "details": {
            "type": "string",
            "description": "Throw-site detail string supplementing the canonical message. Only set when the call site supplied an opts.message; absent otherwise.",
            "example": "Hash JWT is not signed by the node account"
          },
          "error": {
            "type": "string",
            "description": "RFC 6749 §5.2 / RFC 8628 §3.5 top-level OAuth error string. Present only on the OAuth device-polling responses from `POST /oauth/token` (`authorization_pending`, `slow_down`, `expired_token`, `access_denied`), so a standards-compliant OAuth/device client can branch on it directly instead of on the Nevermined `code`. Absent on every other error.",
            "example": "authorization_pending"
          },
          "hint": {
            "type": "string",
            "description": "Actionable remediation hint, sourced from the catalogue or supplied at the throw site. Tells the caller what to fix or where to look.",
            "example": "Verify the wallet has sufficient balance and that the plan is active."
          },
          "docsUrl": {
            "type": "string",
            "description": "Permalink to the docs page describing this error code.",
            "example": "https://nevermined.ai/docs/development-guide/api-errors/codes#bck-x402-0008"
          },
          "category": {
            "type": "string",
            "description": "Coarse classification of the failure, useful for client-side branching without parsing the message.",
            "enum": [
              "validation",
              "auth",
              "business",
              "integration",
              "internal"
            ],
            "example": "integration"
          },
          "retryable": {
            "type": "boolean",
            "description": "Whether the failure is transient and the same call can be retried with identical inputs. Absent when the catalogue does not assert either way.",
            "example": true
          },
          "correlationId": {
            "type": "string",
            "description": "Request-scoped correlation id stamped by the global filter. Echoed in the x-correlation-id response header. Quote this when reporting issues.",
            "example": "a3f6b1c4-7d2e-4a9b-8e0c-12f4d8e6c5b9"
          },
          "uuid": {
            "type": "string",
            "description": "Per-error UUID generated at construction. Useful for log correlation.",
            "example": "e-550e8400-e29b-41d4-a716-446655440000"
          },
          "date": {
            "type": "string",
            "description": "ISO timestamp at which the error was constructed.",
            "example": "2026-05-09T12:34:56.789Z",
            "format": "date-time"
          },
          "params": {
            "type": "string",
            "description": "JSON-stringified contextual parameters supplied at the throw site (e.g. IDs, state). Always a string in the wire format — parse with JSON.parse if needed.",
            "example": "{\"planId\":\"43298432984329\",\"reason\":\"Invalid configuration\"}"
          }
        },
        "required": [
          "code",
          "httpStatus",
          "message"
        ]
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your Nevermined API Key (starts with 'nvm:'). Get one at [nevermined.app](https://nevermined.app) under **Settings > API Keys**."
      }
    }
  }
}
