Docs menu

POST /v1/responses

The OpenAI Responses wire — what Codex CLI speaks, and what the OpenAI SDKs' client.responses calls. Base URL: https://sator-api.princep.org/v1. Every model in the catalog is served on it.

Stateless, by design#

The Responses API is two things: a request and response shape, and a store of past responses on OpenAI's servers that a later turn can point at by id. Sator serves the shape and keeps no store — nothing you send is written down between requests (Privacy). So a request is accepted exactly when it stands on its own:

  • Send the whole conversation in input on every turn. This is what Codex CLI does (store: false, full history), what the OpenAI SDKs do unless you opt into the store, and what LangChain does.
  • previous_response_id and conversation are refused, 400 with code unsupported_parameter naming the field. So is an input item of type item_reference, and background: true.
  • store is always false. Sending true is not an error; it is simply not what happens, and the response says "store": false.

Request#

bash
curl https://sator-api.princep.org/v1/responses \
  -H "Authorization: Bearer $SATOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4-flash",
    "instructions": "Be brief.",
    "input": [
      {"role": "user", "content": "Say hi"}
    ],
    "max_output_tokens": 64
  }'
FieldNotes
modelRequired. An id from Models. Unknown → 404 model_not_found.
inputRequired. A string, or an array of items: messages (user, assistant, system, developer), function_call, function_call_output, reasoning.
instructionsThe system prompt.
max_output_tokensOptional. A value above the model's max output is lowered to it.
streamtrue for server-sent events. See below and Streaming.
temperature, top_pSampling, passed through.
tools, tool_choice, parallel_tool_callsFunction tools, in the Responses shape (type, name, description, parameters, strict at the top level). See Tool calling.
text.formattext, json_object or json_schema.
reasoning.effortPassed through to models that take it.
reasoning.summaryAsk for one (auto, concise, detailed) to receive a reasoning output item with readable summary text; without it no reasoning text is returned.
prompt_cache_keyYour own conversation key, up to 256 characters. See Prompt caching.
include, text.verbosity, user, safety_identifier, metadataPassed through to models that take them.

Content parts: input_text, input_image with a base64 data URL (a remote image URL is not fetched), and input_file with file_data. Bodies over 5 MB are rejected 413.

A namespace tool — how Codex CLI groups its function tools — is accepted everywhere: on a model that is not Responses-native its member functions stand in for the group, and calls come back with the bare function name.

Built-in toolsweb_search, file_search, computer_use, code_interpreter, mcp, custom grammars — are OpenAI's own and are not available on any model here. On a model that is not Responses-native they are refused 400 invalid_body with param: "tools"; on a Responses-native model they are passed through and the model's own provider answers for them. Codex CLI sends web_search by default; its page says how to turn that off.

Response#

json
{
  "id": "resp_...",
  "object": "response",
  "created_at": 1787593841,
  "status": "completed",
  "error": null,
  "incomplete_details": null,
  "model": "deepseek-v4-flash",
  "output": [
    {
      "id": "msg_...",
      "type": "message",
      "status": "completed",
      "role": "assistant",
      "content": [{"type": "output_text", "text": "Hi! How can I help you today?", "annotations": []}]
    }
  ],
  "parallel_tool_calls": true,
  "store": false,
  "usage": {
    "input_tokens": 12,
    "input_tokens_details": {"cached_tokens": 0},
    "output_tokens": 9,
    "output_tokens_details": {"reasoning_tokens": 0},
    "total_tokens": 21
  }
}

output is the list of items the model produced, in order: a reasoning item when you asked for a summary, a message, and one function_call item per tool call (call_id, name, arguments as a JSON string). status is incomplete with incomplete_details.reason: "max_output_tokens" when the output cap cut the answer short.

usage.input_tokens_details.cached_tokens is how many input tokens were served from cache and billed at the cache-read rate. Every response carries an x-request-id header; quote it to support.

Streaming#

With "stream": true the body is text/event-stream of named events, each with a sequence_number. Every output item is bracketed by response.output_item.added and response.output_item.done — the done carries the whole item — with deltas in between, and the stream ends at response.completed (or response.incomplete), whose response.usage carries the counts.

event: response.created
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_...","status":"in_progress",...}}

event: response.output_item.added
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_...","type":"message","status":"in_progress","role":"assistant","content":[]}}

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! How can I help"}

event: response.output_item.done
data: {"type":"response.output_item.done","sequence_number":7,"output_index":0,"item":{"id":"msg_...","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi! How can I help you today?","annotations":[]}]}}

event: response.completed
data: {"type":"response.completed","sequence_number":8,"response":{"id":"resp_...","status":"completed","output":[...],"usage":{"input_tokens":12,"output_tokens":9,...}}}

Function-call arguments stream as response.function_call_arguments.delta; reasoning summaries as response.reasoning_summary_text.delta. A stream that fails mid-way ends with response.failed. See Streaming for disconnects and billing.

Which models are native#

gpt-5.6-luna, grok-4.6 and muse-spark-1.2 speak this wire natively and pass straight through — every event exactly as their provider emits it, reasoning items with encrypted_content included. Every other model is reached by translating the request onto the model's own wire and translating its answer back into the events above; the fields in the table are what survives the translation.

Errors#

The OpenAI envelope, {"error": {"message", "type", "param", "code"}}. Every status and code is in Errors.