Jev Router: When Routing Beats Pinning Jev 1.13

Updated Applies to Jev 1.13

OpenRouter lists two TypeSafe entries that both carry the name Jev, and they are not the same product. One is the decision model this site has covered from the start — typesafe/jev-1.13, which answers typed questions with probabilities. The other is typesafe/jev-router, which does not answer your question at all. It decides who should answer it, then gets out of the way.

That distinction is worth an article because the two have opposite failure modes. Pin jev-1.13 and you get a typed, machine-checkable answer, but you also get a model that is deliberately bad at writing prose and open-ended reasoning. Call jev-router and you get a normal text response from whatever model the router selected, with the routing decision itself being made by Jev. Reach for the wrong one and you spend a week fighting a tool that was never meant for the job.

Everything below is drawn from OpenRouter’s own Jev Router documentation and from the OpenRouter models API, both read on 2026-11-03. Where a number appears, it is the number the API returned that day.

TL;DR: typesafe/jev-router is a drop-in model slug: no plugin needed, it works across Chat Completions, Responses and Messages, and the response’s model field tells you who answered. Add the optional jev-router plugin only when you need to pass cost_tier (low, medium, high), include preferences (models / allowed_models) or hard exclusions (excluded_models). Includes are soft — they fall back when they leave no routable candidate; exclusions are hard. A cost tier is a routing preference, not a fixed model, a guaranteed score, or a per-request price cap. If you need a typed decision you can branch on, pin jev-1.13 instead; the router is for ordinary generation with cost control.

Two products, one name

Start with what each one returns, because that determines everything downstream.

Jev 1.13 (typesafe/jev-1.13)Jev Router (typesafe/jev-router)
What comes backA typed decision: chosen answer plus a probabilityOrdinary text from the selected model
Who composes the answerJev itselfWhichever model the router picked
What you sendState plus a question phrased as Noul, Choice or ScoreA normal completion / responses / messages request
Typed output shapeDecision JSONWhatever the serving model emits
Best fitBranching logic, gates, scoring, routing decisions inside your codeOne endpoint across many models with cost policy and preferences
Where the model is namedIn your config, pinned by youIn the response’s model field, decided per request

The catalog data backs the split. When I read the models API on 2026-11-03, the typesafe/jev-1.13 entry reported output modality decisions and its endpoint listed a 262,144-token context with 235,929 maximum completion tokens. The typesafe/jev-router entry reported text output, a 1,000,000-token context, 65,536 maximum completion tokens, input across text, image, file, audio and video — and an empty endpoints array, because there is no single provider sitting behind it. Its pricing fields are -1 for both prompt and completion, which means no fixed per-token rate is published for the router itself: you are billed for whatever model it selects.

That last point deserves emphasis. The router is not a model with its own price list. It is a decision procedure wrapped around other people’s models, so your bill is the sum of the routed models’ charges, not a router rate. Budget against the models in your pool, not against a router number.

What the router actually decides

OpenRouter describes the mechanism in three steps.

Assess the request. Jev evaluates the conversation to identify what the task needs. This is the decision model doing the thing it was built for: judging the shape and difficulty of a request rather than producing output.

Select a model and effort. The router applies the chosen cost policy, the available models’ capabilities, and any restrictions you set on models or providers. Effort here refers to reasoning effort, which matters for models that expose it.

Adapt across turns. In supported agentic workflows the router can retain the model, adjust its effort, or switch models as the task progresses. When the policy permits it, an expert advisor can assist the model doing the work.

Two caveats come straight from the documentation and they prevent most bad expectations. First: the available models and routing policies can change over time. Second, and more important operationally: a cost tier is a routing preference, not a fixed model, guaranteed score, or per-request price cap. If you need a hard ceiling on spend, a router tier is not that mechanism — you want a spend limit on the account or a fixed model, not a preference.

Two request shapes

The simplest integration ignores the plugin entirely. Set the model slug and send your request:

