
# Catalog curation

Third-party OpenAI-compatible clients (Jan, Chatbox, and most others)
offer exactly the models `GET /v1/models` returns. The full catalogue
is hundreds of entries, which makes those pickers unusable. Catalog
curation lets you store two allowlists on your account so the listing
shows a short, relevant selection instead.

Curation is account-wide: every API key on the account sees the same
curated listing. An unauthenticated request (no API key) always sees
the full catalogue.

## The two lists

**Model allowlist** — exact three-part public IDs
(`{provider}/{creator}/{model}`). When non-empty, only these models
are listed.

**Region allowlist** — locodes (e.g. `fi-hel`) and/or two-letter
country codes (e.g. `de`, which matches every locode in Germany).
When non-empty, each listed model's `regions[]` is trimmed to the
matching regions; a model left with no region drops out of the
listing. A `global` region always survives — it names the absence of
a region commitment, not a place — so a model served only globally
stays listed under any region list.

One caveat: `eu` is a provider *bloc code*, not a country. An `eu`
entry matches only regions published under the literal `eu` code —
to curate EU-wide, list the countries you want (`de`, `fr`, `fi`,
…).

The lists apply independently: use either one alone, or both.

Namespaced entries follow their own rules:

- `auto/<creator>/<model>` entries appear exactly when their
  underlying model backs one of your allowlisted IDs. The region list
  never affects them — [auto-routing](routing) picks its own region
  per request.
- `alias/<name>` entries are always listed. An alias is itself
  curation: you created it on purpose, so no list needs to repeat
  that decision.

## Managing the lists

In the dashboard: **Settings → API model catalog**, with pickers for
models and regions.

On the API, `GET`/`PUT /v1/catalog` with your API key:

```bash
curl https://api.lowrouter.ai/v1/catalog \
  -H "Authorization: Bearer $LOWROUTER_API_KEY" \
  -X PUT -H "Content-Type: application/json" \
  -d '{
    "model_allowlist": [
      "mistral/mistralai/mistral-small",
      "vertex/anthropic/claude-opus-4.6"
    ],
    "region_allowlist": ["fi-hel", "de"]
  }'
```

`PUT` replaces both lists wholesale — send the entire desired state.
An empty (or absent) list means "show everything", so
`{"model_allowlist": [], "region_allowlist": []}` resets the account
to the full catalogue.

Every entry is validated against the live catalogue. An unknown model
ID or region comes back as a `400` naming each offending entry, and
nothing is stored — a typo can never silently curate the wrong thing.

Entries you already have stored are exempt from re-validation: if a
model you allowlisted is later delisted, it never blocks saving the
rest of your selection. A stale entry hides nothing; remove it
whenever you like (once removed, it validates as new again).

## Display-only — not a security boundary

Curation changes what `GET /v1/models` *advertises*, and nothing
else. A hidden model ID still resolves on `GET /v1/models/{id}` and
still routes on completions. Do **not** use these lists to restrict
what a key is allowed to call — an enforced per-key allowlist
(requests to hidden models rejected with a 403) is a separate,
planned feature.

## Interaction with other filters

The `?jurisdiction=` and `?modality=` query filters apply on top of
curation: the listing is first curated to your lists, then faceted by
the request. A model your lists admit can still be excluded by a
facet, and vice versa.
