Changelog
2026-10-17
- Webhooks. odnoga now calls your app, signed, when a user's credits or your organisation's credit run low or out — so you can warn or top up without polling. Add them under Connect → Webhooks; every delivery is shown, failures are retried for 12 hours. See Webhooks.
- Tests now say whether the answer is as good. When you test two or more models (route editor, Recommendations,
routes.test), the first is the reference and a judge model scores every other answer against it — 0–10, better / as good / slightly worse / worse, and one sentence why — instead of only checking that a valid answer came back. A cheaper recommendation can now be approved only when its answer scored 7 or more against the route's current model. See Routes. - No more spending the same last dollar twice. While a request runs, its estimated cost is held from your organisation's credit (when you pay from credit) and from the end user's wallet (when wallets block at zero), so requests sent at the same moment can no longer all spend the last of a balance. When a request ends, the hold is replaced by the real charge, or given back if it failed. Image and audio requests now check end-user wallets too. See Per-user credits.
- Read balances from the API.
GET /v1/balancereturns your organisation's credit (held and available) and, with?end_user=, a user's wallet; an end-user token gets only its own wallet, so a browser app can show it directly.GET /v1/ledger?end_user=lists that user's grants, top-ups and spends. See Showing a balance. - Your JSON schemas fit every vendor. Tool parameters and
json_schemaanswers are adapted to what the vendor serving the call accepts — for example,$refdefinitions written out in place for a vendor that refuses$ref. What each vendor accepts is measured every week by sending each keyword on its own; a keyword the vendor accepts is never touched. Every change is reported in the newx-odnoga-dialectheader and on the request in your log. When a fallback model answered, the parameters it could not take are now reported too (x-airouter-stripped), as they always were for the first model.
2026-10-16
- One engine chooses every model. Every request — a model name,
autoor a named route — is now planned by the Route engine, in every workspace. It was compared with the code it replaces on every live request for a week (8,000+ requests, no difference), so nothing you send behaves differently. The automatic chain and each model's fallbacks are stored as routes with versions and history, androuting.previewnow answers from the same engine as the gateway, named routes included. Model families, which no workspace could use, are gone (x-airouter-familyand theno_route_candidateerror with them). - Allowed models now cover fallbacks too. A model outside a workspace's allowed list is no longer tried as a fallback — the restriction applies to every model a request can reach, as the Allowed models page says. (No workspace had an allowed list, so no traffic changed.)
- Routes within limits. Set a ceiling (price per 1M tokens), the capabilities a route needs, vendors, and
cheapest / fastest / newest — the editor shows what that means today, and odnoga keeps the route on the best fit.
Every change is a recorded version with its reason, never above your limits, undoable from History. Extend and
up; AI tools get
routes.save_within_limits. See Routes. - Route recommendations, with your approval. For each named route odnoga proposes a replacement for a
retiring model, a newer version of the same line, or a cheaper model of the same class — with the saving worked
out on that route's own last 30 days. A cheaper model is approved only after it passed a test against the
current one on your sample; every approval is a new version you can undo. Extend and up; AI tools get
routes.recommendations. See Routes. - Test a route before you save it. The route editor sends one sample request to each model — a short
answer, a tool call, a JSON answer, or your own — and shows the measured time, tokens, cost and whether tools
were called with valid JSON, marking the fastest and cheapest. The most it can cost is shown before it runs.
AI tools get
routes.test. See Routes. - Cost by function. Usage by function now has the same cost report as the organisation view, one level
down: what each function spent by day, week or month, its share of the workspace's spend, and — pick a
function — which models it used and what each cost. Stacked, area, line or share-donut charts, CSV export,
a year of history in an instant. The functions table gains a Share column and each function's models show
their share of its cost. AI tools get
usage.by_function, andfunctions.listrows carrycost_share_pct. See MCP. - Every share in the usage report. The organisation and platform usage tables show each row's share of the total next to its cost.
2026-10-13
- Named routes are part of the Growth plan. Fallbacks and the automatic chain stay on every plan, Free included. Named routes, their versions and undo, and connecting a function to a route come with Growth and up. Nothing already made is taken away: on a plan without them, existing routes keep serving, read-only. See Routes.
2026-10-12
- Routes: one name, the models behind it chosen in odnoga. Send
"model": "odnoga/smart"and odnoga serves it from the models you picked, in your order, failing over between providers. Pick models from a list, drag them into order, see exactly what a request will do before saving, restore any earlier version. Every response says which route and version served it (x-odnoga-route,x-odnoga-route-version). See Routes. - Connect a route to a function — change a process's AI without a code change. Every call
from a connected function is served by its route, whatever model its code sends; each route
shows which functions it serves. Responses carry
x-odnoga-route-connected. - No more typing model names. The automatic chain (
"model": "auto") and per-model fallbacks are edited on the Routes page with the same picker; the old Routing rules page is gone and its address opens Routes. - AI tools (MCP): new
routes.*tools —routes.guidefirst — so a connected assistant lists, previews, changes and connects routes with the same checks as the page.
2026-10-11
- Gemini tools take the full JSON Schema you send. Tool definitions sent to Gemini models now
keep
additionalProperties,$ref/$defsand nullable types such as["integer", "null"]— the shape OpenAI strict tools require — instead of being refused with a400("Unknown name additionalProperties"). This completes the 2026-10-04 change, which covered structured outputs only. Nothing changes for tools that already worked.
2026-10-06
- The cost on every response is what you are charged.
x-airouter-cost-usdand_odnoga.cost_usdnow report the full charged amount for the call — the model's price plus odnoga's fees (fees only when the call ran on your own vendor key) — the same figure your balance and the Requests page show. Previously they showed the model's price before fees, so totals added up from the header came out slightly low. A newx-airouter-cost-basisheader (and_odnoga.cost_basis) saysbilled; in the rare case the charge cannot be confirmed in time it sayslistand the header shows the model's price only. See Headers. - A call odnoga cannot charge for correctly is stopped before it starts. Before a request is
sent to a model, odnoga now checks that everything the request will use — tokens, an image, a
web search — can be billed for that model. If it cannot, the call is not sent: you get
503 billing_unavailablewith a ticket reference inx-odnoga-ticket, nothing is charged, and the odnoga team is alerted at once. With a fallback chain the next model simply answers. See Errors. - Gemini image models are billed per image. Images from
gemini-*-imagemodels are now counted and charged at the published per-image price.
2026-10-05
- Docs now say exactly what the API does. A pass over the documentation, plan pages and MCP
tool descriptions, correcting every place that promised more (or something different) than you
get:
sk_test_keys are not a sandbox. Test and live keys behave the same: both call the real vendor and both bill your organisation. The environment is a label — keep one key per environment so usage is attributed separately and each can be revoked on its own.- One base URL:
https://api.odnoga.com/v1. Every example now uses it, withODNOGA_API_KEYandODNOGA_BASE_URL. The longer URLs from earlier examples keep working. - Errors lists the codes the gateway actually returns, with their real HTTP
status and
type— including the429limit codes (workspace_concurrency_limit,tenant_concurrency_limit,workspace_rpm_limit,workspace_tpm_limit,capacity_unavailable), the managed-prompt codes, and409/422for idempotency. Codes that were never returned are gone. - Timeouts: the model gets up to 300 seconds whether you stream or not; streaming avoids the
~150-second idle cut-off, it does not extend the deadline.
x-airouter-timeout-ms(now documented) shortens it per request. - Idempotency is documented for non-streamed requests, under
Idempotency-Key,x-odnoga-idempotency-keyorx-airouter-idempotency-key, with thex-airouter-idempotent-replayheader. Streams are not deduplicated. - Headers now cover the requests-per-minute, tokens-per-minute and
organisation session headers,
x-airouter-skipped-modelsandx-airouter-key-source.x-ratelimit-resetis only sent when a budget window exists. Browser callers can now read all of these, and can sendx-odnoga-streamand the idempotency headers. - Plans: the Free plan no longer lists prompts (prompt management starts on Growth, and Growth now says so); the public status page is open to everyone, so it is no longer listed as a Growth feature; Enterprise SSO/SCIM, on-prem/VPC and audit-log export are marked on request.
2026-10-04
- Final-only streaming — beat the ~150-second limit without parsing thousands of frames. Send
x-odnoga-stream: finalwith"stream": trueon/v1/chat/completions. The connection opens at once and stays alive with: keepalivecomments every 15 seconds, so a Supabase Edge Function or any platform with an idle limit never times out waiting on a long reasoning call. When the answer is ready you receive one frame, the completechat.completionwith content, tool calls, usage and the_odnogacost block, then[DONE]. Billing, logging and request ids are exactly the same as a normal call. The response carriesx-odnoga-stream: final. See Streaming → Final-only mode. - Every model capability is now measured, not assumed. We test each capability a model lists
(JSON mode,
json_schemastructured outputs, tools, image input) against the vendor itself, and a model can only be switched on once its capabilities are complete. Corrections from this pass:gpt-6.1-solandclaude-sonnet-4-5now acceptjson_schemastructured outputs; Perplexitysonaracceptsjson_schema; Gemini structured outputs now take the full JSON Schema you send (formats,$ref,additionalProperties) rather than a reduced subset;gpt-6.1-solsupports batch. - Tools on GPT-5.6 / GPT-6 models just work. OpenAI accepts tools on
gpt-5.6-luna,gpt-5.6-solandgpt-6-solonly withreasoning_effort: "none". Leavereasoning_effortout and odnoga sets it for you (reported onx-airouter-set); set a different value and you get a clear400 required_with_toolsinstead of a vendor error. - Output caps work on Gemini.
max_tokens(andmax_completion_tokens) from an OpenAI SDK were not passed on to Gemini models, so a capped call could run to its natural length. They now become Gemini's own output cap, reported onx-airouter-renamed. json_schemastructured outputs work on every model that offers them, including models with no separate JSON mode (Perplexity Sonar). And as with OpenAI, a schema without"strict"is no longer refused for not meeting strict-mode rules: it is sent strict when it can be, non-strict otherwise. An explicit"strict": trueis still checked in full.- Complete parameter rules for
gemini-3.8-flash,grok-4.7andperplexity/low. Parameters these models do not accept are removed and reported onx-airouter-strippedinstead of reaching the vendor;temperature/top_pon Perplexity'sperplexity/lowpreset are among them. - A model that cannot be served says so. Models that a vendor offers only through another API,
or that are awaiting activation, now answer
400 model_not_servingwith a plain explanation instead of a generic503. Nine such models were switched off;models.listlists only models that serve. - Brief capacity limits are retryable. A request that arrives while odnoga is momentarily at
capacity now gets
503 capacity_unavailablewithRetry-After: 2— which every official SDK retries automatically — instead of500 internal. Nothing reaches a vendor and nothing is charged. - Repair headers readable from the browser.
x-airouter-stripped,-clamped,-set,-renamed,x-odnoga-streamandRetry-Afterare now exposed to front-end code. - MCP:
platform.endpointsdocuments keepalive and final-only streaming;platform.error_lookupexplainscapacity_unavailable,model_not_serving,missing_capability,required_with_toolsand tool-forcing refusals.
2026-09-16
-
Tell odnoga when a stop is expected. Some stops are the job finishing rather than the job breaking — a backfill that has drained, a migration that has completed, a feature you retired. Mark the caller expected, from the alert or from the list, and it stops being reported. Not a mute: watching stays on, and by default the expectation lasts only until that caller calls again, so if the job ever runs and stops again you are still told. Give it a number of days instead when you are pausing something on purpose. Through MCP:
traffic.expected.set/.clear. -
Traffic health reads correctly on a new caller, and on longer windows. A caller's normal is the median of its own past windows, and windows from before that caller existed were being counted as silence — which dragged a young caller's normal towards zero and could report a busy new function as having too little traffic to judge, most visibly when comparing over a day rather than six hours. A caller's history now starts at its first call, and each one shows how many windows it has and when it was first seen, so "not judged yet" says when it will be. The last-call time is now the actual last call rather than the start of the window it fell in, and an alert carries the window it was judged at.
-
Traffic health — odnoga tells you when one of your callers stops. Every app and function you send through the gateway is measured against its own normal. If one stops calling, or suddenly calls far more than usual, you are told — with the number that is normal for it, how far off it is now, and, crucially, how many calls odnoga refused in the same window. When that number is zero we say so plainly: every request we received succeeded, so the change is on your side and you know where to look in the first minute. Under Workspace → Functions, organisation-wide under Tenant admin → Traffic health, and through MCP as
traffic.health. Included on Extend and Enterprise; available as an add-on on Growth. -
Tuned so it does not cry wolf: a caller is compared against the median of its own recent windows, must be under the threshold for two windows in a row before anyone is emailed, has a volume floor beneath which it is not judged at all, and a cooldown. You are told once when traffic recovers, so silence never has to be interpreted. Nothing is ever sent while an odnoga incident is open.
-
Concurrent sessions are now a thing you can see, size and buy. Tenant admin → Finance shows your pool, where each session came from (included with your plan, granted by odnoga, or purchased), what every workspace is using, and the busiest moment in the last 30 days — so you can tell "we need more" from "we peak at 5 of 100". Extra sessions go on your existing subscription, prorated; remove them the same way.
-
Going over your session pool no longer breaks your product. By default the call is served and the response carries
x-odnoga-pool-exceededplus a readableWarningheader; nothing extra is charged. Your admins see it counted per day next to the smallest purchase that would have covered it. If you would rather be refused than served over your ceiling, ask support and we will enforce it for you. -
A new error code,
capacity_unavailable: odnoga itself is at capacity, not you. Retry after theRetry-After; nothing on your side will fix it and nothing you buy will either. -
Batch traffic is now attributed. Jobs submitted with
x-odnoga-app/x-odnoga-functioncarry that identity onto every request they produce, so batch work shows up in the functions list, in the request-log filters and in traffic health instead of pooling into "unattributed". -
New doc: Concurrent sessions — what a session is, the two ceilings and which one bit, the 429 contract, and how to size a worker pool. Headers reference updated.
2026-08-21
- Response shape is now a workspace choice: OpenAI-translated (default) or the raw vendor body, with a tenant-level default and lock. Every response carries
x-airouter-response-format(openai|native); streamed responses are always OpenAI-shaped. Configure under Workspace → Models → Response format. - Token headers are the billing record: every non-streamed response carries
x-airouter-prompt-tokens/x-airouter-completion-tokens/x-airouter-total-tokensalongsidex-airouter-cost-usd— read usage from these, never by parsing the body (whose shape is now a setting). - Vendor batch routing:
/v1/batchesjobs are handed to the vendor's own Batch API at a genuine 50% discount — OpenAI, Anthropic and Google live. Honest billing semantics:billing_tieris what you are actually charged,fallback_reasonsays why a job lost the vendor route,fault+odnoga_noteread the vendor's verdict for you, and per itembilled_tierin the results is authoritative. - New Batch jobs page (Workspace → Batch jobs) showing status, execution mode, billed tier and fallback reason per job.
_odnogaecho hardened: addsvendor_model(the dated snapshot the vendor reports serving — provenance, never a pricing key). End-user token (eut_) responses are redacted: cost basis and vendor identity never reach your end-users.- Prepaid-always billing: usage is always debited from prepaid credits at your plan's margin (Free 6%, Growth 5%, Extend 4%); paid plans ($5/mo Growth, $49/mo Extend) are the base fee only and buy the lower margin. A lapsed base fee keeps plan rates for a 2-day grace window, then reverts to Free rates — credits never freeze.
- Docs: headers reference, concepts, streaming and cost-control updated for all of the above.
- xAI batch is live at the vendor's real 20% (grok-4.3 and the grok-4.20 family only — the discount is model-gated by xAI, not by odnoga; other models fall back to standard with the reason on the record). grok-4.3 (1M context) joined the catalogue.
- New pricing API —
GET /v1/pricing: the price list your workspace pays, margins included, with per-vendor service-tier multipliers and region uplifts. End-user tokens are refused; end users buy at your published plans.
2026-05-30
- Docs rewrite: full restructure into Quickstart / Concepts / Best practices / SDKs / Ops / Headers / Errors / Troubleshooting.
- New in-app Docs route at
/workspace/:slug/odnoga/docs. - Promoted
x-airouter-end-userto a first-class step. - Added
callodnoga()shared-helper pattern. - Added per-SDK cookbooks for OpenAI Node/Python, Anthropic, LangChain, Vercel AI SDK,
@odnoga/node.