Browse documentation

Per-Request Options

Control fallback models, guardrails, and response caching on a single nRouter call with the nrouter_fallbacks, nrouter_guardrails, and nrouter_cache body fields.

Last updated

Most of what nRouter does for a request is decided once, in the dashboard, and then applies to every call your keys make. Per-request options are the exception: three JSON fields you put in the request body to change routing, guardrails, or caching for that one call, without touching your organization's configuration and without a redeploy.

TL;DR

Add nrouter_fallbacks, nrouter_guardrails, or nrouter_cache to the JSON body of a call to /v1/chat/completions, /v1/completions, /v1/responses, or /v1/messages. They are nRouter control fields: the Rust gateway reads them and strips them, so no provider ever sees them. The response tells you what happened on x-nr-routing, x-nr-attempts, and x-nr-response-cache.

The three fields

FieldTypeLimitWhat it does
nrouter_fallbacksarray of model names1–4 entriesNames the models to try if the primary cannot be reached. For this call the list replaces whatever fallback policy your organization has for the alias in model.
nrouter_guardrailsarray of guardrail IDs or names1–8 entriesAdds pre-call guardrails your organization owns to this call's chain.
nrouter_cachebooleanfalse skips the response cache for this call, in both directions.

Anything else beginning with nrouter_ is refused with 400 rather than forwarded, so a typo surfaces as an error instead of reaching a provider as an unknown field.

Coming soon. nrouter_cache is live on api.nrouter.ai today. nrouter_fallbacks and nrouter_guardrails are documented here as the shipped wire contract and become available on api.nrouter.ai with the next gateway release; until then a request carrying either field is refused. The response headers x-nr-routing and x-nr-attempts described below are already emitted.

Fallback models for one call

Send the models you want tried, in order, if the primary is refused admission:

{
  "model": "gpt-5.4-mini",
  "messages": [{ "role": "user", "content": "Summarize this incident report." }],
  "nrouter_fallbacks": ["claude-sonnet-4-5", "gemini-2.5-pro"]
}

What that contract is, precisely:

  • The primary stays model. nrouter_fallbacks never changes which model is tried first; it only says what comes after it.
  • It replaces, it does not append. For this call the list you send is the fallback order, in place of whatever fallback policy your organization has for the alias you named. Your stored configuration is untouched and applies again on the next call that names none.
  • A Smart Router alias stays authoritative. If model names a Smart Router alias, its saved deployment chain is what runs, and a request that also carries nrouter_fallbacks is refused with 400 fallback_not_allowed rather than served off a chain you did not ask for. Same for nrouter/auto and the other allowance models, whose plan is the platform's. Per-request lists over a stored chain are a v1 limitation: the refusal is deliberate, because silently ignoring the list is the one outcome this field exists to prevent.
  • At most four entries, trimmed and de-duplicated, and none of them equal to model.
  • Every target must be one your key can already route to. A model your key is not permitted to call, or one the catalog no longer serves, is refused with 400 fallback_not_allowed naming the target. It is never quietly dropped — you asked for it by name, so you get an answer about it.
  • Failover is narrow on purpose. A later entry is attempted only on an upstream admission refusal (429, 503, Anthropic 529) or a connect-phase transport failure. Other statuses and any failure after a connection was established are returned as they are, because the provider may already have generated billable work.
  • One request, one bill, one slot. A call that walks its fallback list still takes a single credit reservation, a single rate-limit slot, and at most three provider calls in total. Naming four fallbacks plus the primary is legal; the tail is simply unreachable inside that budget.

Which entry answered

The response says so, so you never have to infer it from latency:

x-nr-routingMeaning
directThe entry at chain index 0 — the first one — answered.
fallback:1The entry at chain index 1 answered: the second model, and the first fallback.
fallback:2The entry at chain index 2 answered — the third model — and so on.

The number after fallback: is the 0-based chain index of the entry that answered, so fallback:1 is the second model and the first fallback. x-nr-attempts reports how many provider calls the request actually made, counting retries and failovers alike, and is always at least 1.

Both headers are absent on a cache hit and on a refusal, because in neither case did a chain entry answer a provider call. Absence is a fact about the request, not a missing value.

Guardrails for one call

Name guardrails your organization already owns, by ID or by name, to run on this call in addition to whatever your key, team, and organization configuration resolves to:

{
  "model": "gpt-5.4-mini",
  "messages": [{ "role": "user", "content": "Draft a reply to this customer." }],
  "nrouter_guardrails": ["pii-strict", "3f2a8c14-9b7e-4d21-8f66-1a2b3c4d5e6f"]
}
  • Add-only, always. A request can add protection. It can never remove a guardrail your organization or team configured, and it can never remove or weaken the platform safety floor that runs on every request. There is no per-request way to turn a rule off — that is the point of the field being add-only.
  • Your organization's guardrails only, and pre-call ones in this version. A guardrail belonging to another organization is not addressable, and an ID that does not exist gets the same answer as one you cannot reach: 400 guardrail_not_found, with a byte-identical body. The refusal deliberately tells you nothing about whether something exists elsewhere.
  • At most eight entries.
  • If your organization has guardrails switched off, a request cannot switch them back on for itself — the same guardrail_not_found refusal applies. A call may add to a plane you left on; it may not re-open one you turned off.
  • Added rules change the cache key, so a response produced under an added guardrail is never replayed to a call that did not ask for it.

If an added guardrail blocks the request, the answer is 400 with x-nr-guardrails: blocked, no provider call, and no spend. Guardrail concepts, actions, and scopes are in Guardrails.

