
# Routing

Every request goes through the router. For an explicit model the
router's job is small — resolve it to a healthy upstream that serves
it. For an auto-routed model it also chooses which provider and region
serve it. This page describes both.

## Three modes

- **Explicit model.** You send a full model ID (see
  [available models](available)); the router resolves it to the
  provider and region encoded in that ID and sends it upstream.
- **Auto-routed model (`auto/<creator>/<model>`).** You name the
  model; the router picks which provider serves it and from where.
  See [auto-routing a model](#auto-routing-a-model) below.
- **Alias (`alias/<name>`).** You send the name of one of your
  account's [model aliases](aliases); the router resolves it to the
  stored route and continues exactly like an explicit model. The
  `alias/` prefix (like `auto/`) is a reserved namespace — no provider
  can ever be named that.

The `model` field is required. There is no per-request `route` object
and no separate routing parameters — everything the router needs comes
from the model ID you send and the model catalogue. To constrain
provider or region, encode it in the model ID (below).

**`lowrouter/auto` has been removed.** It used to let the router pick
the *model* as well as the route, ranked on carbon per token — which
meant the greenest answer was always the smallest model in the
catalogue, a capability downgrade you had not asked for and could not
see. Choosing the model is your decision; choosing the route is ours.
Requests naming it now return `400` and point here.

## Auto-routing a model

The same model is often served by several providers, in several
regions, at different prices and on very different electricity grids.
`auto/<creator>/<model>` lets you name the model and leave that choice
to the router:

```
auto/mistralai/mistral-large-2512
```

You get the same canonical model — same weights, same output. Only the
route is decided for you. Auto-routed IDs appear in `GET /v1/models` alongside the explicit ones
(see [available models](available)) for every model served by more
than one route, so they are selectable in any OpenAI-compatible
client. They are omitted when you filter that listing with
`?jurisdiction=` — auto ranking is global and cannot promise to stay
inside a facet, so a filtered catalogue lists only explicit IDs, which
name their provider and region outright.

### The priority order

The order is **fixed** and identical for every account — there is no
policy to select. Each step only breaks ties left by the previous one:

1. **EU-sovereign routes first.** A route counts as sovereign only
   when *both* halves hold: the provider is an EU-sovereign company
   **and** the serving region is in the EU/EEA. An EU-sovereign
   company serving from a US datacenter does not qualify, and neither
   does a US-controlled provider serving from Frankfurt.
2. **Lowest grid carbon intensity.** A region whose grid intensity we
   have no data for ranks below every region we do — an unknown is
   treated as unknown, never as the greenest.
3. **Lowest price.** Because carbon is decided first, two
   identically-priced routes can never resolve to the dirtier grid. A
   route with no published price ranks below every priced one.
4. **Round-robin** across whatever is still tied.

Routes a provider has taken out of service are excluded outright, at
every step.

### Sovereignty here is a preference, not a guarantee

If a model has no sovereign route at all, an auto request does **not**
fail — it falls through to the greenest, then cheapest route
available anywhere. `auto/anthropic/claude-sonnet-5` routes, to a
non-EU provider.

The response is what keeps this honest.
`lowrouter_metadata.eu_sovereign` tells you whether the route you got
was sovereign, alongside `provider` and `region`, and the `model`
field carries the fully resolved four-segment ID. If you need
sovereignty as a hard constraint rather than a preference, use an
explicit ID — it fails loudly instead of falling back outside your
constraint.

`lowrouter_metadata.routing_reason` names the step that decided:
`eu_sovereign_preferred`, `lowest_carbon_intensity`, `lowest_cost`,
`round_robin`, or `only_route` when the model has just one route.

### What auto-routing does not do

- It never changes **which model** you get — that is your choice, and
  the whole point of naming it.
- It does not score latency or answer quality.
- It does not consult your [aliases](aliases): `auto/` is its own
  namespace and always applies the order above.
- A version shorthand still works — `auto/mistralai/mistral-large`
  resolves to the latest version — but a region does not: there is no
  4th segment to write, because choosing the region is the point.

## Pinning a provider or region

Region and provider are part of the **model ID**, not a separate
field. The public ID has the form
`{provider}/{creator}/{model}[/{locode}]`:

| Goal | How |
|------|-----|
| Pin the provider | Use an explicit model ID — the first segment is the provider, e.g. `vertex/anthropic/claude-opus-4.6`. |
| Pin the region | Append a UN/LOCODE as the 4th segment, e.g. `vertex/anthropic/claude-opus-4.6/sg-sin`. |
| Default region | Omit the 4th segment; see [how IDs resolve](#how-ids-resolve) below. |

If you request a region a model isn't served in, the request is
rejected rather than silently served elsewhere — the region you pin is
the region you get.

## How IDs resolve

You do not have to write the full four-segment ID. What you leave out
is filled in from the catalogue, most-specific first, and the response
always tells you what you actually got.

| You send | What happens |
|---|---|
| `mistral/mistralai/mistral-large-2512/eu` | Used exactly as written. |
| `mistral/mistralai/mistral-large-2512` | **Region defaulted** — resolves to `/eu`. |
| `mistral/mistralai/mistral-large` | **Version and region defaulted** — resolves to `mistral-large-2512/eu`. |
| `anthropic/anthropic/claude-sonnet` | Resolves to the latest sonnet at `/global`. |

**Missing version** resolves to the most recent version of that same
model. The version is the only thing that moves: `claude-sonnet`
resolves to the newest *sonnet*, never to an opus, and
`mistral-large` never to a `mistral-small`. A shorthand that would be
ambiguous between genuinely different models — `openai/openai/gpt-oss`,
where `gpt-oss-20b` and `gpt-oss-120b` are separate models rather than
two versions of one — is **rejected**, not guessed.

**Missing region** resolves to `global` when the model has a global
endpoint. When it doesn't, it resolves to the model's only region, or,
where several exist, to the **lowest-carbon** one. A region whose grid
intensity we don't have data for is never chosen over one we do — an
unknown is treated as unknown, not as zero.

The response's `model` field always carries the **fully resolved**
four-segment ID, so a request is reproducible from its own response —
copy it back and you pin exactly what ran. `lowrouter_metadata.region`
reports the region independently.

### Pinning still fails loudly

Resolution only ever fills in what you left out. It never overrides
what you wrote:

- A version that doesn't exist is an error, not a nudge to the nearest
  one. `mistral/mistralai/mistral-large-9999` → `404`.
- A region the model isn't served in is an error, not a reroute.
  `mistral/mistralai/mistral-large-2512/us-iad` → `503`.
- An explicit `/global` on a model with no global endpoint is an
  error, not a silent switch to a regional row.

That asymmetry is deliberate. Defaults exist so the IDs in our own
docs and model list are callable as printed; pins exist so that when
you name a version or a jurisdiction, the thing you named is the thing
that ran. Aliases are stricter still: an [alias](aliases) target must
be a full, concrete `{provider}/{creator}/{model}/{locode}` triple, so
a stored route can never drift under you.

## Worked scenario: EU-only first, lowest carbon second

Say a data-residency commitment requires EU inference, and within that
constraint you want the lowest carbon available. The order matters:
sovereignty is a **constraint**, carbon is an **optimisation**, and
the two are handled by different mechanisms.

1. **Encode the constraint.** Filter the
   [providers page](https://lowrouter.ai/providers) to EU providers,
   then compare per-token carbon across the models that remain in the
   [catalogue](https://lowrouter.ai/models).
2. **Pin the winner** with an explicit ID, e.g.
   `mistral/mistralai/mistral-large-2512/eu` — or better, point an
   [alias](aliases) at it, so you can re-run the comparison next
   quarter and repoint without touching client code.
3. **Verify, don't trust.** Check `lowrouter_metadata.region` on the
   responses; the pin is proven on every call.

Note what you did *not* do: ask the router for "EU only".
Auto-routing does not treat a jurisdiction as a hard constraint.
`auto/<creator>/<model>` does *prefer* EU-sovereign routes above
everything else, and reports whether it got one — but when a model has
no sovereign route it falls back rather than failing, so it is an
optimisation, not a commitment. Per-key jurisdiction restrictions are
coming; until then the honest tool for a hard constraint is the
explicit ID, which fails loudly rather than falling back outside it.

## What happens on failure

LowRouter does not silently move your request to a different provider:
an explicit ID is a pin, and an auto-routed request commits to the
winner of the ranking. If that route fails, the request returns `503`
naming the provider that failed rather than reporting a different one
you never asked for. Substituting a provider would bill against
another pricing row and serve from another jurisdiction than the ID
promised.

Which providers were attempted is reported on the response in
`lowrouter_metadata.providers_attempted`, alongside
`lowrouter_metadata.fallback_occurred`.

## What the router does not do

- It does not choose the model — that is what naming it is for.
- It does not benchmark output quality — it optimises for
  sovereignty, then carbon, then cost, not "is the answer good".
- It does not accept a per-request `route` object or a
  `prefer_low_carbon` flag. Low carbon is already ranked ahead of
  price, so there is nothing to opt into.
