Routes
A route is a name such as
odnoga/smartthat stands for an ordered list of models. Your code sends the name instead of a model; odnoga tries the models in order and moves to the next one when a provider fails. You change the models in odnoga — the code never changes, and nothing changes without you: every response names the route and version that served it.
Create a route
Workspace → Routing → Routes → New route.
- Give it a name:
odnoga/plus lowercase letters, digits, dots, dashes or underscores. - Start from a suggestion built from your own catalogue and prices — Cheapest that works, Fast, Strongest, EU only; each takes one model per provider, so one provider outage never stops the route — or add models from the list. Model names are never typed.
- Drag the models into order (or use the arrows). What a request does shows exactly what the gateway will do — it is worked out by the same code the gateway runs.
- Create it, then send its name as the model:
POST https://api.odnoga.com/v1/chat/completions
{ "model": "odnoga/smart", "messages": [{ "role": "user", "content": "Hello" }] }
Every save is a new version with who made it and why. History shows every version and restores any of them as the newest one. Changes reach every request within 15 seconds.
Test before you save
In the route editor, Test before you save sends one sample request to each model of the list, one at a time, and shows what each did: time, tokens in and out, cost, and — when the sample offers tools — whether the model called the tool with valid JSON, answered without it, or broke the JSON. Start from a ready-made sample (short answer, a tool call, a JSON answer) or paste your own request. The fastest and cheapest answers are marked.
Is the answer as good? With two or more models, the first in the list is the reference — put the model you use today first. A judge model reads your request and both answers and scores each other model against the reference: 0–10, better / as good / slightly worse / worse, and one sentence why. It judges correctness, completeness and following your instructions (language, format), and for tool calls the right tool with the right values — not length or style. The judge is never one of the models being compared, and its call is billed like the others.
The calls are real: billed to the workspace like any call (labelled route-test, at most 1,024 output tokens
each), and the editor shows the most the test can cost before you run it. Each call is pinned to its model, so a
row never describes a fallback. Nothing about the route changes until you save. Workspace admins, from the Growth
plan; 30 test runs per workspace per hour. AI tools use routes.test.
Recommendations (recommended with approval)
odnoga watches the catalogue for every named route and proposes, for its first model:
| Kind | What it means |
|---|---|
| Replacement | The model is being retired and the catalogue names its successor. |
| Newer version | Same vendor and model line, newer, no more expensive. |
| Cheaper — test first | Same kind of call, capabilities and price class, at least 20% less on the route's own traffic. |
Each shows the saving simulated on the route's own last 30 days at your workspace's prices (or on 1,000
typical calls when the route has no traffic yet). A cheaper model can be approved only after Test against
current on your sample in the last 24 hours: a judge model reads your request and both answers and scores the
cheaper one against the current one from 0 to 10, with one sentence why — it needs 7 or more. Nothing changes
until you approve: approving saves a new version with the recommended model first (undo from History), Not now
hides it until the route changes. Included from the Extend plan. AI tools use routes.recommendations.
Within limits (autonomous)
Instead of a fixed list, a route can follow limits you set in the editor: the highest price per 1M tokens
(in and out — at least one), the capabilities it must have (tools, JSON schema, images, reasoning, EU), optionally
which vendors, what to prefer (cheapest, fastest — measured median response time on odnoga over the last 7
days — or newest) and how many models. The editor shows live what the limits mean today (A → B → C),
spread across vendors so one outage does not stop the route.
odnoga re-checks every 15 minutes. When the best fit changes it saves an ordinary new version whose note says
why (Autonomous: within your limits the best fit is now …) — so every change is in History and can be undone, and
no model above your limits is ever chosen. If nothing fits any more, the last good list stays and the route card
says Limits: needs attention. Answer quality (for example in Polish) is not measured by odnoga yet, so it is not a
limit — use Test before you save. Switching the route back to a fixed list stops the limits. Extend plan and up;
AI tools use routes.policy_preview and routes.save_within_limits.
Connect a route to a function
Different processes need different models. A function that sends the
x-odnoga-app and x-odnoga-function headers appears in odnoga after its first
call; on a route, Connect function picks it. From then on every call from that function is
served by the route, whatever model its code sends — you switch a process's AI here, without a
code change. The route card lists the functions it serves (Used by).
- One route per function; connecting it to another route moves it.
- A call that is part of a running prompt A/B test keeps the model being tested.
- A route never takes a call of another kind: a chat route never serves an embeddings call.
- Archiving a route sends its functions back to the model their code sends.
What every response says
| Header | Meaning |
|---|---|
x-odnoga-route | The route that served the call. |
x-odnoga-route-version | Its version. |
x-odnoga-route-chain | The models it was going to try, in order. |
x-odnoga-route-connected | <app>/<function> when the route served the call because the function is connected. |
x-airouter-model | The model that answered. |
x-airouter-fallback | 1 when an earlier model failed and a later one answered. |
The same is recorded on every request (Requests → the request → route).
Rules a route follows
- 1 to 8 models, no duplicates, each serving today and allowed in this workspace.
- One kind of call: chat models and embedding models never mix. Image and audio models are not routed yet.
- Every model is checked against the workspace's Allowed models; one that is not allowed is left out, and the preview says so.
Automatic chain and per-model fallbacks
On the same page: the automatic chain is what {"model": "auto"} uses, and the fallback for
any model without its own; fallbacks for a specific model say which models answer when that
one fails. Both are edited with the same picker.
Plans
| Free | Growth and up | |
|---|---|---|
Automatic chain ("model": "auto") and per-model fallbacks | ✓ | ✓ |
Named routes (odnoga/<name>), versions and undo | ✓ | |
| Connect a route to a function | ✓ | |
routes.* changes from AI tools (MCP) | ✓ | |
| Test before you save | ✓ | |
| Recommendations with one-click approval | Extend and up | |
| Routes within limits (autonomous) | Extend and up |
An organisation that leaves Growth keeps what it made: its named routes keep serving and their
functions stay connected. They become read-only — archiving a route and disconnecting a function
still work; creating, changing, rolling back, restoring and connecting answer
Named routes is included from the Growth plan until the plan includes them again.
Errors
| Code | When |
|---|---|
400 route_not_found | No route of that name in this workspace (or it is archived). |
400 route_missing_capability | None of the route's models can do what the request needs (for example images). |
400 model_not_allowed | None of the route's models is allowed in this workspace. |
With AI tools (MCP)
Connected assistants use the routes.* tools: routes.guide explains everything above and the safe
order of calls; routes.models lists the models a route may use; routes.preview shows what a
request would do; routes.save refuses to overwrite a change someone else made meanwhile. See
MCP.