Caching for one call

{
  "model": "gpt-5.5",
  "messages": [{ "role": "user", "content": "Summarize this contract." }],
  "nrouter_cache": false
}

Caching is on by default. nrouter_cache: false means the call is neither answered from the cache nor written into it, and the response comes back with x-nr-response-cache: bypass so you can confirm it. The full mechanics — what a cache entry is, how it is keyed, and how long it lives — are in Router Settings › Response caching.

One consequence worth knowing if you combine the fields: the cache key is computed from the request after nRouter's own control fields are removed, so two calls that differ only in their fallback list share a cache entry. When the second one is served from the cache it carries x-nr-response-cache: hit and no x-nr-routing header at all. That is not your list being ignored — no provider call was made, so there is no answering entry to report. Send nrouter_cache: false when you want the routing headers on every call.

All three together

curl -X POST https://api.nrouter.ai/v1/chat/completions \
  -H "Authorization: Bearer $NROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.4-mini",
    "messages": [
      { "role": "user", "content": "Summarize this incident report in three bullets." }
    ],
    "nrouter_fallbacks": ["claude-sonnet-4-5", "gemini-2.5-pro"],
    "nrouter_guardrails": ["pii-strict"],
    "nrouter_cache": false
  }'

A successful response to that call carries, among the usual telemetry:

x-nr-request-id: req_01JBQ7V5K2N8Y3RXM4WQ9ZTC6D
x-nr-model: claude-sonnet-4-5
x-nr-routing: fallback:1
x-nr-attempts: 2
x-nr-response-cache: bypass
x-nr-guardrails: pass
x-nr-request-cost: 0.00241
x-nr-cost-status: exact

Read together: the primary was refused admission, the first fallback answered on the second provider call, the cache was skipped because you asked it to be, and every guardrail in the chain — yours, your organization's, and the platform floor — passed.

The same three fields work from any of the nRouter SDKs. SDKs that model the request body strictly expose them through an extra_body-style escape hatch; see the snippet for your language.

When an option is refused

Every refusal carries a machine-readable code next to its message, so you can branch on it instead of matching prose:

{
  "error": {
    "type": "gateway_error",
    "message": "Fallback target 'gpt-4-turbo' is not routable by this key.",
    "code": "fallback_not_allowed"
  }
}
codeHTTPWhen
fallback_not_allowed400A nrouter_fallbacks target your key cannot route to, or a fallback list on a request shape that does not accept one.
guardrail_not_found400A nrouter_guardrails entry your organization does not own, or does not have enabled.
input_too_large400The prompt exceeds the model's declared input ceiling.
max_output_tokens_too_large400The requested output length exceeds the model's declared maximum.
guardrail_blocked400A guardrail in the resolved chain refused the request before it reached a provider.

A request refused at any of these points never reaches a provider and costs you nothing.

Next steps

  • Router Settings — Standing routing strategy, fallback chains, and the caching toggle
  • Guardrails — Guardrail types, actions, and the key/team/organization scopes
  • Observability & Logs — The full response-header reference and the request log
  • Chat Completions — The endpoint reference these fields attach to

FAQ

Which endpoints accept per-request options?

The four text wires: /v1/chat/completions, /v1/completions, /v1/responses, and /v1/messages. The fields go in the JSON request body of all four, as top-level keys alongside model and whatever that wire's prompt field is — messages on /v1/chat/completions and /v1/messages, input on /v1/responses, and prompt on /v1/completions:

{
  "model": "gpt-5.4-mini",
  "prompt": "Summarize this incident report.",
  "nrouter_cache": false
}

Will a provider ever see these fields?

No. nrouter_fallbacks, nrouter_guardrails, and nrouter_cache are nRouter control fields. The Rust gateway reads them and removes them from the body before the request is forwarded, so nothing beginning with nrouter_ reaches an upstream provider.

Does a fallback attempt cost me a second request's worth of credits or rate limit?

No. One request takes one credit reservation and one rate-limit slot regardless of how far it walks the chain, and you are billed for the model that actually answered — reported on x-nr-request-cost. The walk is capped at three provider calls in total.

Can I use nrouter_guardrails to turn a guardrail off for one call?

No, and that is deliberate. The field is add-only: it can add a guardrail your organization owns, and it can never remove one your organization or team configured or weaken the platform safety floor. Turning a rule off is an organization-level change made under Guardrails.

What does fallback:1 mean — the first fallback, or the second model?

Both — they are the same entry. The number is the 0-based chain index of the entry that answered, so direct means the entry at index 0 answered, fallback:1 means the entry at index 1 did (the second model, and the first fallback), and fallback:2 the third.

Why is there no x-nr-routing header on some of my responses?

Because no chain entry answered a provider call. That happens on a cache hit and on any refusal. x-nr-routing and x-nr-attempts are both absent in those cases rather than reporting a placeholder.

Why did two calls with different fallback lists return the same answer?

The response cache keys on the request after nRouter's control fields are stripped, so two calls differing only in their fallback list are the same cached request. The second one comes back with x-nr-response-cache: hit and no routing headers. Send nrouter_cache: false on calls where you want the provider hit and the routing headers every time.

What happens if I send a misspelled nrouter_ field?

It is refused with 400 rather than forwarded. The accepted set is fixed, so an unknown nrouter_* key is treated as a mistake worth telling you about instead of an extension worth passing on to a provider.

Was this page helpful?