Some models in the picker are not one model. Jev Router, typesafe/jev-router on
OpenRouter, looks at each request and chooses another model to answer it. When you pick it
in aimo, the model that replies can change from one message to the next, and the
platform shows you which one it was.
This article follows the API calls behind that: how the router gets into the list, what the gateway sends, where the choice is made and what you can read back. Every command below was run against a live installation on 5 October 2026, and the answers quoted are the ones it gave.
The short answer
aimo never switches models. It sends one model id, OR/typesafe/jev-router, and the
gateway passes that id on to OpenRouter. The choice happens inside OpenRouter. Jev, the
decision model behind the router, reads the conversation, judges the task and how hard it
is, and picks the cheapest model in its pool that meets the bar. The model it chose is
reported in the model field of the answer, and the provider that ran it in provider.
The gateway’s part is smaller. It lists the router like any other OpenRouter model, applies the models your account may use, sends the request and records the provider and the cost OpenRouter reports.
Who does what
| Step | Who | Where |
|---|---|---|
| Register the router | Administrator | Console, /providers → OpenRouter |
| Allow it for a team or person | Administrator | Console, /keys → model profiles |
| Pick it in a chat | Each person | aimo, the model picker |
| Call it from a script | Each person | Any HTTP client, with a platform key |
Step 1: the router is in the list
An administrator registers typesafe/jev-router on the OpenRouter tab of /providers.
Add & check asks OpenRouter two things that cost no tokens: whether the key is valid and
whether the id exists. A router has no fixed endpoint, so an empty endpoint list is normal
and does not count as an error. The check saves the model’s modality, and the platform
turns that into the capabilities it shows.
aimo fills its picker from the list the gateway gives it. Look at the same list yourself:
GW=https://ai.corp.example
curl -s $GW/v1/models -H "Authorization: Bearer $BOSPHORUS_KEY" \
| jq -c '.data[] | select(.id=="OR/typesafe/jev-router")'
The entry the live gateway returned:
{"capabilities":["vision","audio"],"id":"OR/typesafe/jev-router","nodes":["openrouter"],"object":"model","owned_by":"openrouter","type":"llm"}
OpenRouter models are listed under the prefix OR/. The entry appears only if the models
your account may use include it. A capability that is missing from an entry means the
platform could not confirm it, not that the model lacks it.
Step 2: what one message does
When you send a message, aimo posts to /v1/chat/completions with the model
OR/typesafe/jev-router. The gateway then:
- Removes the
OR/prefix and works with the idtypesafe/jev-routerfrom here on. - Serves the request from the OpenRouter models only. It never falls through to another provider that happens to have a model of the same name.
- Checks that the model is enabled, that a key exists for it and that your account may use it. Then your policy checks and rate limits run, as for any model.
- Sends the request to OpenRouter with one field added, so that OpenRouter reports the cost.
OpenRouter hands the request to Jev, which chooses the model. The answer comes back with the model that ran and the provider that served it, and the gateway streams it to you unchanged.
Step 3: see the switch yourself
Send two very different prompts and compare the model field of each answer. The first
command defines a small helper so the two calls stay short.
ask() {
curl -s $GW/v1/chat/completions \
-H "Authorization: Bearer $BOSPHORUS_KEY" -H 'Content-Type: application/json' \
-d "$1" \
| jq -c '{model, provider, cost: .usage.cost, tokens: .usage.completion_tokens}'
}
An easy prompt:
ask '{"model":"OR/typesafe/jev-router","max_tokens":200,"messages":[{"role":"user","content":"Say hello in one word."}]}'
{"model":"openai/gpt-6-luna","provider":"Azure","cost":0.0000037,"tokens":5}
A hard one:
ask '{"model":"OR/typesafe/jev-router","max_tokens":3000,"messages":[{"role":"user","content":"Prove that the square root of 2 is irrational, then find the smallest solution of x^2 - 2y^2 = 1 with y>0."}]}'
{"model":"anthropic/claude-sonnet-5.5","provider":"Claude Platform on AWS","cost":0.005046,"tokens":494}
Same model id in the request, two different models in the answers, and a cost for each that is the figure OpenRouter reported for that one request. These two are samples of what Jev chose on that day. What it picks is up to OpenRouter and TypeSafe, and it can change.
With "stream": true the first frame already carries the same two fields, so a client can
show which model is answering before the text arrives:
{"model":"openai/gpt-6-luna","provider":"Azure"}
Narrowing the pool
By default Jev chooses from its whole pool. A script can narrow it by sending a
jev-router plugin with the request. Each entry is an exact model id or a pattern such as
openai/*. The gateway passes the field through. aimo’s picker does not send one.
ask '{"model":"OR/typesafe/jev-router","max_tokens":300,"plugins":[{"id":"jev-router","models":["openai/*"]}],"messages":[{"role":"user","content":"Prove that the square root of 2 is irrational, in three sentences."}]}'
{"model":"openai/gpt-6.1-sol","provider":"OpenAI","cost":0.001734,"tokens":169}
A hard prompt that went to Anthropic’s model above stayed with OpenAI’s here. Entries in
models that match nothing are ignored. Entries in excluded_models are never ignored:
if they remove every candidate, the request fails with a 404.
A policy note. Allowing OR/typesafe/jev-router for a team allows whatever Jev routes to,
because the platform only learns the chosen model when the answer arrives. If that matters,
narrow the pool with the plugin or choose a specific model. The
article on calling the API with your own keys
explains which credential a script sends.
What the platform records
Each request leaves one usage record. It holds the model you asked for, typesafe/jev-router,
the provider that actually served it and the cost OpenRouter reported. The name of the
model that answered is in the response body that you receive, not in that record. In the
console the same requests appear on /activity, and the cost on /usage.
When something is not right
| What you see | Likely cause |
|---|---|
| The router is missing from the list | It is not registered, it is disabled, or the models your account may use leave it out. Ask an administrator. |
A 503 that names /providers |
The id is not registered, or no key is set for it. |
A 403 model_not_allowed |
The models your account may use do not include it. In aimo this appears as a message in the chat. |
| A 404 after you sent a plugin | Your excluded_models removed every candidate. |
| Costs look like they change between turns | Jev chose a different model. Compare the model field of each answer. |