Responses API

/v1/responses implements OpenAI’s Responses format. It takes a single input instead of a messages array, and unlike /v1/chat/completions it holds state: a response gets an id you can fetch later, chain the next turn onto, or cancel while it runs.

Reach for it when you want the server to keep the thread, when you want to start long work and collect it later, or when you need store, metadata or background, which are specific to this endpoint. See OpenAI Compatibility for the full comparison.

Creating a response

$curl -X POST https://api.asi1.ai/v1/responses \
> -H "Content-Type: application/json" \
> -H "Authorization: Bearer $ASI_ONE_API_KEY" \
> -d '{
> "model": "asi1",
> "input": "Summarize the causes of the 1970s oil shocks."
> }'

The OpenAI SDK’s responses client works against this endpoint unchanged, so create, retrieve, stream, cancel and delete are all available on it. Every Python example further down this page reuses the client built here. output_text is an SDK convenience that concatenates the text for you; the underlying object is below.

The reply is a response object:

1{
2 "id": "resp_ffbdb0b00b324505b1a46b92d4e1f5d2",
3 "object": "response",
4 "status": "completed",
5 "model": "asi1",
6 "created_at": 1784651750.64,
7 "completed_at": 1784651751.15,
8 "output": [
9 {
10 "id": "msg_4fb7625285db",
11 "type": "message",
12 "role": "assistant",
13 "status": "completed",
14 "content": [
15 {
16 "type": "output_text",
17 "text": "The 1973 embargo followed...",
18 "annotations": []
19 }
20 ]
21 }
22 ],
23 "usage": {
24 "input_tokens": 367,
25 "input_tokens_details": { "cached_tokens": 320 },
26 "output_tokens": 7,
27 "output_tokens_details": { "reasoning_tokens": 0 },
28 "total_tokens": 374
29 }
30}

The three fields you will reach for:

  • id - what you pass to retrieve, cancel, delete, or previous_response_id.
  • status - completed, failed, incomplete or cancelled once the run has ended; queued or in_progress while it is still going. This is what you poll on for a background response.
  • output - an array of items, not a single string. The assistant’s text is at output[0].content[0].text. Reasoning and tool calls arrive as their own items in the same array, so walk it by type rather than assuming position.

usage uses the Responses spelling: input_tokens and output_tokens rather than the prompt_tokens and completion_tokens you get from /v1/chat/completions.

There are three ways to take delivery, and they are the main decision on this endpoint.

ModeSetYou get
SynchronousnothingThe finished response on the POST
Streaming"stream": trueServer-sent events on the POST
Background"background": trueAn immediate queued response to poll or stream later

Synchronous is the default and is right for short work. Streaming is right for anything a person is waiting on. Background is right for work you want to start and collect later, possibly from a different process.

background requires store to be left at its default of true. A background response is delivered by id, so it has to be retrievable. Sending "background": true with "store": false returns a 400.

Storing and retrieving

Responses are stored by default, which is what makes the rest of this page possible. Fetch one by id:

$curl https://api.asi1.ai/v1/responses/resp_ffbdb0b00b324505b1a46b92d4e1f5d2 \
> -H "Authorization: Bearer $ASI_ONE_API_KEY"

A stored response stays retrievable for three days after it finishes. After that GET, cancel, delete and chaining onto it all return a 404, which means gone rather than still running.

Send "store": false on create when you do not want the response kept. It is then delivered on the request itself and is not retrievable afterwards, so all four of those stop applying to it immediately.

GET /v1/responses/{id}/input_items returns the input items for that single turn. It does not return the whole reconstructed conversation.

Streaming

Send "stream": true on create and the POST returns server-sent events. Each frame carries a named event: and a JSON data: payload, which is the main difference from /v1/chat/completions, where every frame is an anonymous chunk containing choices[0].delta.

id: 1784651772096-0
event: response.created
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_39695f36c4694d31b84e39292d2ffd3f","status":"in_progress",...}}
id: 1784651772414-0
event: response.output_text.delta
data: {"type":"response.output_text.delta","sequence_number":4,"delta":"The 1973","item_id":"msg_2e58b2b0cb0a","output_index":0,"content_index":0}
id: 1784651772482-0
event: response.completed
data: {"type":"response.completed","sequence_number":9,"response":{...}}

response.created carries the response id, which is how you capture it when streaming a create: you need it to cancel the run or to chain the next turn onto it.

A full run emits, in order: response.created, response.in_progress, response.output_item.added, response.content_part.added, one response.output_text.delta per fragment, response.output_text.done, response.content_part.done, response.output_item.done, and a terminal event. Reasoning arrives first, on response.reasoning_text.delta events closed by a response.reasoning_text.done, as its own output item ahead of the text.