curl https://openrouter.ai/api/v1/responses \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "typesafe/jev-router",
    "input": "Explain the difference between a mutex and a semaphore."
  }'

The response’s model field reports the model that served the request. The router works with Chat Completions, Responses and Messages, streaming and non-streaming — so it slots into an existing client without a new SDK.

The second shape adds the plugin when you want routing configuration to differ per request:

curl https://openrouter.ai/api/v1/responses \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "typesafe/jev-router",
    "plugins": [
      { "id": "jev-router", "cost_tier": "medium" }
    ],
    "input": "Summarize this incident report."
  }'

Plugin parameters:

ParameterTypeBehaviour
idstringRequired. Must be "jev-router".
cost_tierstring"low", "medium" or "high". Omit for Jev’s default routing policy.
modelsstring arrayInclude preferences with fallback, applied before dynamic pool selection.
allowed_modelsstring arrayAlias of models, same fallback behaviour. Entries from both fields are combined.
excluded_modelsstring arrayHard exclusions. Matching models are never selected, including during include fallback.

All fields except id are optional. Unknown fields and invalid values return 400 — the documentation’s example is that allowed_model (singular) is not accepted as an alias. That is the kind of typo that costs an afternoon, and it fails loudly rather than silently, which is the good outcome.

Cost tiers in practice

TierIntent
lowPrioritise lower estimated cost.
mediumBalance estimated cost and capability.
highFavour more capable models when the expected benefit justifies the added cost.

cost_tier selects the routing policy, not the model’s reasoning effort — the two are separate knobs and the documentation is explicit about it. Your model and provider restrictions still apply on top of the tier. Omitting cost_tier means Jev’s default policy; setting "medium" explicitly is how you pin the balanced policy rather than accept the default. An unrecognised tier name returns 400.

One consequence worth designing around: changing cost tiers can change the selected model even within the same conversation. If your application depends on response style or output format staying constant across turns, a per-turn tier change will break that expectation. Pick the tier per workflow, not per call, unless you genuinely want the swap.

Model preferences: soft includes, hard exclusions

This is where most of the operational detail lives, and where the semantics are easy to get wrong.

models and allowed_models are include preferences with fallback, not hard allowlists. The router first tries to build a routable pool inside the combined include set. If that pool is empty, it retries without the includes. So models: ["anthropic/*"] prefers Anthropic but can fall back to another author when the include list leaves no routable candidates. The documentation says it plainly: do not use an include list to enforce an author restriction.

excluded_models is the opposite — a hard restriction that stays enforced in both attempts. Two failure modes follow directly:

For dynamic pools, the filters apply before ranking and before the pool cap. Excluding a higher-ranked model can therefore free a slot for another eligible model. Includes never bypass eligibility checks, endpoint policy, capabilities, or token budgets, and explicit operator pools and built-in pools keep their configured membership.

One more subtlety that will bite anyone tuning cost: the lists can lower the capability tier. When a hard request needs stronger models than the lists leave available, the router can drop its capability requirement, and the stage reports this as list_tier_cap. That is not the same thing as the low / medium / high cost tier, and confusing the two will send you debugging the wrong knob. If no eligible model remains in the pool at all, the request fails with 404 and the error names the fields you sent — advice like widen allowed_models or remove excluded_models.

Pattern syntax and its limits

Each entry in a model preference list can be one of four things:

Size limits are documented: up to 1,024 patterns per list, at most 1,024 characters per pattern, and 65,536 characters total per list. Case sensitivity is the detail people trip over — *Flash* and *flash* are different patterns, and only one of them matches a lowercase slug.

Reasoning effort is a hint, not a setting

The router normally chooses reasoning effort. You can supply the standard top-level reasoning.effort field as a hint, independently of cost_tier:

{
  "model": "typesafe/jev-router",
  "plugins": [{ "id": "jev-router", "cost_tier": "medium" }],
  "reasoning": { "effort": "high" },
  "input": "Analyze the tradeoffs in this design."
}

