> ## Documentation Index
> Fetch the complete documentation index at: https://nevermined.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Quote a Request Before Paying

> Ask what `POST /route` would charge for this call, WITHOUT paying it. The Router makes the
same unpaid probe `/route` makes, reads the 402, selects the payment option exactly as a
payment would (MPP first, then x402) and prices it with the Router fee — then stops. Nothing
is signed, no credential is minted, no payment is recorded and no delegation budget is
reserved, so a quote is free and can be repeated.

Use it to decide whether ONE call fits the allocation you planned for it: compare
`fee.capChargedMicros` (exact) or `fee.capChargedCents` (rounded up, the figure
`maxTotalCents` is compared against) with what you want to spend, then call `/route` with
`maxTotalCents` set to that figure so a price change between quote and call is refused rather
than paid.

`delegationId` is optional. With it, the quote prices the options that delegation would select
(an organization-wallet delegation pays only in its own currency, a card delegation pays over
MPP-stripe, a recipient allowlist is enforced); without it, the options a personal crypto
delegation would select. `optionSet` says which. A quote checks neither the remaining cap nor
the wallet balance — those are the payment’s checks (BCK.ROUTER.0003, BCK.ROUTER.0009).

Provide the target `url` OR a catalog `slug` (+ optional `path`, `search`, `method`, `headers`,
`body`), as on `/route`. A non-402 answer returns `paymentRequired: false` with the upstream
status only — the upstream body and headers are never returned.

<Note>
  A quote charges nothing, but it is not free of consequence: the Router sends your request to the service unpaid, so a service that doesn't charge for it performs it, and the quote spends the same per-key and per-service rate budgets as a payment. Quote once per decision; don't poll. Available from API 1.48.
</Note>


## OpenAPI

````yaml POST /router/quote
openapi: 3.1.0
info:
  title: Nevermined API
  description: >-
    API for managing AI agents, payment plans, and x402 payments in the
    Nevermined ecosystem.


    ## Prerequisites


    You need a Nevermined API Key to authenticate. Get one at
    [nevermined.app](https://nevermined.app) under **Settings > API Keys**.


    See 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:
  /router/quote:
    post:
      tags:
        - Router
      summary: >-
        Quote a Router call: the fee-inclusive price, rail and network, without
        paying
      description: >-
        Ask what `POST /route` would charge for this call, WITHOUT paying it.
        The Router makes the

        same unpaid probe `/route` makes, reads the 402, selects the payment
        option exactly as a

        payment would (MPP first, then x402) and prices it with the Router fee —
        then stops. Nothing

        is signed, no credential is minted, no payment is recorded and no
        delegation budget is

        reserved, so a quote is free and can be repeated.


        Use it to decide whether ONE call fits the allocation you planned for
        it: compare

        `fee.capChargedMicros` (exact) or `fee.capChargedCents` (rounded up, the
        figure

        `maxTotalCents` is compared against) with what you want to spend, then
        call `/route` with

        `maxTotalCents` set to that figure so a price change between quote and
        call is refused rather

        than paid.


        `delegationId` is optional. With it, the quote prices the options that
        delegation would select

        (an organization-wallet delegation pays only in its own currency, a card
        delegation pays over

        MPP-stripe, a recipient allowlist is enforced); without it, the options
        a personal crypto

        delegation would select. `optionSet` says which. A quote checks neither
        the remaining cap nor

        the wallet balance — those are the payment’s checks (BCK.ROUTER.0003,
        BCK.ROUTER.0009).


        Provide the target `url` OR a catalog `slug` (+ optional `path`,
        `search`, `method`, `headers`,

        `body`), as on `/route`. A non-402 answer returns `paymentRequired:
        false` with the upstream

        status 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) — 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'
components:
  schemas:
    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
    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
    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
    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
  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**.

````