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:

typePurposeKey fields
message (or omit type and set role)One conversation turnrole, content
function_callEcho back a tool call from a prior responsecall_id, name, arguments
function_call_outputThe result of a tool call, for the model to readcall_id, output
reasoningEcho back a prior reasoning item for continuitysummary, 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 nested tools array: 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:

FieldNote
truncation"disabled" is the effective behavior regardless of value
stream_options.include_obfuscationAccepted; obfuscation payloads are never emitted
max_tool_callsAccepted, no effect
prompt_cache_optionsAccepted; 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.

typeEmitted when
reasoningreasoning was requested, or the model returned reasoning
function_callThe model wants to call a function tool
web_search_callA web_search tool call returned citations
shell_call / apply_patch_call / custom_tool_callA client-executed tool call
messageThe 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 data has a type matching the event: line, and a monotonically increasing sequence_number starting at 0.
  • There is no data: [DONE]. The stream ends after the terminal event.
  • usage only appears on the terminal event (response.completed or response.incomplete).

See the code panel for a full streaming example with SSE parsing.

Event reference

EventWhen
response.createdFirst event. status: "in_progress", empty output, null usage
response.in_progressImmediately after created
response.output_item.added / .doneAn output item (message, reasoning, or a tool call) starts / finishes
response.content_part.added / .doneA content part within a message item starts / finishes
response.output_text.delta / .doneIncremental / final assistant text
response.function_call_arguments.delta / .doneIncremental / final JSON for a function call's arguments
response.reasoning_summary_part.added / .doneA reasoning summary part starts / finishes
response.reasoning_summary_text.delta / .doneIncremental / final reasoning summary text
response.completedTerminal. Full response object including output and usage
response.incompleteTerminal, in place of completed, when generation was cut short
response.failedTerminal, 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:

  1. Send a request with tools and input.
  2. The model returns one or more function_call items in output, each with a call_id.
  3. Run the function yourself.
  4. Send a new request whose input is: the previous input, plus the function_call item(s) from step 2, plus a function_call_output item 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.


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_call output item plus url_citation annotations on the assistant message's output_text.annotations.
  • On sansa-auto with 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:

FieldReason
store: trueNo stored responses
previous_response_id (non-null)No stored responses to reference
conversationNot supported
background: trueNot supported
prompt (stored prompt template ID)No stored prompts
moderationNot supported
context_managementNot 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_items
  • POST /v1/responses/{response_id}/cancel
  • POST /v1/responses/compact

Errors

Errors use the same envelope as the rest of the API. Responses-specific cases:

HTTP StatusCodeparamWhen
400unsupported_parameterstore, previous_response_id, conversation, background, prompt, moderation, context_managementSee Stateless model
400invalid_requestinputinput is missing, empty, or contains an unsupported item type
400invalid_requestreasoningreasoning.effort isn't an accepted value, or it is "none" on sansa-auto and no model can serve the request without reasoning
400unsupported_parametertop_logprobsLog probabilities aren't returned yet
400unsupported_parameterthe fieldA field sansa-auto doesn't take
400invalid_requesttexttext.format.type is "json_schema" without a name or schema
400unsupported_parameterinputAn 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:

ResponsesChat Completions
inputmessages
instructionsleading system message
max_output_tokensmax_completion_tokens
text.formatresponse_format
created_atcreated
output[]choices[0].message
statusinferred from finish_reason
usage.input_tokensusage.prompt_tokens
usage.output_tokensusage.completion_tokens
function tool { name, parameters }{ function: { name, parameters } }
function_call item call_idtool_calls[].id
function_call_output item{ role: "tool" } message
content input_text / output_textcontent text
content input_imagecontent 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.