Responses API
Use the Responses API for stateful inference. SubQ stores each turn server-side and links them by ID, so multi-turn workflows — long document review, case research, and agent loops — need no client-side history management. Pass previous_response_id to continue from a prior turn.
For stateless requests where you send the full messages array each time, use the Chat Completions API.
POST /v1/responsesWhen to use which endpoint
| Chat Completions | Responses API | |
|---|---|---|
| Conversation state | You send full messages[] each request | Server stores history; pass previous_response_id |
| Best for | Drop-in OpenAI migrations, custom message assembly | Multi-turn apps, long-context workflows, simpler clients |
| System prompt | system role in messages | instructions (per-turn; not inherited) |
| Built-in web search | Not available | tools: [{ "type": "web_search_preview" }] |
| Function calling | Nested shape ({"type":"function","function":{...}}), see Function calling | Flattened shape ({"type":"function","name":...}), see Function calling |
| Structured Outputs | response_format | text.format (flattened shape) |
| File input | file parts (file_data or file_id) | input_file parts (file_id only) |
| Reasoning effort | Top-level reasoning_effort | Nested reasoning.effort |
| Stream terminator | data: [DONE] | response.completed event |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
model | string | Yes | Model ID to use, such as subq-preview. See Models. |
input | string or content part[] | Yes | The user's message for this turn. A plain string, or an array of input_text and input_file parts. See Input. |
instructions | string or null | No | System prompt for the current turn only. Not inherited — pass it on every request if it must persist. |
previous_response_id | string or null | No | Links this turn to an existing conversation. Omit or pass null to start a new one. Returns 404 if the ID does not exist. |
tools | object[] or null | No | Tools available to the model: built-in tools SubQ executes server-side ({ "type": "web_search_preview" }, see Web search) and function tools your application executes ({ "type": "function", ... }, see Function calling). |
reasoning | object or null | No | Reasoning configuration for this turn. Set effort to control how much internal reasoning the model uses. See Reasoning effort. |
stream | boolean | No | When false or omitted, returns one JSON response. When true, returns server-sent events. |
text | object or null | No | Output format. Defaults to { "format": { "type": "text" } }. See Structured outputs. |
Reasoning effort
Pass a reasoning object with an effort field — not a boolean. Possible effort values are none, low, medium, high, and max. Which values are accepted, and what the default is when you omit reasoning, depends on the model.
This is the Responses equivalent of Chat Completions reasoning_effort. SubQ forwards the selected effort to the upstream model.
- Reasoning tokens are billed as output tokens. They are included in
usage.output_tokens. A short answer can bill dozens of times more with reasoning enabled than without. - There is no bounded default. Omitting
reasoningdoes not mean light reasoning—the model may reason at length. Set it explicitly on any request with a latency or cost ceiling.
Only none gives a guarantee: zero reasoning tokens.
{
"model": "subq-preview",
"instructions": "You are a legal research assistant. Use plain language. Cite only facts in the conversation.",
"input": "Summarize the major self-preferencing antitrust cases since 2010 and list open questions for counsel.",
"reasoning": {
"effort": "high"
}
}Choosing a value
Effort selects a reasoning strategy, not a token budget: higher effort does not reliably mean more tokens, and the relationship differs by prompt and by model version. Benchmark the candidates on your own prompts rather than assuming an ordering.
| Workload | Suggested effort |
|---|---|
| Classification, routing, extraction, short answers | none |
| Open-ended analysis and multi-step work | medium or high; compare both |
Input
Set input to a plain string:
{
"input": "Summarize the major self-preferencing antitrust cases since 2010."
}A content-part array is also accepted. SubQ processes input_text (user text) and input_file (an uploaded file referenced by file_id). Image parts are ignored. Prefer the string form when the turn has no files.
For large documents, include the text in input (or earlier turns) and use SubQ's long context window, or upload the file and pass its file_id.
Uploaded files (file_id)
Upload the document with the Files API, wait until status is processed, then send an input_file part. Responses uses OpenAI's input_file type — not the Chat Completions file part.
{
"model": "subq-preview",
"input": [
{ "type": "input_file", "file_id": "file_abc123" },
{ "type": "input_text", "text": "Summarize this document." }
]
}| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Must be "input_file". |
file_id | string | Yes | File identifier returned by POST /v1/files. |
Inline file_data is not accepted on Responses. Upload the file and pass file_id, or use Chat Completions for a one-off base64 request. A missing file_id returns 400 validation_error. An unknown id, or a file owned by another organization, returns 404 file_not_found.
You can also wrap parts in a message item ({ "role": "user", "content": [ ... ] }), which is the shape the OpenAI SDK sends. See Sending files for upload vs inline, accepted formats, and the Chat Completions part shape.
Instructions
Use instructions for system guidance — policy constraints, role, citation rules, or output style. It is inserted before conversation history for the current turn, equivalent to a Chat Completions system message.
instructions are not carried by previous_response_id. Resend them on every turn that needs the same system prompt.
{
"model": "subq-preview",
"instructions": "You are a legal research assistant. Use plain language. Cite only facts in the conversation.",
"input": "Summarize the major self-preferencing antitrust cases since 2010 and list open questions for counsel."
}Web search
Enable built-in web search with a single tool marker. The proxy runs an agentic loop server-side: when the model asks to search, SubQ executes the search, feeds results back, and continues until a final answer — within one request.
{
"model": "subq-preview",
"instructions": "Be concise (3 bullets max). Prefer Fed, FDIC, and OCC releases from today. If nothing material, say so.",
"input": "Any material US bank regulatory news today?",
"tools": [{ "type": "web_search_preview" }]
}Each executed search appears in output as a web_search_call item, in the order it ran, followed by the final assistant message. The model may also emit a short text message before it searches (see Text preamble before tool calls), so a turn can contain two message items: [message, web_search_call, message].
{
"output": [
{
"id": "ws_019f5c714e4479a5a37680c82da9d06c",
"type": "web_search_call",
"status": "completed",
"action": {
"type": "search",
"queries": [
"US bank regulatory news today",
"FDIC announcements today",
"OCC regulatory updates today",
"Federal Reserve banking regulations today"
],
"query": "US bank regulatory news today"
}
},
{
"id": "msg_019f5c71d7b37b0695ca49dc05c81ba1",
"type": "message",
"status": "completed",
"role": "assistant",
"phase": "final_answer",
"content": [
{
"type": "output_text",
"annotations": [],
"logprobs": [],
"text": "Based on today's regulatory developments, here are the key updates affecting US banks:\n\n- **Immigrant Lending Guidance**: The OCC, FDIC, and NCUA jointly issued nonbinding guidance warning banks about credit risks when lending to undocumented immigrants, citing potential income instability and deportation risks. This implements a May 2026 Trump executive order and follows FinCEN's May advisory on identity theft and money laundering red flags.\n\n- **Circle National Trust Approval**: The OCC granted final approval for Circle to establish a federally regulated national trust bank (Circle National Trust) for digital asset custody. This allows Circle to manage USDC stablecoin reserves under federal oversight, marking a significant step in crypto regulation.\n\n- **Bank Failure**: The OCC shuttered Kentland Federal Savings and Loan Association (Indiana), the nation's smallest standalone bank, due to critical undercapitalization from unsafe practices. This is the third bank failure in 2026."
}
]
}
]
}Production notes:
tool_choiceisauto— the model searches only when needed.- A single
web_search_callmay carry up to 5 queries (action.queries), run concurrently;action.querymirrors the first. - Searches and answers are stored and replayed on later
previous_response_idturns. - Works with streaming and non-streaming.
usagecovers every inference round in the loop. - Don't assume the answer is
output[0]or the onlymessageitem — selectoutputitems bytype, and if more than onemessageis present, the last one is the final answer. See Extracting the assistant text.
Function calling
Function calling (also known as tool calling) lets the model use data and actions provided by your application. Define a function tool with the flattened Responses API shape — type, name, description, parameters, and strict sit directly on the tool object (unlike Chat Completions, which nests these under a function object):
{
"model": "subq-preview",
"input": "Get the latest quotes for BTC-USD, ETH-USD, SOL-USD, and XRP-USD, then summarize the spreads.",
"tools": [
{
"type": "function",
"name": "get_crypto_quote",
"description": "Get the latest bid/ask quote for crypto spot pairs (e.g. BTC-USD, SOL-USD). Returns quotes with quote_time, pair, bid, bid_size, ask, ask_size, and exchange.",
"parameters": {
"type": "object",
"properties": {
"pairs": {
"type": "array",
"description": "Crypto spot pairs to quote, e.g. BTC-USD, ETH-USD, SOL-USD, XRP-USD.",
"items": { "type": "string" }
}
},
"required": ["pairs"],
"additionalProperties": false
},
"strict": true
}
]
}When the model calls a function, the response contains a function_call output item — one per call, since the model may call the same function (or several different ones) in parallel within a single turn:
{
"id": "resp_01a3f8c2...",
"output": [
{
"id": "fc_01a3f8c2b9d14e67a8b5c3d0e2f14a79",
"type": "function_call",
"status": "completed",
"call_id": "chatcmpl-tool-3f9a7c1e6b2d4508",
"name": "get_crypto_quote",
"arguments": "{\"pairs\": [\"BTC-USD\", \"ETH-USD\", \"SOL-USD\", \"XRP-USD\"]}"
}
]
}call_id doesn't always share the resp_/fc_ prefix style — treat it as an opaque string and pass it back verbatim in function_call_output.
Optional text preamble. The model may emit a short text message before its tool calls (e.g. "Let me pull those ticks."). When it does, that text appears as an assistant
messageitem ordered before thefunction_callitem(s), sooutputmay be[{ "type": "message", ... }, { "type": "function_call", ... }]. Select calls bytyperather than assumingoutput[0], and don't assume afunction_callturn has no message. See Text preamble before tool calls.
Execute the function in your application, then send its result back as a function_call_output. Include one function call output per call_id:
{
"model": "subq-preview",
"previous_response_id": "resp_01a3f8c2...",
"input": [
{
"type": "function_call_output",
"call_id": "chatcmpl-tool-3f9a7c1e6b2d4508",
"output": "{\"quotes\":[{\"quote_time\":\"2026-07-17T17:42:03.000Z\",\"pair\":\"BTC-USD\",\"bid\":71284.5,\"bid_size\":0.18,\"ask\":71291.25,\"ask_size\":0.22,\"exchange\":\"coinbase\"}]}"
}
],
"tools": [{ "type": "function", "name": "get_crypto_quote", "description": "...", "parameters": { "type": "object", "properties": {} } }]
}The proxy replays the function call and function call output into model context. The next response may contain a final response or another function_call; repeat the tool calling flow until the model returns a message. Resend tools on each request where the model should be allowed to make another call. Function call outputs are accepted only for calls from the immediately preceding previous_response_id.
Full loop
A response can contain more function calls after receiving function call outputs, so keep looping until the model returns a final response — cap the number of rounds to avoid runaway loops. Execute every function_call in a turn (the model may call in parallel) with Promise.all, and use the last message item to surface any text preamble to the user while functions run:
Text preamble before tool calls
When tools (web search or function calling) are enabled, the model may produce a short text preamble before it issues its tool calls — for example, "Let me look that up." This preamble is surfaced as its own assistant message output item, ordered before the tool-call item(s) it precedes. A single turn can therefore contain multiple message items: an opening preamble, and (for web search, which resolves within the same request) a final answer after the searches.
Implications for clients:
- Do not assume a tool-call turn has no
message, and do not assume the assistant text is alwaysoutput[0]or always last. Iterateoutputand dispatch on each item'stype. - Ordering follows the model:
outputmay be[message (preamble), web_search_call, message (final answer)], or[message (preamble), function_call]. - When streaming, the preamble arrives as
response.output_text.deltaat its ownoutput_index(before the tool items), exactly like any other assistant text. See Streaming.
Request examples
Basic request with system instructions:
Response
A non-streaming request returns a Response object:
{
"id": "resp_01939abc1234567890abcdef",
"object": "response",
"created_at": 1768000000,
"completed_at": 1768000001,
"status": "completed",
"model": "subq-preview",
"output": [
{
"id": "msg_019f42995d097843a2bbdada3a046f14",
"type": "message",
"role": "assistant",
"status": "completed",
"phase": "final_answer",
"content": [
{
"type": "output_text",
"text": "Self-preferencing means a platform favors its own products over competitors. Major cases since 2010 include the EU Google Shopping decision and related follow-on actions. Open questions for counsel often include how to define the relevant market and how to measure harm to rivals.",
"annotations": [],
"logprobs": []
}
]
}
],
"usage": {
"input_tokens": 100,
"input_tokens_details": { "cached_tokens": 0 },
"output_tokens": 68,
"output_tokens_details": { "reasoning_tokens": 0 },
"total_tokens": 168
},
"instructions": "You are a legal research assistant. Use plain language. Cite only facts in the conversation.",
"previous_response_id": null,
"tools": [],
"text": { "format": { "type": "text" }, "verbosity": null },
"error": null,
"incomplete_details": null
}Extracting the assistant text
Without tools, the assistant message is output[0]:
response.output[0].content[0].textWhen tools run, output may contain web_search_call / function_call items, and the model may emit a text preamble message before them (see Text preamble before tool calls). Don't assume the assistant text is at index 0 or that there is exactly one message — select message items by type, and take the last one as the final answer:
const messages = response.output.filter((item) => item.type === "message");
const text = messages.at(-1)?.content[0]?.text ?? "";Save response.id for the next turn's previous_response_id.
Key response fields
| Field | Description |
|---|---|
id | Stable response ID for previous_response_id. |
status | in_progress, completed, failed, or cancelled. |
output | Ordered items for this turn: message, web_search_call, and/or function_call, in production order (a text preamble message may precede tool-call items — see Text preamble before tool calls). |
usage | Token counts for this turn only (not the full conversation history). |
instructions / tools / text / reasoning | Echo of request fields. |
Other request fields such as temperature, store, and tool_choice may also be echoed.
Output items
type | Meaning |
|---|---|
message | Assistant text. Read content[].text where type is output_text. A turn may include more than one — a preamble before tool calls, and the final answer. |
web_search_call | A completed search. See action.queries / action.query. |
function_call | A function call generated by the model. See call_id, name, arguments, and Function calling. |
Usage fields
| Field | Description |
|---|---|
input_tokens | Tokens from the current turn's input (including web-search loop rounds). |
input_tokens_details.cached_tokens | Prompt tokens served from cache. |
output_tokens | Tokens generated in the response. |
output_tokens_details.reasoning_tokens | Reasoning tokens for this turn. Not currently populated—always 0. To measure reasoning consumption, use Chat Completions reasoning_tokens. |
total_tokens | Sum of input and output tokens. |
Multi-turn conversations
Pass the id from a previous response as previous_response_id to continue. The server rebuilds history automatically — send only the new input and the prior response ID.
Turn 1: POST /v1/responses
{ "model": "subq-preview", "input": "Who won the 2024 Nobel Prize in Physics?" }
← { "id": "resp_019f42fa...", "output": [{ "content": [{ "text": "John J. Hopfield and Geoffrey E. Hinton — for foundational work on machine learning with artificial neural networks." }] }] }
Turn 2: POST /v1/responses
{ "model": "subq-preview", "input": "How does this connect to modern deep learning?", "previous_response_id": "resp_019f42fa..." }
← { "id": "resp_019f430a...", "previous_response_id": "resp_019f42fa...", "output": [{ "content": [{ "text": "Hopfield networks inspired associative memory and energy-based models; Hinton's backpropagation and deep networks underpin today's CNNs, transformers, and LLMs." }] }] }
Turn 3: POST /v1/responses
{ "model": "subq-preview", "input": "Are there any other physics-inspired ML ideas not from these laureates?", "previous_response_id": "resp_019f430a..." }
← { "id": "resp_019f4319...", "previous_response_id": "resp_019f430a...", "output": [{ "content": [{ "text": "Yes — Boltzmann machines, diffusion models, renormalization-group analyses of deep networks, and thermodynamics-inspired views of SGD, among others." }] }] }Chain turns by passing the latest id as previous_response_id. Resend instructions (and tools, if needed) on each turn — they are not inherited.
Key behaviors:
- History is branch-safe: branching from an older
previous_response_idcreates a new conversation branch rather than corrupting the existing chain. - History tokens are not re-billed: only the current turn's input tokens are used for credit estimation.
- Prior web searches are replayed into context so the model retains what it found.
- If
previous_response_iddoes not exist, the server returns404immediately, before inference.
Streaming
When stream: true, the response is SSE (Content-Type: text/event-stream). Lifecycle events are always present; tools add output-item events.
Lifecycle: response.created → response.output_text.delta… → response.completed
Output items (when tools run): response.output_item.added → item progress events → response.output_item.done
event: response.created
data: { "type": "response.created", "sequence_number": 0, "response": { ... } }
event: response.output_text.delta
data: { "type": "response.output_text.delta", "sequence_number": 1, "delta": "Self" }
event: response.completed
data: { "type": "response.completed", "sequence_number": 2, "response": { ... } }| Event type | When |
|---|---|
response.created | First. Skeleton response (status: "in_progress"). Use response.id for the next turn. |
response.output_text.delta | Text chunk of assistant text — the final answer, or a preamble before tool calls. Concatenate delta values per output item. |
response.completed | Last. Full output and usage. |
response.output_item.added / done | A structured output item (web_search_call or function_call) is created / finalized. |
response.web_search_call.in_progress / searching / completed | Status pings while a search runs. Queries appear on output_item.done. |
response.function_call_arguments.delta / done | Streamed arguments for a function_call. Concatenate delta values, then parse done's arguments as JSON. |
Items are ordered by output_index, incrementing per item in the order the model produced them. A text preamble is a message item and takes the index before the tool-call item(s) it precedes; the final answer message takes the index after the last item. The model's internal tool-call planning is never streamed — only the resulting items and assistant text reach the client.
Treat unknown event types as ignorable. The stream ends after response.completed — not data: [DONE]. A : keepalive comment is emitted every 15s while the model is slow. If the stream closes without response.completed, treat it as an error and retry.
Structured outputs
Set text.format to constrain the model's output. Three modes are supported:
| Mode | Shape | Guarantee |
|---|---|---|
text (default) | { "type": "text" } | Unconstrained text. |
json_object | { "type": "json_object" } | Output is valid JSON, but not constrained to any schema. |
json_schema | { "type": "json_schema", "name": "...", "strict": true, "schema": { ... } } | Output conforms to the supplied JSON Schema. |
Note the flattened shape: name, strict, and schema sit directly on text.format — unlike Chat Completions, where they nest under response_format.json_schema.
{
"model": "subq-preview",
"input": "RFI-042 from Structural: Confirm rebar grade for Level 3 slab. Spec section 03 20 00 says Grade 60. Reply needed by 2026-07-18 before pour.",
"text": {
"format": {
"type": "json_schema",
"name": "rfi_extract",
"strict": true,
"schema": {
"type": "object",
"properties": {
"rfi_id": { "type": "string" },
"from_trade": { "type": "string" },
"question": { "type": "string" },
"spec_section": { "type": "string" },
"due_date": { "type": "string" }
},
"required": ["rfi_id", "from_trade", "question", "spec_section", "due_date"],
"additionalProperties": false
}
}
}
}Example output at output[0].content[0].text (parse the string to get the object):
{
"rfi_id": "RFI-042",
"from_trade": "Structural",
"question": "Confirm rebar grade for Level 3 slab. Spec section 03 20 00 says Grade 60.",
"spec_section": "03 20 00",
"due_date": "2026-07-18"
}The structured JSON arrives as ordinary assistant text at output[0].content[0].text — parse that string to get the object. json_schema guarantees a valid shape; for reliable field values, put the facts in input and ask the model to extract them (as above).
Unlike OpenAI, json_object mode does not require the word "JSON" in your prompt — output is grammar-constrained by the model server.
For schema design guidance, see Structured outputs.
Errors
{
"error": {
"message": "Response 'resp_xxx' not found.",
"type": "server_error",
"code": "response_not_found"
}
}Use code for programmatic handling — message text may change.
Shared codes with Chat Completions include invalid_api_key, insufficient_credits, validation_error, and rate_limit_exceeded. Responses-specific:
| HTTP | code | Meaning |
|---|---|---|
| 404 | response_not_found | previous_response_id does not exist in the store |
| 400 | validation_error | input_file is missing file_id, or the part used unsupported inline file_data |
| 404 | file_not_found | file_id does not exist or is owned by another organization |
See Authentication for auth and credit errors.
Next steps
- Authentication — API keys and request headers.
- Models — supported model IDs and context windows.
- Sending files —
input_filevs Chat Completionsfileparts. - Files API — upload and manage files referenced by
file_id. - Structured outputs — schema design for
text.format. - Function calling — tool calling concepts and Chat Completions' nested function-tool shape, for comparison with Responses function calling.
- Chat Completions API — stateless, drop-in OpenAI replacement.