Gateway
The router
Send model: auto and Conifer picks the model for that turn from the catalog your key can call. The receipt names what it picked. Nothing is ever substituted behind a named id.
What it is
Every model in the catalog is behind one key. When you would rather not choose between them, send one of three ids instead of a model name and Conifer chooses for you, turn by turn. It looks at what the request needs, picks the model, keeps a short list of fallbacks in case the first choice cannot be served, and answers in the same request. The receipt headers say which model served.
The three ids are listed in /v1/models with virtual: true.
| Model id | What it optimises for |
|---|---|
auto | Balanced value. The default way to let Conifer choose, and the same as balanced. |
balanced | The best value among the models that can answer the request well. Frontier-class answers at a fraction of frontier cost on most turns. |
best | The most capable model for the request, whatever it costs. Hard proofs and deep systems work land on frontier models here. |
Use it
Any Conifer key. Any client that lets you set the model id. The examples below are the same call.
curl
curl -s -D - https://api.conifer.build/v1/chat/completions \
-H "authorization: Bearer $CONIFER_API_KEY" \
-H "content-type: application/json" \
-d '{"model":"auto","messages":[{"role":"user","content":"What is 17 * 23?"}],"max_tokens":50}' \
| grep -i x-conifer
# x-conifer-requested-model: auto
# x-conifer-effective-model: deepseek-v4-flash
# x-conifer-receipt-reason: routed
# x-conifer-cost-nanousd: 10120The Conifer SDK
import { Conifer } from "conifer-sdk";
const conifer = new Conifer(); // reads CONIFER_API_KEY
const answer = await conifer.chat({
model: "auto", // or "balanced" | "best"
messages: [{ role: "user", content: "What is 17 * 23?" }],
maxTokens: 50,
});
answer.receipt.effectiveModel; // "deepseek-v4-flash"
answer.receipt.reason; // "routed"
// The decision alone, free, no completion:
const pick = await conifer.route({ query: "…", policy: "best" });
pick.model; // "claude-opus-5"
pick.fallbacks; // the next picks, in orderfrom conifer_sdk import Conifer, ChatRequest, RouteRequest
conifer = Conifer() # reads CONIFER_API_KEY
answer = conifer.chat(ChatRequest(
model="auto",
messages=[{"role": "user", "content": "What is 17 * 23?"}],
max_tokens=50,
))
answer.receipt.effective_model # "deepseek-v4-flash"
answer.receipt.reason # "routed"
pick = conifer.route(RouteRequest(query="…", policy="best"))
pick.model, pick.fallbacksThe openai package
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.CONIFER_API_KEY,
baseURL: "https://api.conifer.build/v1",
});
// The model id is the whole integration. The receipt is in the headers.
const { data, response } = await client.chat.completions
.create({ model: "auto", messages: [{ role: "user", content: "hi" }] })
.withResponse();
response.headers.get("x-conifer-effective-model");
response.headers.get("x-conifer-receipt-reason"); // "routed"Codex, Cursor, Cline, anything with a model field
# Codex speaks the Responses wire. Conifer routes it the same way.
export OPENAI_BASE_URL=https://api.conifer.build/v1
export OPENAI_API_KEY=$CONIFER_API_KEY
codex --model autoThe decision without the completion
POST /v1/route returns the pick and stops. It is free. Use it when you want to make the call yourself, log the decision, or send the request somewhere Conifer does not serve.
curl -s https://api.conifer.build/v1/route \
-H "authorization: Bearer $CONIFER_API_KEY" \
-H "content-type: application/json" \
-d '{"query":"Prove the Cauchy-Schwarz inequality rigorously.","policy":"best"}'
# {"model":"claude-opus-5",
# "fallbacks":["claude-fable-5","kimi-k3","gpt-5.6-sol"],
# "policy":"best"}| Field | Meaning |
|---|---|
query | The text to route on. Send the last user message, not the whole conversation. |
policy | balanced or best. Anything else is a 400. |
model | A catalog id your key can call right now. |
fallbacks | Up to three further picks, in order. On a chat turn the gateway walks these itself if the first pick cannot be served. |
Read the receipt
On a routed turn x-conifer-requested-model is the id you sent and x-conifer-effective-model is the model that served. x-conifer-receipt-reason tells you why.
| receipt-reason | Meaning |
|---|---|
routed | Conifer chose this model for this turn. |
as_requested | You named a model, or the router did not answer in time and the gateway served its default pin under the id you sent. |
provider_failover | The first pick could not be served and one of the fallbacks answered. |
You pay the catalog price of the model that served. The router itself is free. A best turn that lands on a frontier model costs what that model costs.
When the router does not answer
You never wait on the router. If it does not answer in time, the gateway serves its default pin for that turn and marks the receipt as_requested. The next turn routes as normal. If you call /v1/route directly and get a 503 saying the router did not answer, retry.
Which doors route
| Endpoint | model: auto |
|---|---|
/v1/chat/completions | Routes. |
/v1/responses | Routes. Codex and the other Responses-wire agents get the router. |
/v1/messages | Serves the default pin. This door relays the native Anthropic wire and cannot carry a pick to a non-Anthropic model. Claude Code users name a model. |