Responses
API reference for the OpenAI Responses-compatible endpoint
Creates a model response using OpenAI's Responses API shape, the item-based alternative to Chat Completions. Point an OpenAI SDK's baseURL at Sansa and call client.responses.create(...) with no other changes. Model selection works as on Completions.
POST /v1/responses
Sansa's Responses endpoint is stateless: nothing is stored server-side between requests. Send the full conversation in input on every call; see Stateless model.
See the code panel for request and response examples.
Body Parameters
On sansa-auto, some fields return 400; see What sansa-auto accepts.
input
Required. A string (one user turn) or an array of items representing the conversation so far. Empty input returns 400.
// Shorthand for a single user message.
input: string
// Or an array of items:
input: InputItem[]Item types you send:
type | Purpose | Key fields |
|---|---|---|
message (or omit type and set role) | One conversation turn | role, content |
function_call | Echo back a tool call from a prior response | call_id, name, arguments |
function_call_output | The result of a tool call, for the model to read | call_id, output |
reasoning | Echo back a prior reasoning item for continuity | summary, encrypted_content |
interface MessageItem {
type?: "message"; // optional when role is present
role: "system" | "developer" | "user" | "assistant" | "tool";
content: string | ContentPart[];
}
interface ContentPart {
type: "input_text" | "input_image" | "input_file";
// For "input_text".
text?: string;
// For "input_image". Accepts an https URL or a data URI.
// detail: "low" | "high" | "auto" | "original" (maps to "high").
image_url?: string | { url: string; detail?: string };
detail?: string;
// For "input_file". Only image data URIs are supported
// (no Files API); other files return 400.
file_data?: string;
}developer is accepted as an alias for system. Audio (input_audio) isn't supported on Responses; use Completions for audio.
instructions
Optional. A developer/system message that applies to this request only. Prepended ahead of the flattened input.
model
Optional. Same behavior as Completions: "sansa-auto", null, or omitted routes automatically; a catalog model ID calls that model directly; an unknown ID returns 400 invalid_model.
stream
Default: false. If true, the response is delivered as named Server-Sent Events; see Streaming.
temperature
Optional. Same semantics as Completions.
top_p
Optional. Same semantics as Completions.
max_output_tokens
Optional. Upper bound on generated tokens, including reasoning tokens. Equivalent to max_completion_tokens on Chat Completions.
tools
Optional. Function tools in the flat Responses shape:
interface FunctionTool {
type: "function";
name: string;
description?: string;
// A JSON Schema object.
parameters: object;
strict?: boolean; // accepted; forwarded where supported
}Also accepted in tools:
web_search/web_search_preview: a hosted tool Sansa runs for you. See Web search.shell,apply_patch,custom: client-executed tools that round-trip like function calls; see Client-executed tools.type: "namespace"wrapping a nestedtoolsarray: unwrapped automatically.- Other hosted tool types (
file_search,code_interpreter,computer_use,image_generation,mcp,tool_search) are accepted and ignored rather than rejected, so a mixed tool array from an existing agent config doesn't 400.
tool_choice
Default: "auto". Same values as Completions ("auto", "none", "required", { type: "function", name: "..." }). { type: "web_search" } maps to "auto".
parallel_tool_calls
Default: true. Same as Completions.
reasoning
Optional. Reasoning configuration.
{
effort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
// Requests a visible reasoning summary: the model's visible reasoning,
// when it produced any; otherwise empty.
summary?: "auto" | "concise" | "detailed";
}See Reasoning for what each effort level means. On sansa-auto, omitting reasoning entirely still defaults to effort: "high", same as Completions.
text
Optional. Structured output configuration, equivalent to response_format on Completions.
// Default. Unstructured text.
{ format: { type: "text" } }
// Guaranteed valid JSON, no schema enforcement.
{ format: { type: "json_object" } }
// Structured outputs conforming to a JSON Schema.
{
format: {
type: "json_schema",
name: "...",
strict: true,
schema: { ... }
}
}include
Optional. Array of strings controlling extra output data. Only "reasoning.encrypted_content" has an effect; see Reasoning.
metadata, session_id, prompt_cache_key, prompt_cache_retention
Same as on Completions. On sansa-auto, send a prompt_cache_key; see Prompt Caching.
safety_identifier, user
Optional. Used as a fallback source for metadata.end_user_id when neither sansa.end_user_id nor metadata.end_user_id is set (checked in that order).
text.verbosity, service_tier
Optional. Passed to the model. Successful responses echo service_tier: null.
Accepted, no effect
The following are accepted for compatibility but don't change behavior:
| Field | Note |
|---|---|
truncation | "disabled" is the effective behavior regardless of value |
stream_options.include_obfuscation | Accepted; obfuscation payloads are never emitted |
max_tool_calls | Accepted, no effect |
prompt_cache_options | Accepted; only a 1h/3600s TTL has an effect |
Returns
A response object.
interface Response {
id: string; // "resp_..."
object: "response";
created_at: number; // unix timestamp
status: "completed" | "incomplete" | "failed";
error: { code: string; message: string } | null;
incomplete_details: { reason: "max_output_tokens" | "content_filter" } | null;
// The model that actually served the request.
model: string;
output: OutputItem[];
usage: {
input_tokens: number;
input_tokens_details: { cached_tokens: number };
output_tokens: number;
output_tokens_details: { reasoning_tokens: number };
total_tokens: number;
};
// Echoed request fields.
instructions: string | null;
max_output_tokens: number | null;
metadata: Record<string, string>;
parallel_tool_calls: boolean;
reasoning: { effort: string | null; summary: string | null } | null;
text: { format: { type: string; [k: string]: unknown } };
tool_choice: string | object;
tools: object[];
top_p: number;
temperature: number;
truncation: "disabled";
user: string | null;
service_tier: null;
// Always these values: responses are never stored.
store: false;
previous_response_id: null;
// Routing metadata, as on Chat Completions, plus `cost` in USD.
// See the Models docs.
sansa?: {
routed: boolean;
routed_model: string | null;
routing_latency_ms: number | null;
cost: number;
};
}Output items
output holds one entry per item the model produced, in the order it produced them.
type | Emitted when |
|---|---|
reasoning | reasoning was requested, or the model returned reasoning |
function_call | The model wants to call a function tool |
web_search_call | A web_search tool call returned citations |
shell_call / apply_patch_call / custom_tool_call | A client-executed tool call |
message | The model produced visible text (omitted on tool-only turns) |
{
"id": "rs_...",
"type": "reasoning",
"status": "completed",
"summary": [{ "type": "summary_text", "text": "..." }],
"content": [],
"encrypted_content": "..."
}{
"id": "fc_call_abc123",
"type": "function_call",
"status": "completed",
"call_id": "call_abc123",
"name": "get_weather",
"arguments": "{\"location\":\"Tokyo\"}"
}{
"id": "ws_...",
"type": "web_search_call",
"status": "completed",
"action": {
"type": "search",
"query": "",
"sources": [{ "type": "url", "url": "https://example.com/..." }]
}
}{
"id": "msg_...",
"type": "message",
"status": "completed",
"role": "assistant",
"content": [
{ "type": "output_text", "text": "...", "annotations": [] }
]
}status on the top-level response is "incomplete" when generation hit max_output_tokens (incomplete_details.reason: "max_output_tokens") or a content filter ("content_filter"); otherwise "completed".
Streaming
Set stream: true. Unlike Chat Completions' data:-only chunks, Responses streaming sends named events:
event: response.created
data: {"type":"response.created","sequence_number":0,"response":{...}}
event: response.output_text.delta
data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_...","output_index":0,"content_index":0,"delta":"Hi"}- Every event's
datahas atypematching theevent:line, and a monotonically increasingsequence_numberstarting at 0. - There is no
data: [DONE]. The stream ends after the terminal event. usageonly appears on the terminal event (response.completedorresponse.incomplete).
See the code panel for a full streaming example with SSE parsing.
Event reference
| Event | When |
|---|---|
response.created | First event. status: "in_progress", empty output, null usage |
response.in_progress | Immediately after created |
response.output_item.added / .done | An output item (message, reasoning, or a tool call) starts / finishes |
response.content_part.added / .done | A content part within a message item starts / finishes |
response.output_text.delta / .done | Incremental / final assistant text |
response.function_call_arguments.delta / .done | Incremental / final JSON for a function call's arguments |
response.reasoning_summary_part.added / .done | A reasoning summary part starts / finishes |
response.reasoning_summary_text.delta / .done | Incremental / final reasoning summary text |
response.completed | Terminal. Full response object including output and usage |
response.incomplete | Terminal, in place of completed, when generation was cut short |
response.failed | Terminal, only if an error occurs after streaming has already started. response.error is set |
Errors before any event is sent are a normal JSON error body. Errors after response.created arrive as response.failed.
Tool calling
Round trip is item-based rather than message-based:
- Send a request with
toolsandinput. - The model returns one or more
function_callitems inoutput, each with acall_id. - Run the function yourself.
- Send a new request whose
inputis: the previousinput, plus thefunction_callitem(s) from step 2, plus afunction_call_outputitem per call:
{
"call_id": "call_abc123",
"type": "function_call_output",
"output": "{\"temperature\": 22, \"conditions\": \"sunny\"}"
}Pass back the exact call_id from the function_call item, and include tools on every request, not just the first. output can be a string or an array of input_text parts (concatenated); image parts in tool output are not supported.
Client-executed tools
shell, apply_patch, and custom tools follow the same round trip as function tools, but with typed item names on both sides: shell_call / apply_patch_call / custom_tool_call from the model, and shell_call_output / apply_patch_call_output / custom_tool_call_output for your results. Your runtime (such as Codex) runs them; Sansa doesn't.
Web search
Pass { "type": "web_search" } (or "web_search_preview") in tools to let the model search the web. Sansa runs the search; you don't implement anything.
- Billed $0.01 per search that returned citations, in addition to the token cost of citation snippets injected into context. A search that returns nothing is not charged.
- Results appear as a
web_search_calloutput item plusurl_citationannotations on the assistant message'soutput_text.annotations. - On
sansa-autowith no other tools, search-only requests route to a search-capable model automatically.
Reasoning
Set it with the reasoning parameter.
When the model returns reasoning, Sansa emits a reasoning output item with a plaintext summary. To carry reasoning state across turns for models that need it, pass include: ["reasoning.encrypted_content"]: the reasoning item then also carries encrypted_content, an opaque value. Echo it back unmodified on a reasoning input item on your next request:
{
"type": "reasoning",
"summary": [{ "type": "summary_text", "text": "..." }],
"encrypted_content": "..."
}Treat it as opaque. It may be missing, depending on the model; requests work without it.
Stateless model
Sansa's Responses endpoint does not persist responses. Every request must carry the full conversation in input.
Accepted (no-ops): omitted store, store: false, omitted previous_response_id, previous_response_id: null. Successful responses always echo store: false and previous_response_id: null.
Rejected with 400 unsupported_parameter:
| Field | Reason |
|---|---|
store: true | No stored responses |
previous_response_id (non-null) | No stored responses to reference |
conversation | Not supported |
background: true | Not supported |
prompt (stored prompt template ID) | No stored prompts |
moderation | Not supported |
context_management | Not supported |
An input item with type: "item_reference" returns 400 invalid_request (param: "input").
These stored-response endpoints return 400:
GET /v1/responses/{response_id}DELETE /v1/responses/{response_id}GET /v1/responses/{response_id}/input_itemsPOST /v1/responses/{response_id}/cancelPOST /v1/responses/compact
Errors
Errors use the same envelope as the rest of the API. Responses-specific cases:
| HTTP Status | Code | param | When |
|---|---|---|---|
400 | unsupported_parameter | store, previous_response_id, conversation, background, prompt, moderation, context_management | See Stateless model |
400 | invalid_request | input | input is missing, empty, or contains an unsupported item type |
400 | invalid_request | reasoning | reasoning.effort isn't an accepted value, or it is "none" on sansa-auto and no model can serve the request without reasoning |
400 | unsupported_parameter | top_logprobs | Log probabilities aren't returned yet |
400 | unsupported_parameter | the field | A field sansa-auto doesn't take |
400 | invalid_request | text | text.format.type is "json_schema" without a name or schema |
400 | unsupported_parameter | input | An input_audio content part was sent (text-only) |
Other errors match Completions; see Errors.
Migrating from Chat Completions
Routing, credits, and tools work the same. Field renames:
| Responses | Chat Completions |
|---|---|
input | messages |
instructions | leading system message |
max_output_tokens | max_completion_tokens |
text.format | response_format |
created_at | created |
output[] | choices[0].message |
status | inferred from finish_reason |
usage.input_tokens | usage.prompt_tokens |
usage.output_tokens | usage.completion_tokens |
function tool { name, parameters } | { function: { name, parameters } } |
function_call item call_id | tool_calls[].id |
function_call_output item | { role: "tool" } message |
content input_text / output_text | content text |
content input_image | content image_url |
A key whose endpoint allowlist includes chat.completions can also call /v1/responses. To allow only Responses, use the responses slug.
Billing
Billed like Completions, plus $0.01 per web search. The cost is on sansa.cost; Responses has no usage.cost.