Skip to content
Get an API key

Build

Updated

Create a response

POST /v1/responses creates one turn of a conversation. This page covers the request fields a turn needs, how input becomes a turn, and the turns that follow.

POST /v1/responses creates a response, which is one turn of a conversation. To continue that conversation, send the next turn with previous_response_id set to the id you got back.

Terminal window
curl https://api.helping.io/v1/responses \
-H "Authorization: Bearer $HELPINGIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-5",
"input": "Say hello in exactly 3 words."
}'

The reply is a response object. The fields you will read most are id, status, output, and usage:

{
"background": false,
"completed_at": 1767225600,
"conversation": null,
"created_at": 1767225600,
"error": null,
"frequency_penalty": 0.0,
"id": "resp_ZXhhbXBsZS9yZXNwL2ZpcnN0LXR1cm4AAAAAAAAAAAAAAAAAAAAAAA",
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
"max_tool_calls": null,
"metadata": null,
"model": "anthropic/claude-sonnet-5",
"object": "response",
"output": [
{
"content": [
{
"annotations": [],
"logprobs": [],
"text": "Hello there, friend.",
"type": "output_text"
}
],
"id": "msg_00000000000000000000000000000001",
"role": "assistant",
"status": "completed",
"type": "message"
}
],
"parallel_tool_calls": true,
"presence_penalty": 0.0,
"previous_response_id": null,
"prompt_cache_key": null,
"reasoning": null,
"safety_identifier": null,
"service_tier": "default",
"status": "completed",
"store": true,
"temperature": 1.0,
"text": {
"format": {
"type": "text"
}
},
"tool_choice": "auto",
"tools": [],
"top_logprobs": 0,
"top_p": 1.0,
"truncation": "disabled",
"usage": {
"input_tokens": 100,
"input_tokens_details": {
"cache_write_tokens": 0,
"cached_tokens": 0
},
"output_tokens": 20,
"output_tokens_details": {
"reasoning_tokens": 0
},
"total_tokens": 120
}
}

The full object also echoes the sampling and tool fields you sent, or their defaults. Fields are snake_case throughout.

Send the next user turn with previous_response_id. The API keeps the conversation history, so you send only the new turn, never the history.

POST /v1/responses
{
"input": "And again.",
"previous_response_id": "resp_ZXhhbXBsZS9yZXNwL2ZpcnN0LXR1cm4AAAAAAAAAAAAAAAAAAAAAAA"
}

Two rules apply to the turns after the first:

  • A conversation is bound to the key that started it. A next turn sent with a different API key answers the same 404 as an unknown id, even inside the same organization. A rotated key keeps its conversations; revoking a key ends them for everyone.
  • model carries forward. Omit it on a later turn and the conversation’s model is used. Supply it to switch models for that turn. See Choose a model.

input carries exactly one thing: the current user turn.

You send What happens
A bare string One user message
message items with role user Their text joins the user message. Several user items concatenate in wire order, separated by a blank line
A message item with role assistant 400. Continue the conversation with previous_response_id instead
A message item with role system or developer 400. Use instructions for per-request steering
A function_call_output item The tool-result path. See Function calling
A function_call item with its matching function_call_output Carried onto the new conversation as a real tool exchange. The call’s arguments and the result’s text become context; the tool’s name is not sent
A function_call with no matching output, or an output with no call 400 naming which item and which half is missing
A reasoning item Stored and readable, never sent to the model
An item-reference item 400. A first turn does not resolve item references, so send the item itself

Multi-part user content keeps its text parts (input_text, output_text, text), joined with newlines. Every other part fails loudly rather than vanishing: input_image, input_file, input_video, summary_text, reasoning_text and refusal parts are each a 400 naming the part. 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.

A user item whose content is empty is skipped. An input array that yields no user text at all is a 400. When that input holds a function_call_output, the message says so instead, because a new conversation has no tool call waiting for a result. Every refusal in this section sets param to input, and a refusal about one item names its index and type in the message.

Three more refusals apply to the whole request body:

  • A string anywhere in the body that contains a NUL character (U+0000) is a 400 naming where it is. Remove or replace the character before sending.
  • conversation and previous_response_id together are a 400: each names the conversation a response continues, so send one of them.
  • A malformed previous_response_id is a 400, and one that names no response your key can see is a 404.

