Reference
Updated
Compatibility and versioning
Which parts of the Responses API the helping.io API does not implement, where it differs, and the versioning policy for the surface.
Which parts of the Responses API this API does not implement, where it behaves differently, and the versioning policy. Which clients work, and how to point them here, is on Clients and SDKs.
Not implemented
Section titled “Not implemented”Anything not listed as available on these pages is not on the wire. The items below are the ones a Responses API client is most likely to reach for.
Request fields that are refused
Section titled “Request fields that are refused”Each is a 400 naming the field, so nothing is silently dropped. The wait
window a background request would have outlived is
90 seconds, and a stored response is
removed by deleting it: Retrieve and delete.
| Field | What happens instead |
|---|---|
background | Not accepted. Send the request without it: a non-streaming request that outlives its wait window returns the response in progress, and you retrieve it later with GET /v1/responses/{id}. |
include | Each value either returns its data or is refused. reasoning.encrypted_content adds each reasoning item's encrypted content to the response. message.output_text.logprobs returns log probabilities on a model that produces them and is refused, naming the model, on one that does not; no current model produces them. file_search_call.results, web_search_call.results, web_search_call.action.sources and code_interpreter_call.outputs name hosted tools this API does not offer, and computer_call_output.output.image_url names the computer tool, which is not offered either, so each is refused. message.input_image.image_url is refused for the reason image input is: image input is not accepted, because only text reaches the model. |
reasoning | Not accepted; omit it. Reasoning effort is fixed by the configuration your request runs on and cannot be set per request. Reasoning output still streams and is stored, so you read it the same way. |
store | Not accepted. Every response is stored, which is what makes continuing a conversation and retrieving a response work. To remove one, delete it with DELETE /v1/responses/{id}: it can no longer be retrieved, and later turns no longer send it to the model or count it against the context window. |
Two more things are refused that are not request fields, so they are not in the table above.
| What | Status |
|---|---|
input_image, input_file, input_video parts, and summary_text, reasoning_text or refusal parts in a message |
A 400 naming the part, on a response’s input and on items you add to a conversation. Only text reaches the model, so send what it needs as an input_text part: a file’s text, or a description of an image |
message items with role assistant, system, or developer |
On POST /v1/responses only: send the user turn only. See How input becomes a turn. The Conversations API accepts all four roles, which is how you seed a history that started elsewhere |
Generation settings depend on the model
Section titled “Generation settings depend on the model”The fields below are applied when the model supports them. Support
differs from model to model, so a setting the model does not support, or a
value outside the range it supports, is a 400 that names the model and the
field, before the response starts. For example, a model that reasons by effort
does not support temperature or top_p. None of these settings is accepted
and then dropped.
| Field | What happens |
|---|---|
frequency_penalty | Applied when the model supports it. The response reports the value it was generated with, and 0 when none was applied. |
parallel_tool_calls | Applied when the model supports it: false asks for at most one tool call per turn. The response reports the value it was generated with, and true when none was set. |
presence_penalty | Applied when the model supports it. The response reports the value it was generated with, and 0 when none was applied. |
temperature | Applied when the model supports it, within the model's range. A model that reasons by effort does not support it. The response reports the value it was generated with, and 1 when none was set. |
text | Its format sets the output shape, and a json_schema format constrains the output to the schema it carries, which it must include. Its verbosity sets how much detail to aim for. Each is applied when the model supports it, and the response reports the format and verbosity it was generated under. |
top_logprobs | No current model returns log probabilities, so a request that sets it is a 400 naming the model and the field. The response reports 0. |
top_p | Applied when the model supports it, within the model's range. A model that reasons by effort does not support it. The response reports the value it was generated with, and 1 when none was set. |
On a Claude model that thinks within a token budget, temperature or top_p
is also a 400 before the response starts when thinking is on.
Any other condition belongs to the model provider: when it refuses, the
response fails, its status is failed, and error carries the provider’s
reason.
The response reports each value it was generated with. Where no value was
set, it reports the specification’s default: 1 for temperature and
top_p, true for parallel_tool_calls, 0 for both penalties, and
text for text.format.type.
A text.format of type json_schema constrains the output to the schema you
supply, and must carry one: a json_schema format without a schema is a
400.
What each model accepts
Section titled “What each model accepts”Which of the settings above each model accepts, and the range it accepts
them in. A setting a model’s row does not list is a 400 for that model,
before the response starts. The table is generated from the models this API
serves, by the same check a request meets, so it changes when a model’s
support does.
| Model | Settings it accepts |
|---|---|
anthropic/claude-fable-5 | parallel_tool_calls, text.format |
anthropic/claude-fable-5-1 | parallel_tool_calls, text.format |
anthropic/claude-opus-4-8 | parallel_tool_calls, text.format |
anthropic/claude-opus-5-5 | parallel_tool_calls, text.format |
anthropic/claude-sonnet-5 | parallel_tool_calls, text.format |
anthropic/claude-sonnet-5-5 | parallel_tool_calls, text.format |
google/gemini-3.1-flash-lite | temperature (between 0 and 2), top_p (between 0 and 1), presence_penalty, frequency_penalty, text.format |
google/gemini-3.5-flash | temperature (between 0 and 2), top_p (between 0 and 1), presence_penalty, frequency_penalty, text.format |
google/gemini-3.5-flash-lite | temperature (between 0 and 2), top_p (between 0 and 1), presence_penalty, frequency_penalty, text.format |
google/gemini-3.6-flash | temperature (between 0 and 2), top_p (between 0 and 1), presence_penalty, frequency_penalty, text.format |
google/gemini-3.7-flash | temperature (between 0 and 2), top_p (between 0 and 1), presence_penalty, frequency_penalty, text.format |
google/gemini-3.8-flash | temperature (between 0 and 2), top_p (between 0 and 1), presence_penalty, frequency_penalty, text.format |
openai/gpt-5.4-mini | parallel_tool_calls, text.format, text.verbosity (one of low, medium, high) |
Request fields that are advisory
Section titled “Request fields that are advisory”These are accepted and never refused, because ignoring one of them changes nothing about the answer you get back. What each asks for is not done here yet, and the response says so rather than repeating your value: it reports what was applied. That report is a fact about the response, not a missing field. It is how you tell that the setting did not take effect, and the day one of them does take effect the same field is how you will tell that too.
| Field | What happens, and what the response reports |
|---|---|
prompt_cache_key | Your key does not route the provider cache, and the response reports it as null. Caching still happens, keyed by the conversation, so a continued conversation keeps its cached prefix without you sending anything. |
service_tier | There is one tier. Asking for another runs on that one, and the response always reports that one tier rather than the tier you asked for. |
Endpoints that do not exist
Section titled “Endpoints that do not exist”| Path | Status |
|---|---|
POST /v1/chat/completions |
Not available. The API speaks the Responses API, which OpenAI introduced in 2025 as the successor to Chat Completions. If your app uses Chat Completions today, OpenAI’s guide on moving to Responses covers the changes, and the same code works against this API once migrated |
POST /v1/responses/compact |
Not available. Long conversations are not compacted on request; start a new conversation when you want a fresh context |
GET /v1/responses/{id}/input_items |
Not available. Read the conversation’s items instead: Conversations |
/v1/embeddings, /v1/completions, /v1/files |
Not available. This API serves the Responses API only. Use your provider directly for embeddings, and send file content as text in your input |
| WebSocket transport | Not available. The API is HTTP and server-sent events only. For token-by-token output, set stream: true and read the SSE body: Streaming |
How truncation differs
Section titled “How truncation differs”truncation is applied, and it behaves as the Responses API describes:
disabled, the default, refuses a turn that does not fit the model’s context
window, and auto drops the oldest turns so the turn can proceed. Five
differences are worth knowing before you rely on either value.
| Difference | What it means for you |
|---|---|
| The size of a turn is estimated, not tokenized | The estimate reads about four characters per token. To stay safe, a turn must fit in the window left after the reply’s max_output_tokens, less 15 percent of it, less 2000 tokens for the platform’s own instructions and the estimated size of your tools. So a turn near the model’s real limit can be refused although a token count would have fit it |
auto drops whole turns from the oldest end, never part of one |
The oldest end is the end the Responses API documents dropping from, so that much is the same |
| The turn you just sent is never dropped | If it alone does not fit, the request fails rather than being emptied. This is our guarantee, not a shared one: the Responses API documents nothing about which items auto may keep |
auto trims further than it strictly needs to |
It frees 10 percent of the usable budget beyond what the turn requires, so the next few turns fit without dropping anything more. Trimming to the line would drop a turn on every turn after the first, and each drop costs you the cached prefix |
| A refused turn is not stored | The conversation is left exactly as it was, so you can send a shorter turn against the same previous_response_id |
The refusal is a 400 with the code context_length_exceeded, which is the
code OpenAI’s own clients branch on, and a message naming the window and the
estimate. Match the code rather than the message. Retrieving a stored response
reports the truncation the turn actually ran under.
One thing worth knowing before you build on either value: OpenAI marked
truncation deprecated, in favor of a compaction setting this API does
not implement. Read from their published API description on 2026-09-21, at
revision ddface9b. It is applied here, and any change to that would follow
the versioning policy below.
Behavior that differs from what you might expect
Section titled “Behavior that differs from what you might expect”- No stream resume.
Last-Event-IDis not honored, andstarting_afteris refused with a400rather than ignored, so a client that asks to resume learns that it cannot. If a stream drops, retrieve the response by id or retry the request with the sameIdempotency-Key. - Some declared stream events are never sent, among them the reasoning summary ladders. Each one, and what arrives in its place, is listed under Events you will not receive.
- Where a refusal’s words arrive depends on the model’s provider. When a
model declines, its message gains a
refusalcontent part after any text it already produced, and the response completes, as OpenAI reports a refusal. For Anthropic models, the part’srefusaltext is the model’s explanation when it gives one. For OpenAI models, the words of the refusal arrive first as ordinaryoutput_text, and the part’srefusaltext is empty. For Google models, the part’srefusaltext is empty. max_tool_callscounts every tool call. OpenAI applies it to built-in tools only. Here it counts every call the model makes while generating the response, your own function tools included. Your organization’s policy also limits how many calls one turn may make. Calls past either limit are neither returned nor run, andhelpingio:tool_calls_droppedon the response says how many were cut. When you send function call outputs, the response that continues from them starts a new count. A value below1is a400.safety_identifierreaches only providers with a field for it. It is sent, as a keyed hash of your value, to models whose provider has a per-end-user field, and not to Google models, which have none. The response still reports the value you sent, andhelpingio:warningssays when it was not sent. The deprecateduseris used in its place when it is absent.- Cancel works on any response your key created, not only one created
with
background: true. See Cancel a response. - Retrieval is live, not a snapshot.
GET /v1/responses/{id}reflects the response’s current state. - When you answer a tool call, resent history is ignored rather than
appended. Client libraries usually resend the whole transcript alongside
the
function_call_output. Those items are already in the conversation, so this API acts on the tool output and ignores the rest, where OpenAI appends what you send. The result is the same conversation without a second copy of the same exchange. On a new conversation there is nothing to duplicate, so afunction_callsent with its matchingfunction_call_outputis carried in as real history. - A retrieve reports what the response ran under, not what you sent.
metadata,previous_response_id,truncationandinstructionscome back as you sent them.modelcomes back as the model that ran, andmax_output_tokensas the limit that applied, which is lower than you asked for when the model’s own maximum is lower.toolsandtool_choicecome back as the first turn of the conversation set them, because a turn that answers a tool call runs with the settings of the turn that asked: this API ignorestools,tool_choice,max_tool_callsandsafety_identifierwhen you send tool output, as the standard function-calling loop does when it re-sends your request body. So if you changed any of them while answering a tool call, the retrieve reports the earlier values, which are the ones the model actually had. The response that continues from your tool output counts its tool calls from zero against that samemax_tool_calls. previous_response_idnames the turn a response continues, not always its immediate predecessor. Answering a tool call produces a new response without starting a new turn, so every response of one turn reports the sameprevious_response_id: the response that turn continued. Walking the chain backwards therefore steps over the tool-answer responses in between. Use the conversation’s item list to read a turn’s full history in order.- There is no way to change a response’s
metadataafter it is created. That is not a limitation of this API: the Responses API has no update-response route at all, so metadata is set once with the response. A conversation’s labels can be replaced, throughPOST /v1/conversations/{id}. metadatais never sent to a model, on a response as on a conversation. It is storage and labeling, not instruction.- A response reports
conversationonly when you named one. Sendconversationand the response carries that id, on the create, on the stream and on a later retrieve. Send none and it isnull, even though your history is still kept for you, because a conversation you did not ask for is not one you can use. - Deleting is a tombstone, and it keeps your billing records. Both
DELETE /v1/responses/{id}andDELETE /v1/conversations/{id}stop the data being readable at once, and erase its content 30 days after the delete, without destroying what you were charged or the record of the calls that produced it. Deleting the same thing twice is not an error. There is no undelete. - A deleted response also disappears from its conversation’s items. It stops
being readable, stops being continuable through
previous_response_id, and its messages and tool calls drop out ofGET /v1/conversations/{id}/items. The rest of the conversation is untouched. - A response still running omits
usage. The field appears once the response settles, so read it with a presence check rather than expectingnull. Completed and failed responses carry it, and so does a response that ends in a function call. usagecounts the whole exchange, not one turn. A conversation that runs through several tool rounds reports the running total every time, so the figure grows as you go and the last one is what the exchange cost. It is not a per-turn breakdown, and subtracting two of them is not one either.- Error codes are this API’s own, not OpenAI’s.
response.error.codeand thecodein an error envelope come from this API’s own catalog, such asprovider_overloadedorinsufficient_credits. Match on the HTTP status and the errortypewhen you need behavior that works against both APIs. One code is shared on purpose:insufficient_quota, with OpenAI’s own type and code, for an organization that cannot pay for the request. The full list is on the Errors page. created_aton a conversation is Unix seconds, matching the Responses API’s own convention for the field.- Only items you added can be removed from a conversation. Removing an item
a response produced is a
400; delete the conversation instead. - Two kinds of history never appear in a conversation’s item list: a server-side tool failure, and response lifecycle. A response reports its own tool failures as items; the conversation’s list does not. See What the item list does not show.
stricton function tools is echoed but not enforced. Check eachfunction_call’s arguments against your schema in your own code before you run the tool.function_call_output.statusis ignored, and a failure you report in a tool output counts as a completed tool call. Say that the tool failed in theoutputtext, which the model does read.
Versioning policy
Section titled “Versioning policy”The API implements the Open Responses specification; the OpenAI SDKs are
clients that work with it. Every wire response and stream event is validated
against a pinned schema before a change can ship, so the
shapes on this surface are the ones your SDK already expects. There is one
prefix on the API, /v1, and the rules below say what a client can build
against without watching for announcements.
Where this surface and OpenAI’s differ, the difference is listed on this
page. A field the API does not implement is accepted and ignored, or
refused with a 400 in OpenAI’s own error shape that names it.
What you can rely on
Section titled “What you can rely on”- Unknown request fields never fail a request. No wire type rejects
unknown fields. A field is rejected only when it asks for behavior the
API cannot honor, and then with a
400that names it, which is what lets a newer SDK keep working against this surface. - An unknown value of a known field is a
400that names it. An itemtype, aservice_tieror a tooltypethis API does not define is refused, with the field inparamand its path, such asinput[1], in the message. The message quotes the value when it is a short name rather than free text. - Extensions are namespaced. Everything beyond the specification carries
the
helpingio:prefix and never changes an enum, union, or required field of it. See Extensions.
Breaking and compatible changes
Section titled “Breaking and compatible changes”| Breaking, would require a new prefix | Compatible, may land under /v1 |
|---|---|
| Removing or renaming any field, status value, event type, or error code a client validates against the schema | A new helpingio: field |
| Changing the meaning of an existing one | A new error code under an existing HTTP status |
| Narrowing what is accepted | A loosened acceptance rule |
| A new endpoint |