Chat completions
Create OpenAI-compatible chat completions, with optional Inferada processors, routing controls, and operational metadata.
Overview
Send an OpenAI Chat Completions request to /v1/chat/completions. Add an optional inferada object when the request needs processors or gateway-specific routing. Standard response fields remain unchanged. Operational metadata appears under the top-level inferada object, while billing metadata appears under usage.inferada. Unknown OpenAI request fields pass through to the model.
Compatibility and Inferada extensions
Inferada additions do not replace standard fields. Clients that ignore unknown JSON fields can continue to use the OpenAI-compatible response. Chat Completions and Responses place Inferada metadata in the same locations for buffered responses and terminal stream events.
inferadaobject- Top-level operational metadata: request_id, timings, routing, processors, tools, buffering and guard outcomes.
usage.inferadaobject- Billing-only metadata. Currently contains cost_eur; operational fields never appear here.
x-request-idheader- Stable request correlation for every transport, including binary audio and file responses.
The @inferada-ai/ai-sdk-provider package maps these additions to providerMetadata.inferada in the Vercel AI SDK. Use its .passthrough provider when application code should receive only standard AI SDK result fields. The raw client also exposes client.passthrough, which removes Inferada metadata from OpenAI-shaped JSON and SSE data. Transport headers remain available in both modes.
import { createInferada } from '@inferada-ai/ai-sdk-provider'
import { generateText } from 'ai'
const inferada = createInferada({ apiKey: process.env.INFERADA_API_KEY })
// Enriched: result.providerMetadata?.inferada
const enriched = await generateText({ model: inferada('your-model-id'), prompt: 'Hello' })
// Clean AI SDK result: no Inferada metadata extraction
const clean = await generateText({ model: inferada.passthrough('your-model-id'), prompt: 'Hello' })Create a chat completion
/v1/chat/completionsCreate a buffered response or stream incremental output with Server-Sent Events (SSE).
Request
modelrequiredstring- ID of the model to use. Use /v1/models to discover available ones.
messagesrequiredarray- Array of messages with role (system / user / assistant / tool) and content. The
systemrole is rendered server-side per the model's configuredtemplate_render_mode. See Prompt templating. streamboolean- Set to true to receive incremental SSE chunks. Defaults to false. A processor that needs the complete response can force buffering.
temperaturenumber- Sampling temperature. Standard OpenAI semantics.
top_pnumber- Nucleus sampling.
max_tokensnumber- Max tokens to generate in the completion.
stopstring | string[]- Stop sequences.
frequency_penaltynumber- Standard OpenAI parameter.
presence_penaltynumber- Standard OpenAI parameter.
toolsarray- Tool / function definitions the model can call.
tool_choicestring | object- How the model picks tools.
response_formatobject- E.g. { type: "json_object" } for structured output.
inferadaobject- Inferada's extension namespace. It carries
processors: string[], an optionallanguagestring, and processor-specific config. See the Processors guide for the catalogue and per-processor shape. inferada.haystackobject- Caller-side structured context:
{ data, schema?, format?, redact? }. Activate by including"haystack"ininferada.processors. See the Haystack guide for the field-def shape and the published meta-schema. inferada.system_modestring"merge_default"(default) or"replace_default". Controls whether the model's configured default system prompt is merged into the assembled upstream system prompt or skipped. See the system-prompt assembly guide.
{
"model": "qwen3.5-35b",
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "Hello!" }
],
"stream": false,
"temperature": 0.7,
"max_tokens": 1000
}Response · 200
Buffered response. The standard OpenAI fields are preserved. Operational additions live under inferada, billing additions under usage.inferada, and timings remains as a transitional legacy alias.
idstring- Completion ID. Derived from the request correlation ID.
choicesarray- Array of completion choices (OpenAI shape).
usageobject- Standard token counts. usage.inferada is a billing-only extension containing cost_eur when pricing is available.
timingsobject- Transitional alias of inferada.timings. New integrations should use the namespaced field.
inferadaobject- Operational metadata. Always includes
request_id; may includetimings,routing, processor summaries, tool summaries, and guard outcomes. See the Processors guide.
{
"id": "chatcmpl-550e8400-e29b-41d4-a716-446655440000",
"object": "chat.completion",
"created": 1704900000,
"model": "qwen3.5-35b",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 8,
"total_tokens": 33,
"inferada": {
"cost_eur": 0.000014
}
},
"timings": {
"total_ms": 870,
"upstream_ms": 820,
"tokens_per_second": 38.5
},
"inferada": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timings": {
"total_ms": 870,
"upstream_ms": 820,
"tokens_per_second": 38.5
}
}
}Errors
Possible errors include 400 for an invalid body or unknown processor,401 for an invalid token, 403 for a scope or IP restriction,404 for an unavailable model, 422 for a processor policy rejection or missing or unsupported inferada.language; see Processors), 429 for a rate or concurrency limit, 500 for an internal gateway or processor failure, and 502 for an upstream failure. See Errors and Rate limits.
Streaming
With stream: true, the endpoint responds with Server-Sent Events and Content-Type: text/event-stream. Each chunk is an OpenAI-compatible delta frame, and [DONE] terminates the stream. The final success or error chunk includes top-level inferada metadata even when stream_options.include_usage is false. When usage is requested, a separate chunk with an empty choices array contains token counts and usage.inferada.
Streaming uses an idle timeout that resets whenever a chunk arrives. If the upstream produces no chunk within that window, the gateway aborts the stream with a service-failure error. Buffered requests use a fixed request deadline.
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"inferada":{"request_id":"...","timings":{"upstream_ms":820}}}
data: [DONE]Some processors require the full response, so the gateway disables streaming for that request. See the Processors guide for details.
Processors
Processors add redaction, output correction, policy checks, or routing controls to a completion. See the Processors guide.
Tools
The gateway supports the OpenAI tools and tool_choice request fields. When you send tool definitions, the gateway forwards them to the model verbatim, streams tool_calls deltas back on the wire, and stays out of the execution loop: your client runs the tool and sends the result in a follow-up turn. When the request does not define tools, the model can use the catalogue tools attached by your organisation, which the gateway executes. See the Tools guide.
Timings
Every buffered completion includes the latency breakdown at inferada.timings. The same value is temporarily mirrored at top-level timings for existing integrations:
total_msnumber- End-to-end latency, including queue time and gateway processing.
upstream_msnumber- Time spent in the upstream model request.
tokens_per_secondnumber- Completion-token throughput computed from upstream_ms. Present only when completion tokens are greater than zero.
processors_msnumber- Measured aggregate of pre + post processor time. Only present when processors ran. See Processors.
queued_msnumber- Time spent waiting for a concurrency slot before processing began. Present only when greater than zero. A non-zero value indicates contention under a max_concurrent rule. Streaming responses expose the same value in the
x-inferada-queued-msheader. gateway_msnumber- Residual time spent in the gateway itself (validation, serialization, accounting writes). Only present when non-zero.
When the selected upstream is vLLM, its numeric performance metrics are preserved in the same object. This includes time_to_first_token_ms, generation_time_ms, and mean_itl_ms. To keep provider timings distinct from gateway timings, vLLM's queue_time_ms is exposed as upstream_queue_ms and its tokens_per_second as upstream_tokens_per_second. Standard OpenAI usage token counts and system_fingerprint remain in their standard top-level locations.
Model aliases
An organisation administrator can create a stable alias for one or more model IDs. For example, send model: "chat" instead of a version-specific model ID. The alias can later point to a different model without requiring a client change.
- Each alias maps to an ordered chain of real model_ids. The resolver selects the first active entry permitted by the token's
allowed_modelsrestriction. - The gateway also tries the next eligible target when dispatch fails before a response is committed because of connectivity, timeout, throttling, upstream 5xx, or upstream 404. Caller-invalid and authentication responses such as 400, 401, 403, and 422 are terminal.
- Content processors run once and remain in force across attempts. A fallback target is eligible only when its effective processor policy matches the policy already executed; explicit request processor settings apply to every target. Route policies such as
require_localare evaluated against every concrete candidate before any upstream request is sent. - The standard response
modelremains the requested alias. Inspectinferada.routingforresolved_model,fallback_used,dispatch_attempts, and the optionalfallback_reason. - Once streaming output is visible, or a gateway-executed tool has produced effects, the route is fixed and no transparent retry occurs.
- Real model_ids always win over aliases: adding an alias never shadows an existing model.
- Aliases are discoverable via
/v1/models: each model entry carries analiasesarray listing every alias key that routes to it. - When an administrator sunsets a model, its database identity and historical references are retained. Existing aliases promote their next active target; the old concrete model ID can also be retained as an alias to a chosen replacement.
- Aliases are scoped per service. The completions service's
chatand the TTS service'schatwould be independent.
List available models
/v1/modelsList chat and embedding models available to the token. The OpenAI-compatible response includes additional display and service metadata.
Request
Optional query parameters:
scopestring- Narrow to one model scope: "completions" (chat) or "embeddings". Omit to list both. An unrecognised scope returns an empty list.
service_idstring- Narrow to a single service (see GET /v1/services). Omit to span every service.
# All models
curl https://api.inferada.com/v1/models -H "Authorization: Bearer inf_YOUR_TOKEN"
# Only embedding models
curl "https://api.inferada.com/v1/models?scope=embeddings" -H "Authorization: Bearer inf_YOUR_TOKEN"Response · 200
Each model includes the standard fields plus additional metadata (scope, display_name, description, aliases) and an additive inferada object identifying the owning service. OpenAI clients that ignore unknown response fields remain compatible.
{
"object": "list",
"data": [
{
"id": "qwen3.5-35b",
"object": "model",
"created": 1704900000,
"owned_by": "inferada",
"scope": "completions",
"display_name": "Qwen 3.5 35B",
"description": "General-purpose chat model with 128K context.",
"aliases": ["chat", "smart"],
"inferada": {
"service_id": "01J6A...",
"service_name": "LLM vLLM",
"service_is_default": false,
"processing": "local",
"pricing": {
"currency": "EUR",
"input_per_mtok": 0.4,
"cached_input_per_mtok": 0.1,
"output_per_mtok": 1.2
},
"default_processors": ["anonymise"],
"default_language": "nl",
"tools": { "gateway": ["web_search"], "mcp_servers": [] },
"processor_languages": { "anonymise": ["en", "nl"], "haystack": ["en", "nl"] }
}
}
]
}inferada.processing"local" | "remote" | null- Where the model runs: on infrastructure your organisation controls, or at an external provider. For chat models this is exactly what require_local checks; null means the upstream is not classified, which require_local treats as not local.
inferada.pricingobject | null- List prices in EUR per million tokens (input, cached input, output), before allowances. Image models use the same fields. null when usage on this model is not billed, and for image models where billed requests are refused because the upstream is not local.
inferada.default_processorsstring[]- Chat models only (empty elsewhere): processors that run when a request sends no processors. A request that sends inferada.processors replaces this list, so include these if you still want them.
inferada.default_languagestring | null- Chat models only: the inferada.language applied when a request sends none.
inferada.toolsobject- Chat models only: tools the gateway executes for this model (gateway tool names) and the MCP servers whose tools it adds.
inferada.processor_languagesobject- Chat models only: for each processor that needs inferada.language (anonymise, haystack, spellcheck), the languages your organisation supports. A processor is absent when your organisation has no service for it.
The scope field tells a chat model apart from an embedding model. The aliases array lists every alias key that resolves to this model (whether as primary or fallback). See Model aliases. If your token has an allowed_models restriction, this list is filtered accordingly.
Retrieve a model
/v1/models/{model}Retrieve one model by ID or alias. Returns the same shape as the list endpoint, or 404 when the model is unavailable to the token. Retrieved by alias, the response describes the model the alias tries first and adds inferada.alias: the models it falls back through, in order, with where each runs. The service is chosen the way a request would choose it (the service_id query parameter, else the default service); when the alias is ambiguous across services, inferada.alias is left out.
Response · 200
{
"id": "qwen3.5-35b",
"object": "model",
"created": 1704900000,
"owned_by": "inferada",
"scope": "completions",
"display_name": "Qwen 3.5 35B",
"description": "General-purpose chat model with 128K context.",
"aliases": ["chat", "smart"],
"inferada": {
"service_id": "01J6A...",
"processing": "local",
"alias": {
"name": "chat",
"models": [
{ "id": "qwen3.5-35b", "processing": "local" },
{ "id": "qwen3.5-35b-cloud", "processing": "remote" }
]
}
}
}Abbreviated: an alias response carries every field of the list entry. With require_local, a request through the alias only uses the local models in this chain.
Attach models in the portal
Models are configured per completion service. From Organisation → Models, an organisation administrator can use either workflow:
- Add model opens the complete form for one model. Select a template and upstream, enter
model_id, and configure any overrides, tools, or limits. - Add from templates attaches several global or organisation templates to one upstream. Each model uses the template name as its address. For a profile, that address is
upstream_model_id:profile_name, e.g.gpt-4o:code), and inherited defaults resolve from the template at request time. The picker disables a template whose name is already attached to the selected service. Use Clone on the existing model to create a variant.
Both flows enforce one model_id per service, and a new model is callable as soon as the save completes.