Models & Routing
How Ada AI discovers models across your upstreams, picks one by priority, and fails over on 5xx — without ever silently substituting a model.
Model discovery
GET /v1/models returns the union of model IDs across the enabled upstreams
belonging to your key's owner — you, or the one organization the key is scoped
to. It never combines personal and org pools.
curl https://api.ada.ai/v1/models \
-H "Authorization: Bearer sk-rc-..."{
"object": "list",
"data": [
{ "id": "glm-4.7", "object": "model", "owned_by": "llm-proxy" },
{ "id": "glm-4.5", "object": "model", "owned_by": "llm-proxy" },
{ "id": "swiss-ai/Apertus-70B-Instruct-2509", "object": "model", "owned_by": "llm-proxy" },
{ "id": "Qwen/Qwen3.6-35B-A3B-FP8", "object": "model", "owned_by": "llm-proxy" },
{ "id": "moonshotai/Kimi-K2.7-Code", "object": "model", "owned_by": "llm-proxy" }
]
}The owned_by field reflects the proxy, not the underlying provider, because a
single model ID may be served by more than one of your upstreams.
Keeping the list fresh
Each upstream stores its own model list. After you add or change an upstream,
click Sync models in the dashboard (or POST /me/upstreams/{id}/sync-models
via the API) so the proxy can see which IDs are currently available. A model you
request must be present on at least one enabled upstream.
How a request is routed
Given a request for model: "glm-4.7":
- The proxy looks up your key's owner (you, or one org).
- It finds every enabled upstream belonging to that owner whose synced model
list contains
glm-4.7. - It tries them in descending priority — the upstream with the highest
prioritynumber first. - If that upstream returns
5xx, the proxy fails over to the next matching upstream automatically. - If every matching upstream fails, the request returns
502(see Errors). - If no upstream exposes the requested model, the request returns
400— the proxy never silently substitutes a different model.
request: model = "glm-4.7"
upstreams (owner = you) synced models
┌──────────────────────┬───┐ ┌─────────────────────────┐
│ Acme (priority 10) │ ✔ │ │ glm-4.7, glm-4.5, ... │ ← tried first
├──────────────────────┼───┤ ├─────────────────────────┤
│ Backup (priority 1) │ ✔ │ │ glm-4.7, gpt-4.1-mini │ ← failover target
└──────────────────────┴───┘ └─────────────────────────┘Priority is per upstream, not per model
priority orders your upstreams. If Acme is 10 and Backup is 1, then
for every model both of them serve, the proxy tries Acme first and only
falls back to Backup on a 5xx. There is no per-model priority override.
Scope is fixed at mint time
A proxy key resolves against exactly one owner — personal, or one organization — chosen when the key is minted and fixed thereafter.
- A personal key uses only your personal upstreams.
- An org key uses only that org's upstreams — never your personal ones, and never a different org's.
- There is no per-upstream scoping within an owner in v1. Mint separate keys to separate traffic; they share the same upstream pool.
See API keys and Organizations.
Adding an upstream
In the dashboard: Upstreams → Add upstream. Pick a preset (which fills the base URL) or define a custom one. The fields:
| Field | Notes |
|---|---|
| Name | Your label. |
| Base URL | Provider API root including the version segment (https://api.openai.com/v1). The proxy appends /chat/completions and /models. |
| API key | Your provider key. Encrypted at rest. |
| Priority | Higher is tried first. Use this for failover ordering. |
After saving, Sync models populates the model IDs available on that upstream. The Models page shows the union across all your enabled upstreams.
Upstream API keys are encrypted in PostgreSQL with KEY_ENCRYPTION_KEY. Keep
that key stable — losing it makes existing encrypted values unreadable.