Routing modes
Pinned, multi-offer fallback, and auto-routing with deadlines and provider prefs.
A logical model id (e.g. openai/gpt-5.2) can be served by several providers
(OpenRouter-style offers). ultrarouter resolves your model chain into an
ordered list of concrete provider offers and executes them with deadlines.
The router field
Set router on the request (or per call type):
off(default) /pinned— use the resolved model + its offers in order; no cross-model substitution.auto/candidates— auto-routing. The responseur.routingcarriesengine: "not_enabled"where auto-selection is not yet active for your workspace, so the contract is stable for SDKs while the eval-driven engine rolls out per workspace via canary. Auto-selection only ever adopts a model a passing eval verdict has cleared, then rolls it out behind an auto-rollback canary — a downgrade in autonomy (a per-requestroutercan only lower it, never raise it).
Fallbacks and offers
-
modelis the primary;models: [...]are ordered fallbacks. The chain is the outer loop; a model's provider offers are the inner loop. -
Offers are ordered by
provider.sort:price(default, per input-token cost) orthroughput. The suffixes:nitro(throughput) and:floor(price) set the sort per model. -
Provider preferences (
provider.order,ignore,quantizations,data_collection: "deny",allow_fallbacks: false) filter and order candidates. Every rejected candidate is recorded with a machine-readable reason. -
A capability guard skips any offer that lacks a capability your request needs (tools,
json_schema, vision, embeddings). In a multi-offer chain a capable sibling still serves the request; only the incapable offer is skipped (with a reason). -
Capability refusal, never a silent downgrade. If no offer for your requested model can honour a capability you explicitly asked for — most commonly a
response_format: json_schemapinned to a model that does not support structured output — the gateway refuses the request before dispatch with the GW-70capability_unsupportederror (HTTP 400, not retryable), naming the missing capability. It will never quietly downgrade yourjson_schematojson_objector drop it, and never substitute a different model — your output contract is honoured or the request is refused with the real reason. If a provider itself rejects an unsupportedresponse_formatat dispatch (a raw upstream 400), that failure is mapped onto the samecapability_unsupportedcode, so logs and clients see the real cause instead of a generic upstream error. -
Unpriced offers are never routed. An offer with no usable price — a model a live catalog sync discovered that nobody has priced yet, or a custom provider registered without
pricing— is removed before planning, because the gateway cannot compute what a call to it costs and would otherwise record it as$0. A sibling offer that is priced still serves the request, and the removed offer is listed underunpriced_refusedin the routing trail. If the model you asked for has no priced offer, the request is refused before dispatch with the GW-70model_unpricederror (HTTP 403, not retryable). It is never silently served by a fallback model. A price of0that you declared on a custom provider counts as a price. -
Retired and renamed provider models. The catalog is checked against each provider's live model list. When a provider stops listing a model, that offer stops being routed: a request that names it gets
not_foundif no other offer serves it, and a sibling offer still serves the model in a multi-offer chain. When a provider renames a model and the catalog records the new id, the offer moves to the new id at the same price, so the model stays routable under its logical id.
Deadlines
ur.timeout_ms is the total budget (default 30 s non-stream / 60 s stream, clamped
[1s, 300s]) and spans all attempts. Each attempt is capped at
min(remaining_total, attempt_cap). A retryable provider error advances to the
next offer, then the next model; a non-retryable error surfaces immediately.
Streaming may fall back only before the first content byte; once bytes flow, a
failure is a terminal event: error frame — never a silent model switch.
The routing trail
Every request records the full trail — mode, sort, each attempt (model, provider,
outcome, latency), the skipped candidates with reasons, whether a fallback was
used, and the per-field config sources. Fetch it after the fact with
GET /v1/generation.