On Chat Completions the legacy top-level reasoning_effort field is also accepted. But the router can select a different effort or adapt it across turns, so a hint is not a guarantee of the setting sent to the final model. And two fields are rejected outright: reasoning.max_tokens and reasoning.mode: "pro" return 400. If you have a config that sets either, strip it before pointing at the router.

Seeing what the router did

A router that silently picks models is impossible to debug, so OpenRouter exposes the decision. The response’s model field gives you the headline answer. Add the header X-OpenRouter-Metadata: enabled and the jev-router entry inside openrouter_metadata.pipeline carries the details:

FieldMeaning
resolved_modelsModels the router selected, in fallback order.
candidatesRouted candidates with model, effort, capability tier, and estimated_cost. Estimates are not final billed costs.
reasonThe policy’s reason for selecting or retaining a route.
pipeline"default" or "agentic".
advisor_modelThe advisor, when one is selected.
models, excluded_modelsThe lists the router applied; models includes allowed_models entries.
list_fallback"models_ignored" when the include list matched catalog models but no pool model.
list_tier_capThe tier the lists capped the request at, when they lowered it.
max_fallback"deep" when a request that would get an advisor was served without one.

The estimated_cost caveat matters for billing work: candidates carry estimates, and estimates are not final billed costs. If you are reconciling spend, use the actual usage records rather than summing router estimates.

Practical advice: log model and reason for every routed call during a rollout. When someone asks why a request became expensive or why the tone changed between two weeks, reason is the field that answers it, and it is not reconstructible after the fact unless you captured it.

Availability, errors, and the regional restriction

Three restrictions apply before any of the above matters.

Global data region only. Jev Router is available only in the global data region and only in workspaces that allow it. Requests using regional routing are among the documented 400 cases.

Access to the router and to the selected models. You still need access to the router and to whichever models get selected. A pool full of models your workspace cannot call will surface as failures, not as degraded routing.

Do not combine router models. typesafe/jev-router must not be combined with another router model in the same request; doing so returns 400. In practice this means no stacking the Jev Router with the auto router or with an author router in one call.

StatusDocumented examples
400Unknown cost tier or plugin field, unsupported reasoning settings, regional routing, combining router models.
404No eligible model remains after applying model preferences and request requirements, or you do not have access to the router.
503Jev Router is unavailable.

Handle 503 as transient — the router itself being unavailable is not a problem in your request body. Handle 404 as a configuration bug in your preference lists or entitlements. Handle 400 as something you shipped and can fix in a deploy.

Author routers

There is a second way to get Jev’s routing: when available in the catalog, openai/auto, anthropic/auto and other <author>/auto models run Jev with that author’s models. They use the same pool discovery, task policy, reasoning effort and cost tiers as Jev Router.

Two properties to keep straight. First, author scope is a hard restriction: caller models and allowed_models remain include preferences with fallback, excluded_models remains a hard restriction, and all three operate inside the author’s pool. If includes leave no routable candidates, Jev retries without them within the same author; exclusions stay enforced. An author with no eligible model returns 404. Second, the scope is the model author, not the provider — an OpenAI model may still be served by another provider such as Azure. If you need a provider restriction, that is a separate setting.

openrouter/auto is a separate router and does not use this author-alias behaviour. And aliases are catalog artefacts: an alias requires its own catalog router row and, if private, access to that row. The API does not create aliases on request, so you cannot define your own yourauthor/auto by asking nicely.

When the router is the wrong tool

Three situations where pinning jev-1.13 is the correct answer, not the conservative one.

You need a typed decision you can branch on. If the code does if answer.confidence > 0.8 then act, a router that returns prose from an arbitrary model is useless. The decision model exists precisely so there is no text to parse. The router does not produce decisions; it produces text.

You need reproducibility across batches. Scoring, moderation and reranking pipelines compare results over time. The router’s documented behaviour — models and policies can change, tiers can swap models mid-conversation — is the opposite of what a benchmark or a scoring rubric needs. Pin the slug, pin the question phrasing, and treat any change as a deliberate migration.

