> ## 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 from a Commerce Grant

> The OAuth counterpart of `POST /api/v1/router/quote`, for a credential minted from a
`commerce` authorization. Identical behaviour and identical response, with one difference:
**you do not choose the delegation.** The quote prices the options the delegation your grant
is pinned to would select, so it is what `POST /api/v1/router/commerce/route` would charge for
the same call. Sending a `delegationId` is refused, not ignored.

Nothing is signed, no credential is minted, no payment is recorded and no budget is reserved.
The Router still makes the unpaid request to the service, and the quote spends the same
per-key and per-service rate budgets as a payment. Compare `fee.capChargedCents` with what you
want to spend, then call `/commerce/route` with `maxTotalCents` set to that figure.

A plain (non-OAuth) API key cannot use this route: it has no grant to price. Use
`POST /api/v1/router/quote` and name your own `delegationId` instead.

<Note>
  **Rolling out.** This route ships in the first API release after 1.49. Until your environment runs it, the route answers `404`: bound the price with `maxTotalCents` on [`POST /router/commerce/route`](/docs/api-reference/router/commerce-route) instead.
</Note>


## OpenAPI

````yaml POST /router/commerce/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/commerce/quote:
    post:
      tags:
        - Router
      summary: Quote a Router call from your commerce grant, without paying (OAuth)
      description: >-
        The OAuth counterpart of `POST /api/v1/router/quote`, for a credential
        minted from a

        `commerce` authorization. Identical behaviour and identical response,
        with one difference:

        **you do not choose the delegation.** The quote prices the options the
        delegation your grant

        is pinned to would select, so it is what `POST
        /api/v1/router/commerce/route` would charge for

        the same call. Sending a `delegationId` is refused, not ignored.


        Nothing is signed, no credential is minted, no payment is recorded and
        no budget is reserved.

        The Router still makes the unpaid request to the service, and the quote
        spends the same

        per-key and per-service rate budgets as a payment. Compare
        `fee.capChargedCents` with what you

        want to spend, then call `/commerce/route` with `maxTotalCents` set to
        that figure.


        A plain (non-OAuth) API key cannot use this route: it has no grant to
        price. Use

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

````