Skip to content

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.

headers
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
EndpointPurpose
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

POST/v1/chat/completions

The core endpoint, accepting the OpenAI Chat Completions schema. The model is a unoblox slug (author/model) or a router alias.

request
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
}
ParameterTypeNotes
modelstringRequired. Model slug or router alias (unoblox/auto, …).
messagesarrayRequired. Chat messages (system / user / assistant / tool).
streambooleanServer-sent events when true.
max_tokens / max_completion_tokensintegerCap 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):

ParameterTypeNotes
modelsstring[]Ordered fallback chain — try these if the primary can't be served.
providerobjectProvider preferences: order, only/allow, ignore, sort, region, data_collection, zdr, …
transformsstring[]["middle-out"] compresses an over-long prompt to fit context.
presetstringApply a saved preset (or set model to "@preset/slug").
unoblox_session_idstringAffinity key for provider stickiness across a session.
require_capabilitiesstring[]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.

FieldWhat happens
service_tierForwarded, 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_optionsNot 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, deferredNot 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:

response
{
  "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

POST/v1/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.

request
POST /v1/responses
{
  "model": "<model-id>",
  "instructions": "You are concise.",
  "input": "Summarize GST in one line.",
  "max_output_tokens": 256
}
ParameterTypeNotes
modelstringRequired. Model slug or router alias.
inputstring | arrayThe prompt: a bare string, or an array of input items.
instructionsstringSystem instructions.
max_output_tokensintegerCap on output tokens.
text.formatobjectStructured-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.

response
{
  "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

POST/v1/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).

request
POST /v1/messages
{
  "model": "<model-id>",
  "max_tokens": 256,
  "system": "You are concise.",
  "messages": [{ "role": "user", "content": "Summarize GST in one line." }]
}
ParameterTypeNotes
modelstringRequired. Model slug or router alias.
max_tokensintegerRequired. Cap on output tokens.
messagesarrayRequired. user / assistant turns.
systemstring | arraySystem prompt.
streambooleanServer-sent events when true.
stop_sequencesstring[]Stop strings (maps to stop).
temperature, top_p, top_k, tools, tool_choice—Sampling and tools, forwarded.
thinkingobjectenabled/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.

response
{
  "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

GET/v1/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.

response
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.

EndpointPurpose
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:

HeaderMeaning
x-titleYour application name (up to 96 characters). Shown as the app label in rankings.
HTTP-RefererYour 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:

HeaderMeaning
x-generation-idUnique id for the request. Use it for lookups and support.
x-unoblox-cacheHIT / MISS / BYPASS — the cache outcome.
x-unoblox-cache-reasonWhy: 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-generationOn a HIT, the generation id whose stored response is being replayed.
x-unoblox-selected-modelThe concrete model chosen (when a router alias was used).
x-unoblox-selector / -selection-reasonWhich selector picked it, and why.
x-unoblox-variant-notePresent when a variant degraded gracefully.
x-unoblox-presetThe applied preset (scope/slug), when one matched.
x-unoblox-unsupported-fieldsComma-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 envelope
{ "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:

GET/workspaces/:id/credits/balance
GET/workspaces/:id/usage/activity
GET/workspaces/:id/usage/activity/export
POST/workspaces/:id/analytics/query
GET/workspaces/:id/analytics/meta
GET/workspaces/:id/presets
GET/generation?id=:request_id

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/workspaces/:id/keys
GET/workspaces/:id/byok

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.