Completions
API reference for the chat completions endpoint
Creates a model response for the given chat conversation. Compatible with any OpenAI SDK. Sansa can route each request to the best model ("sansa-auto") or call a specific model directly; see Models.
POST /v1/chat/completions
See the code panel for request and response examples.
Body Parameters
On sansa-auto, some fields return 400; see What sansa-auto accepts.
messages
Required. An array of messages comprising the conversation so far. Sansa supports text, image, and audio inputs. Messages can include image_url (URL or base64) and input_audio (base64) content parts. On sansa-auto, they go to a model that can read them.
To transcribe a standalone audio file, use the Transcriptions endpoint instead.
interface Message {
// One of "system", "user", "assistant", "tool".
// The legacy "developer" role is also accepted and treated as "system".
role: "system" | "user" | "assistant" | "tool";
// The text content of the message.
// Can be a plain string or an array of content parts.
// Set to null on assistant messages that contain tool_calls.
content: string | ContentPart[] | null;
// An array of tool calls the model wants to make.
// Present when the model decides to call tools instead of generating text.
tool_calls?: ToolCall[];
// Must match an id from a prior tool_calls entry.
// Required for tool messages.
tool_call_id?: string;
// The model's reasoning text (assistant messages only).
reasoning?: string;
// Structured reasoning blocks (assistant messages only).
// When echoing back assistant messages, pass these unmodified
// to maintain reasoning continuity. See the Reasoning docs.
reasoning_details?: ReasoningDetail[];
}
interface ContentPart {
type: "text" | "image_url" | "input_audio";
// For type "text".
text?: string;
// For type "image_url". Accepts a public URL or a base64 data URI.
// data URI format: "data:<mime>;base64,<data>"
image_url?: {
url: string;
};
// For type "input_audio". Base64-encoded audio bytes.
input_audio?: {
// Base64-encoded audio bytes.
data: string;
// Audio format: "mp3", "wav", "m4a", "webm", "ogg", "flac", or "opus".
format: string;
};
}model
Optional. Controls how the request is dispatched.
"sansa-auto",null, or omitted: the router picks the model. The response'smodelis the model that was used.- A catalog model ID (e.g.
"openai/gpt-5.4","anthropic/claude-sonnet-4.6"): calls that model directly. - An unknown ID:
400 invalid_model.
Model IDs are listed on the Models page. See Models for how each mode works.
stream
Default: false. If true, the response is delivered as Server-Sent Events (SSE), compatible with OpenAI SDK streaming. See Streaming.
temperature
Optional. Sampling temperature between 0 and 2. Higher values produce more random output; lower values produce more deterministic output. When omitted, the model's default applies.
max_completion_tokens
Optional. Upper bound on the number of tokens that can be generated for a completion, including visible output tokens and reasoning tokens. Must be at least 1 if provided. Takes precedence over max_tokens.
max_tokens
Optional. Legacy alias for max_completion_tokens, used when that isn't set.
tools
Optional. A list of tools the model may call.
interface Tool {
type: "function";
function: {
name: string;
description?: string;
// A JSON Schema object
parameters: object;
};
}tool_choice
Default: "auto". Controls which tool (if any) the model calls.
// Model decides whether to call a tool or generate text.
"auto"
// Model will not call any tool.
"none"
// Model must call at least one tool.
"required"
// Forces the model to call the named function.
{ type: "function", function: { name: "my_function" } }parallel_tool_calls
Default: true. Whether to allow the model to make multiple tool calls in a single response.
response_format
Optional. Forces the model to produce output in a specific format.
// Default. Unstructured text output.
{ type: "text" }
// Model output is guaranteed to be valid JSON.
{ type: "json_object" }
// Structured outputs. The response conforms to the provided JSON Schema.
{
type: "json_schema",
json_schema: {
name: "...",
strict: true,
schema: { ... }
}
}reasoning
Optional. Reasoning (extended thinking) configuration.
{
// One of "xhigh", "high", "medium", "low", "minimal", "none".
effort: "medium",
// Token budget for reasoning, instead of effort.
max_tokens: 1000,
// If true, reasoning is used internally but not returned in the response.
exclude: false,
// Optional. false disables reasoning.
enabled: true
}If both reasoning and reasoning_effort are provided, reasoning takes precedence.
On sansa-auto, omitting both defaults to effort: "high"; see Smart Routing.
reasoning_effort
Optional. Top-level OpenAI-style reasoning effort. Accepted values: "xhigh", "high", "medium", "low", "minimal", "none".
Equivalent to reasoning: { effort: ... }; reasoning wins if both are sent.
top_p
Optional. Nucleus sampling. Value between 0 and 1. The model considers only the tokens comprising the top top_p probability mass.
stop
Optional. Up to 4 sequences where the model will stop generating further tokens. The returned text will not contain the stop sequence.
frequency_penalty
Optional. Number between -2.0 and 2.0. Positive values penalize tokens based on their existing frequency in the text so far, reducing repetition.
presence_penalty
Optional. Number between -2.0 and 2.0. Positive values penalize tokens based on whether they have already appeared in the text, encouraging the model to cover new topics.
metadata
Optional. A set of up to 16 key-value pairs attached to the request. Keys must be ≤ 64 characters; values must be ≤ 512 characters.
Use metadata["call_name"] to tag a request with a label. Tagged requests can be filtered in your dashboard.
call_namemust be≤ 64characters and cannot be empty or whitespace-only.- Invalid
call_namevalues return400with codeinvalid_call_name.
{
"call_name": "summarize-email"
}Currently, call_name is the only metadata key that Sansa reads. Other keys are accepted for OpenAI SDK compatibility but are not stored.
session_id
Optional. Keeps a sansa-auto conversation on one model so its prompt cache stays warm (max 256 characters), like prompt_cache_key. Also accepted as the x-session-id header; the body wins if both are sent. See Prompt Caching.
Returns
A ChatCompletion object.
interface ChatCompletion {
// Unique identifier for the completion.
id: string;
// Always "chat.completion".
object: "chat.completion";
// Unix timestamp of when the completion was created.
created: number;
// The model that actually served the request.
// For sansa-auto requests, this is the routed model.
// For direct model requests, this is the model you requested.
model: string;
// Completion choices. Always length 1.
choices: {
// Always 0.
index: number;
message: {
// Always "assistant".
role: "assistant";
// Text content. null when tool_calls is present.
content: string | null;
// Tool calls the model wants to make, if any.
tool_calls: ToolCall[] | null;
// Reasoning text, if reasoning was enabled and not excluded.
reasoning: string | null;
// Structured reasoning blocks.
reasoning_details: ReasoningDetail[] | null;
};
// Reason why the generation stopped.
// "stop": Natural completion or stop sequence hit.
// "length": Hit max_tokens / max_completion_tokens limit.
// "tool_calls": Model wants to call one or more tools.
// "content_filter": Content was filtered.
// "error": Error during generation.
finish_reason: "stop" | "length" | "tool_calls" | "content_filter" | "error";
}[];
// Token usage statistics.
usage: {
// Number of tokens in the input.
prompt_tokens: integer;
// Number of tokens in the output.
completion_tokens: integer;
// Sum of prompt_tokens and completion_tokens.
total_tokens: integer;
// Total billed cost for this completion in USD.
cost: number;
};
// Routing metadata. See the Models docs.
sansa: SansaCompletionExtension;
}Errors
All errors follow this format:
{
"error": {
"code": "error_code",
"message": "Human-readable description."
}
}| HTTP Status | Code | When |
|---|---|---|
400 | invalid_model | The model value isn't "sansa-auto", null, or a known model ID |
400 | invalid_call_name | metadata["call_name"] is empty, whitespace-only, or exceeds 64 characters |
400 | invalid_request | reasoning.effort isn't one of the values above, or it is "none" on sansa-auto and no model can serve the request without reasoning |
400 | unsupported_parameter | n > 1, audio, web_search_options, functions, function_call, logprobs or top_logprobs provided, or a field sansa-auto doesn't take |
401 | unauthorized | Invalid or missing API key |
402 | insufficient_credits | Account balance is zero or negative |
429 | rate_limit_exceeded | Too many requests |
500 | internal_error | Unexpected server error |
See Errors for handling, including 402 with the OpenAI SDK.
Unsupported parameters
These return 400:
n > 1(n=1or omittingnis fine)audiomodalitiescontaining"audio"(omittingmodalitiesor passing["text"]is fine)web_search_optionsfunctions(deprecated, usetools)function_call(deprecated, usetool_choice)logprobs,top_logprobs(log probabilities aren't returned yet)
seed, logit_bias, prediction, store, service_tier, verbosity, user, safety_identifier and tools[].function.strict are passed to the model. stream_options is accepted and has no effect.
Prompt Caching
On sansa-auto, send a prompt_cache_key so a conversation stays on one model; see Prompt Caching. The fields:
prompt_cache_key(orsession_id): one value per conversation.- Top-level
cache_control(for example{ "type": "ephemeral" }or{ "type": "ephemeral", "ttl": "1h" }) for automatic cache breakpoints. - Per-block
cache_controlon text content parts for explicit breakpoints on large static content. prompt_cache_retentionon direct model IDs.
Cached tokens are reported in usage.prompt_tokens_details.cached_tokens.
Billing
- Sansa holds the request's estimated cost while it runs, then charges for the tokens used.
usage.costis the billed amount in USD. - Failed requests are not charged.
- See Pricing for rates.