Reference
API reference
unoblox exposes an OpenAI-compatible inference API on the gateway, in three inbound shapes over one routing engine, plus a separate control-plane API behind the dashboard. The base URL for inference is https://api.unoblox.ai/v1.
Base URL & authentication
Every request authenticates with your ub-gw-… key as a bearer token. Model ids are author/model slugs — browse the live, always-current set at /models rather than hard-coding one from a sample.
POST https://api.unoblox.ai/v1/chat/completions
Authorization: Bearer ub-gw-user_xxx-xxxxxxxxxxxx
Content-Type: application/json
Idempotency-Key: 6f0e-... # optional; identifies this exact request — see Idempotency below| Endpoint | Purpose |
|---|---|
POST/v1/chat/completions | OpenAI Chat Completions — the native endpoint. |
POST/v1/responses | OpenAI Responses API shape. |
POST/v1/messages | Anthropic Messages API shape. |
GET/v1/models | List the live catalog — unauthenticated, not scoped to any key. |
Only model and a non-empty messages array are required.
Beyond those two, most standard OpenAI/Anthropic fields are forwarded to the model unchanged — but several are validated, rejected, or stripped before the upstream call rather than forwarded silently: unsupported tool types, non-file-parser plugins, the :online/:batch model suffixes, and aprovider.sort.partition directive each get a 400 rather than being passed through; unoblox’s own routing fields (provider, models, transforms, preset, …) are stripped before the request leaves the gateway. See each skin’s section below for what it specifically does not accept.
Chat Completions
The core endpoint, accepting the OpenAI Chat Completions schema. The model is a unoblox slug (author/model) or a router alias.
POST /v1/chat/completions
{
"model": "<model-id>",
"messages": [
{ "role": "system", "content": "You are concise." },
{ "role": "user", "content": "Summarize GST in one line." }
],
"temperature": 0.7,
"max_tokens": 256,
"stream": false
}| Parameter | Type | Notes |
|---|---|---|
| model | string | Required. Model slug or router alias (unoblox/auto, …). |
| messages | array | Required. Chat messages (system / user / assistant / tool). |
| stream | boolean | Server-sent events when true. |
| max_tokens / max_completion_tokens | integer | Cap on output tokens, forwarded to the provider exactly as you send it (max_completion_tokens wins if both are sent). unoblox never adds, lowers or defaults it; if you omit it, none is sent. |
| temperature, top_p, stop, seed, tools, tool_choice, response_format, … | — | Standard OpenAI fields, forwarded to the model. |
unoblox extensions (all optional):
| Parameter | Type | Notes |
|---|---|---|
| models | string[] | Ordered fallback chain — try these if the primary can't be served. |
| provider | object | Provider preferences: order, only/allow, ignore, sort, region, data_collection, zdr, … |
| transforms | string[] | ["middle-out"] compresses an over-long prompt to fit context. |
| preset | string | Apply a saved preset (or set model to "@preset/slug"). |
| unoblox_session_id | string | Affinity key for provider stickiness across a session. |
| require_capabilities | string[] | Require named capabilities of the serving endpoint. |
Routing extensions are documented in Routing. They are consumed by the gateway and stripped before the upstream call.
Fields that change what the provider charges:
unoblox only forwards a request field that changes the provider's price when that price is on our price list, so you are always billed exactly what the provider charges (plus our 5% fee) and never something we cannot price. A field we cannot price is not sent to the provider; the request is still answered normally, and the response lists the field in the x-unoblox-unsupported-fields header.
| Field | What happens |
|---|---|
| service_tier | Forwarded, and billed at the provider's published tier price (for example priority or flex), when that model's price list includes the tier. Otherwise the request runs at the standard tier and service_tier is listed in x-unoblox-unsupported-fields. You are billed for the tier the provider reports it served. |
| web_search_options | Not forwarded (provider web search is billed per search and no listed model prices it). Listed in x-unoblox-unsupported-fields. |
| audio, modalities: ["audio"] | Audio output is not forwarded (no listed model prices audio output); text output is unaffected. Listed in x-unoblox-unsupported-fields. |
| search_parameters, deferred | Not forwarded (per-source search fees / deferred completions). Listed in x-unoblox-unsupported-fields. |
If a stream ends early (you cancel it, or the provider fails mid-way), you are billed only what the provider charged for the work it did on that request — nothing if it charged nothing.
Response — the provider's completion, forwarded verbatim:
{
"id": "chatcmpl-9f2a41",
"object": "chat.completion",
"created": 1756800000,
"model": "<model-id>",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "GST is India's unified indirect tax on goods and services." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 24, "completion_tokens": 12, "total_tokens": 36 }
}With stream: true the response is a text/event-stream of chat.completion.chunk frames ending in data: [DONE] — see Streaming.
Responses
The OpenAI Responses API shape, translated onto the same engine. Use instructions for the system prompt and input (a string or an item array) for the turn.
POST /v1/responses
{
"model": "<model-id>",
"instructions": "You are concise.",
"input": "Summarize GST in one line.",
"max_output_tokens": 256
}| Parameter | Type | Notes |
|---|---|---|
| model | string | Required. Model slug or router alias. |
| input | string | array | The prompt: a bare string, or an array of input items. |
| instructions | string | System instructions. |
| max_output_tokens | integer | Cap on output tokens. |
| text.format | object | Structured-output format (maps to response_format). |
| tools, tool_choice, parallel_tool_calls | — | Tool calling, forwarded. |
| temperature, top_p, top_logprobs, seed | — | Sampling, forwarded. |
Not accepted — each gets a 400 rather than being silently ignored: previous_response_id, conversation (unless null), store / background (unless false), context_management (unless an empty array), and an input item of type item_reference.
{
"id": "resp_9f2a41",
"object": "response",
"created_at": 1756800000,
"status": "completed",
"model": "<model-id>",
"output": [
{
"type": "message",
"id": "msg_1",
"status": "completed",
"role": "assistant",
"content": [{ "type": "output_text", "text": "GST is …", "annotations": [] }]
}
],
"output_text": "GST is …",
"usage": {
"input_tokens": 24,
"input_tokens_details": { "cached_tokens": 0 },
"output_tokens": 12,
"output_tokens_details": { "reasoning_tokens": 0 },
"total_tokens": 36
}
}Streaming emits typed events in order: response.created → response.output_text.delta (repeated) → response.completed, each with an incrementing sequence_number.
Messages
The Anthropic Messages API shape. max_tokens is required (as in the Anthropic spec), and system is a top-level field. Authenticate with either Authorization: Bearer or x-api-key; the standard anthropic-version header is accepted and ignored (see Migration).
POST /v1/messages
{
"model": "<model-id>",
"max_tokens": 256,
"system": "You are concise.",
"messages": [{ "role": "user", "content": "Summarize GST in one line." }]
}| Parameter | Type | Notes |
|---|---|---|
| model | string | Required. Model slug or router alias. |
| max_tokens | integer | Required. Cap on output tokens. |
| messages | array | Required. user / assistant turns. |
| system | string | array | System prompt. |
| stream | boolean | Server-sent events when true. |
| stop_sequences | string[] | Stop strings (maps to stop). |
| temperature, top_p, top_k, tools, tool_choice | — | Sampling and tools, forwarded. |
| thinking | object | enabled/disabled thinking control. Enabled requests can return thinking blocks when the selected endpoint supports it. |
Not accepted — each gets a 400 rather than being silently ignored: context_management, container, service_tier, speed, and metadata.
{
"id": "msg_9f2a41",
"type": "message",
"role": "assistant",
"model": "<model-id>",
"content": [{ "type": "text", "text": "GST is …" }],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 24,
"output_tokens": 12,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0
}
}Streaming emits Anthropic events: message_start → content_block_delta (repeated) → message_delta → message_stop.
List models
The live catalog, in OpenAI list form. This endpoint is unauthenticated and returns the same list to everyone — it is not filtered by any key’s model_scope. Full field reference is in Models.
GET /v1/models →
{
"object": "list",
"data": [
{ "id": "<model-id>", "object": "model", "owned_by": "<author>", "name": "<Model name>", "context_length": 128000, "freemium": false }
]
}Embeddings, rerank, batches & files
Four additional surfaces beyond chat/responses/messages, each gated by its own admin-configured enable flag — when disabled, every route below answers 503 provider_unavailable rather than 404, so a caller can tell “not enabled here” from “doesn’t exist”.
Embeddings and rerank are temporarily offline while their models are re-provisioned — routes below still answer 503 provider_unavailable. Batches and files are unaffected.
| Endpoint | Purpose |
|---|---|
POST/v1/embeddings | Create embeddings for one or more inputs. |
GET/v1/embeddings/models | List models that serve embeddings. |
POST/v1/rerank | Rerank a list of documents against a query. |
GET/v1/rerank/models | List models that serve rerank. |
POST/v1/batches | Submit an asynchronous batch of requests. |
GET/v1/batches | List your batches. |
GET/v1/batches/:id | Read one batch's status/results. |
POST/v1/batches/:id/cancel | Cancel a batch that hasn't finished. |
POST/v1/files | Upload a file for a subsequent request to reference. |
GET/v1/files | List your uploaded files. |
GET/v1/files/:id | Read one file's metadata. |
GET/v1/files/:id/content | Download a file's content. |
DELETE/v1/files/:id | Delete an uploaded file. |
Request headers (app attribution)
Optionally identify your application so it appears in public rankings and per-app usage analytics. At most one name source is used per request:
| Header | Meaning |
|---|---|
| x-title | Your application name (up to 96 characters). Shown as the app label in rankings. |
| HTTP-Referer | Your application URL (up to 128 characters; only the origin is kept). |
If your application identifies itself with the x-title or HTTP-Referer request headers, unoblox may show that application’s name and its aggregate usage by model in public rankings. unoblox never publishes usage by individual user, workspace or API key. To stay out of public rankings, do not send these headers.
Response headers
Every inference response carries diagnostic headers:
| Header | Meaning |
|---|---|
| x-generation-id | Unique id for the request. Use it for lookups and support. |
| x-unoblox-cache | HIT / MISS / BYPASS — the cache outcome. |
| x-unoblox-cache-reason | Why: hit / miss on a HIT/MISS; on a BYPASS one of not_opted_in, no_store, stream, non_zero_temperature, sampling_knob, tool_use, provider_pin, file_input, embeddings_no_retention, or rerank_no_retention. See Response caching for what not_opted_in means. |
| x-unoblox-cache-source-generation | On a HIT, the generation id whose stored response is being replayed. |
| x-unoblox-selected-model | The concrete model chosen (when a router alias was used). |
| x-unoblox-selector / -selection-reason | Which selector picked it, and why. |
| x-unoblox-variant-note | Present when a variant degraded gracefully. |
| x-unoblox-preset | The applied preset (scope/slug), when one matched. |
| x-unoblox-unsupported-fields | Comma-separated request fields that were accepted but not applied (see Fields that change what the provider charges). Absent when every field was applied. |
Errors
Every failure returns a typed JSON envelope and a standard HTTP status. Branch on the type string:
{ "error": { "message": "…", "type": "insufficient_balance", "code": 402, "request_id": "7b679c45-…", "error_code": "UB-GW-…" } }The full taxonomy — every type, its status, and whether to retry — is in Errors.
Control plane
A separate API behind the dashboard — not the inference host above. It is reached through the web app itself, at https://unoblox.ai/api/webapi and https://unoblox.ai/api/identity, and (with one exception below) authenticates with the dashboard’s ubs_… session cookie, not your inference key.
On webapi:
A second exception to the session-cookie rule above: credits/balance, usage/activity/export and analytics/query/meta also accept a ub-mgmt-… management-scope API key as a bearer token, scoped to its own workspace only — see the Analytics API.
On identity:
GET /key (identity) is the one exception: an inference key introspects its own limits and scope, authenticated by the key itself (as a bearer token), not the session cookie.
GET /models and GET /providers on webapi are session-gated catalog mirrors for the dashboard — not the public catalog. The actual anonymous, read-only catalog is a distinct set of routes: GET /public/models, GET /public/models/:slug, GET /public/providers, and GET /public/site-config — what /models and /providers themselves call.
OpenAI-compatible on purpose.
Because the schema matches, existing tools — the OpenAI SDKs, LangChain, LlamaIndex — work by changing only the base URL and key. See Migration.