Three event types are terminal, and receiving any of them means the stream is finished:

EventMeaning
response.completedThe run finished and the response is final
response.failedThe run stopped on an error
response.incompleteThe run stopped before it finished

response.completed carries the finished response object, including usage. That is where to read token counts when streaming; the earlier events do not carry them.

There is no [DONE] sentinel on this endpoint: a terminal event is the end of the stream. Treat the event set as open and ignore any type you do not recognize, so new event types do not break your client.

Cancelling a run also produces response.incomplete, so read the response status to tell a cancellation from a token limit.

The OpenAI SDK handles the framing and gives you the assembled response at the end, so you branch on event.type rather than parsing frames yourself:

1with client.responses.stream(
2 model="asi1",
3 input="Summarize the causes of the 1970s oil shocks.",
4) as stream:
5 for event in stream:
6 if event.type == "response.output_text.delta":
7 print(event.delta, end="", flush=True)
8
9 final = stream.get_final_response()
10
11print()
12print(final.status)

A background response can also be streamed from the retrieve endpoint, which is how you attach to work that was started elsewhere:

$curl "https://api.asi1.ai/v1/responses/resp_ffbdb0b00b324505b1a46b92d4e1f5d2?stream=true" \
> -H "Authorization: Bearer $ASI_ONE_API_KEY"

Streaming from retrieve applies to background responses. Adding ?stream=true to a response that was not created with "background": true returns a 400. Retrieve it without the parameter to get the stored snapshot.

Continuing a conversation

Pass a previous response’s id as previous_response_id and the server reconstructs the thread for you, including any tool calls and reasoning it contains. You send only the new turn.

$curl -X POST https://api.asi1.ai/v1/responses \
> -H "Content-Type: application/json" \
> -H "Authorization: Bearer $ASI_ONE_API_KEY" \
> -d '{
> "model": "asi1",
> "input": "Now compare that to the 2008 spike.",
> "previous_response_id": "resp_ffbdb0b00b324505b1a46b92d4e1f5d2"
> }'

This is the main practical difference from /v1/chat/completions, where you resend the whole messages array every turn.

Chain onto a parent that has finished and is still stored. Chaining onto a run that is still going returns a 409, and onto one created with "store": false, or older than the three-day window, a 404.

Cancelling and deleting

$# Stop a run that is still in progress
$curl -X POST https://api.asi1.ai/v1/responses/resp_ffbdb0b00b324505b1a46b92d4e1f5d2/cancel \
> -H "Authorization: Bearer $ASI_ONE_API_KEY"
$
$# Remove a stored response
$curl -X DELETE https://api.asi1.ai/v1/responses/resp_ffbdb0b00b324505b1a46b92d4e1f5d2 \
> -H "Authorization: Bearer $ASI_ONE_API_KEY"

Cancelling a response that has already finished returns the final response rather than an error, so a cancel racing a completion is safe to send. Deleting cancels the run first if it is still going, then removes the stored response.

What this endpoint takes

input takes either a string or an array of input items, so you can send a whole thread in one request rather than a single prompt:

1{
2 "model": "asi1",
3 "input": [
4 { "role": "user", "content": [{ "type": "input_text", "text": "What is agentic AI?" }] },
5 { "role": "assistant", "content": [{ "type": "output_text", "text": "Agentic AI describes systems that..." }] },
6 { "role": "user", "content": [{ "type": "input_text", "text": "How is that different from a chatbot?" }] }
7 ]
8}

Every content part is text. To ask about an image, use /v1/chat/completions with an image_url content part; an input_image part here is rejected with a 400.

Reasoning uses OpenAI’s standard reasoning object here. enable_thinking and thinking_budget also work on this endpoint, so you can migrate at your own pace. See Reasoning.

Tool calling works on both endpoints, and this one additionally accepts the flat Responses tool shape alongside the nested one. See Tool Calling.

Structured output uses text.format here rather than response_format, taking the same JSON schema. See Structured Data.

max_output_tokens bounds the tokens generated for the response, the way max_tokens does on /v1/chat/completions.

metadata takes up to 16 key-value string pairs, which are stored with the response and returned again when you retrieve it. Use it to tag a response with your own trace or tenant id so you can match it up later.

Planner mode runs on /v1/chat/completions.

Next steps

  1. Chat Completions API - The stateless endpoint, and where planner mode runs
  2. OpenAI Compatibility - Which parameters are supported on each endpoint
  3. Tool Calling - Let the model call your own functions
  4. Errors and Rate Limits - What each status code means and what to retry