The fields a turn needs, and what each one does. Three things are true of this surface that are worth knowing before you read the tables: unknown fields are ignored rather than rejected, so a newer SDK never fails here; a field that parses but is not applied is listed as accepted, ignored, so nothing is silently half-supported; and the response object echoes back model, previous_response_id, metadata, tools, tool_choice, truncation, instructions, and the effective max_output_tokens. All of them survive a retrieve, with one difference worth knowing when you answer a tool call, described under what a retrieve reports. The request-only fields it does not echo report the specification’s default value regardless of what you sent, except the generation settings, which report the value the response was generated with.

Field Notes
model Required on a first turn. Carries forward on later turns
input Required on a new turn. String or item array, as above
Field Notes
previous_response_id Continue the conversation that produced this response
instructions Per-request steering, echoed, never inherited
metadata Stored with the response and returned when you retrieve it. Up to 16 pairs of key and value, keys up to 64 characters, values strings up to 512 characters
safety_identifier An opaque id for your end user, up to 64 characters. Sent where the model’s provider has a per-end-user field, as a keyed hash of your value, never the value itself, so one end user can be restricted without restricting your key. Echoed as you sent it
user Deprecated in favor of safety_identifier. Used as your end user’s id when safety_identifier is absent, and ignored when both are sent. It does not group requests for caching, and the response does not echo its value
Field Notes
tools Your function tools. Echoed. See Function calling
tool_choice none, auto, required, {"type": "function", "name": …}, or an allowed_tools object. Echoed. A function choice needs a name; allowed_tools takes mode auto or required and a name on each entry; required needs at least one tool; and naming a tool that is not in your tools is a 400 naming the tool
Field Notes
stream true for server-sent events. Defaults to false. See Streaming
stream_options include_obfuscation, on by default, pads each streamed delta event so its size does not track the length of the text in it. Set it to false to turn the padding off
max_output_tokens Minimum 16. Echoed as the effective value, which can be lower than you asked for, and carried forward to a later turn that omits it
truncation disabled (the default) refuses a turn that does not fit the model’s context window, with the code context_length_exceeded; auto drops the oldest turns so it fits. See How truncation differs

Generation settings, the fields that are accepted but advisory, and the fields that are refused are listed on Compatibility and versioning. The API reference carries every field of the request body.

Header Notes
Authorization: Bearer sk_… Required. See API keys
Content-Type: application/json Recommended on POST. The body is read as JSON whatever this header says
Idempotency-Key Optional, and the official SDKs do not send it on their own: pass it in the request options. Visible ASCII, at most 255 bytes. A retry with the same key and body returns the first response, so the model produces output and bills once; the same key with a different body is a 409. Remembered for 24 hours. See Idempotency

A non-streaming request waits up to 90 seconds. A response that is not finished by then comes back in progress: the same id, a status of queued or in_progress, an empty output, and HTTP 200. It is not an error, and it is not the end of the work. Poll GET /v1/responses/{response_id} for the result.

{
"background": false,
"completed_at": null,
"conversation": null,
"created_at": 1767225600,
"error": null,
"frequency_penalty": 0.0,
"id": "resp_ZXhhbXBsZS9yZXNwL2luLXByb2dyZXNzLWF0LXRoZS13YWl0LWJvdQ",
"incomplete_details": null,
"instructions": null,
"max_output_tokens": 1024,
"max_tool_calls": null,
"metadata": null,
"model": "anthropic/claude-sonnet-5",
"object": "response",
"output": [],
"parallel_tool_calls": true,
"presence_penalty": 0.0,
"previous_response_id": null,
"prompt_cache_key": null,
"reasoning": null,
"safety_identifier": null,
"service_tier": "default",
"status": "in_progress",
"store": true,
"temperature": 1.0,
"text": {
"format": {
"type": "text"
}
},
"tool_choice": "auto",
"tools": [],
"top_logprobs": 0,
"top_p": 1.0,
"truncation": "disabled"
}

For work that takes longer than a few seconds, or when you want tokens as they arrive, use streaming, which is bounded at 300 seconds instead.