You need a hard price ceiling per request. A tier is a preference, and the docs say so explicitly. Budget control belongs at the account level (spend limits) or at the model level (a fixed, cheaper slug), not in a routing preference.

Conversely, the router earns its place when you have a mixed workload and no appetite to select models per task: one endpoint, one client, a policy that adjusts to task difficulty, and optional preferences when a particular workflow needs to stay within a family or away from an expensive family.

A safe rollout checklist

  1. Confirm the slug. Read typesafe/jev-router on the OpenRouter model page before shipping. Slugs and model populations change.
  2. Start model-only. No plugin, no preferences. Confirm the response’s model field is populated and that your downstream parsing tolerates a changing value.
  3. Then add cost_tier, one workflow at a time, and record which tier each workflow uses. Never let two workflows share a tier constant by accident.
  4. Turn on metadata logging (X-OpenRouter-Metadata: enabled) for at least a week and persist model, reason, candidates[].tier and list_fallback. This is your only forensic trail.
  5. Add preferences last, and prefer exclusions over inclusions. Exclusions are hard; inclusions silently fall back. If the intent is “never this model”, use excluded_models. If the intent is “prefer this family”, use models and accept the fallback.
  6. Watch for 404s after any preference change. A 404 almost always means the list no longer leaves an eligible model, or a slug stopped matching.
  7. Do not stack routers. One router per request. If you need author scoping, use the author alias instead of combining.

The failure pattern this checklist prevents is the expensive one: an include list that falls back silently, a tier changed mid-conversation, and a bill that nobody can explain because the metadata was never captured.

Where to go next

If you are still deciding whether Jev is the right shape of tool for your problem, start with what Jev AI is. If you have decided and want the first call working, first API call and get an API key are the shortest path. For the cost picture before you route anything, cost and latency covers the arithmetic; for picking between the official channel and OpenRouter, api channels. And if a routed request starts failing with codes you did not expect, troubleshooting is organised by symptom.

One honest note about scope: this page describes the router as documented by OpenRouter on 2026-11-03 and does not claim anything about TypeSafe’s own direct endpoints. If the official API exposes the router, its authoritative behaviour belongs to typesafe.ai documentation, not to a third-party write-up.

Frequently asked questions

+ What is Jev Router and how is it different from Jev 1.13?

Jev 1.13 (typesafe/jev-1.13) is the decision model itself: you send state plus a typed question and it returns a typed answer with a probability. Jev Router (typesafe/jev-router) is a routing layer: you send an ordinary request, Jev assesses the task, and the router chooses which model and reasoning effort should answer it. The router's output is normal text from whichever model served the request, not a typed decision.

+ Does Jev Router require a plugin?

No. Setting model to typesafe/jev-router is enough. The jev-router plugin is optional and only needed when you want to pass routing configuration such as cost_tier, models, or excluded_models. The plugin's id field is required and must be the string jev-router.

+ Are the models list in Jev Router a hard allowlist?

No. models and its alias allowed_models are include preferences with fallback. The router first tries to build a routable pool inside the combined include set; if that pool is empty it retries without the includes. excluded_models is the hard restriction and is enforced in both attempts. If you need an author restriction, do not rely on an include list for it.

+ Why does the router sometimes return a 404 instead of picking a model?

A 404 means no eligible model remained after applying your preferences and the request's requirements, or that you do not have access to the router. Two documented cases: an include list that matches no public text model in the catalog fails immediately with 404 naming the field, and an excluded_models list that removes every pool model also fails with 404 rather than routing to an excluded model.

+ How do I find out which model the router actually used?

Read the model field of the response; it reports the model that served the request. For more detail, send the header X-OpenRouter-Metadata: enabled and inspect the jev-router entry inside openrouter_metadata.pipeline, which lists resolved_models, candidates with model, effort, tier and estimated_cost, the policy's reason, the pipeline mode, and the advisor_model when one was selected.

Keep reading