---
title: "Quote a Router call from your commerce grant, without paying (OAuth)"
method: POST
path: "/router/commerce/quote"
tags: ["Router"]
---

# Quote a Router call from your commerce grant, without paying (OAuth)

`POST /router/commerce/quote`

## Request

- Base URL: `https://api.sandbox.nevermined.app/api/v1`
- URL: `https://api.sandbox.nevermined.app/api/v1/router/commerce/quote`
- Auth: HTTP bearer

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.

## Request body

- CommerceQuoteRequestDto
  - `url` string: 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.
  - `slug` string: 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`.
  - `path` string: 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.
  - `search` string: 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.
  - `method` 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE': HTTP method to use for the upstream request.
  - `headers` object: Headers to forward to the upstream (e.g. the agent’s own auth). Optional.
  - `body` unknown
  - `credentialHeader` string: 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.
  - `delegationId` string: 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.

## Response `200`

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.

- QuoteResultDto
  - `paymentRequired` boolean, required: 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.
  - `upstreamStatus` number, required: The status the upstream answered the unpaid probe with — 402 when `paymentRequired` is true. The upstream body and headers are never returned.
  - `optionSet` 'delegation' | 'deployment': 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.
  - `delegationId` string, nullable: The delegation whose options were priced, or null for the `deployment` option set. Present when `paymentRequired` is true.
  - `protocol` 'x402' | 'mpp': The payment protocol the Router would pay with — the same auto-detection as `/route` (MPP first, then x402). Present when `paymentRequired` is true.
  - `x402Version` number: The x402 version that would be used, on the x402 rail.
  - `settlement` SettlementDescriptorDto
    - `recipient` string, required: The on-chain recipient (the agent’s `payTo` address from its 402).
    - `amount` string, required: Amount in the asset’s smallest unit (USDC/EURC = 6 decimals).
    - `asset` string: For x402 the asset symbol (`USDC`/`EURC`); for mpp the tempo payment token contract address (mpp carries no on-wire symbol).
    - `network` string, required: Network the payment settles on (name or CAIP-2).
    - `approxCents` string, required: 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.
    - `scheme` 'exact' | 'charge' | 'spt': 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.
  - `fee` RouterFeeDto
    - `bps` number, required: Rate applied to this payment, in basis points over a 10,000 denominator (e.g. `200` = 2%). `0` means no routing fee was charged.
    - `amount` string, required: 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.
    - `cents` string, required: Cents this fee added to the delegation-cap reserve, i.e. `capChargedCents - settlement.approxCents`.
    - `capChargedCents` string, required: 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.
    - `capChargedMicros` string, required: 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.

## Other responses

- `400`: 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.
- `401`: 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.
- `403`: 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).
- `404`: 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`.
- `409`: The target is a cataloged Nevermined service that must be addressed by its slug, not by raw URL (BCK.ROUTER.0014).
- `413`: 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.
- `429`: 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.
- `500`: 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.
- `503`: BCK.ROUTER.0028 — the quote could not be computed because a read it depends on failed. Nothing was signed or charged; retry with backoff.

---

[API](https://skmtc.dev/nevermined/apis/nevermined-api.md) · [All operations](https://skmtc.dev/nevermined/apis/nevermined-api/llms.txt) · [OpenAPI document](https://skmtc.dev/nevermined/apis/nevermined-api/revisions/cabbe618c80b?raw)
