> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.asi1.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.asi1.ai/_mcp/server.

# Responses API

> Create, stream, retrieve, chain, cancel and delete responses on /v1/responses

`/v1/responses` implements OpenAI's Responses format. It takes a single `input`
instead of a `messages` array, and unlike
[`/v1/chat/completions`](/documentation/build-with-asi-one/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](/documentation/build-with-asi-one/openai-compatibility) for the
full comparison.

## Creating a response

#### cURL

```bash
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."
  }'
```

#### Python

```python
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("ASI_ONE_API_KEY"),
    base_url="https://api.asi1.ai/v1",
)

response = client.responses.create(
    model="asi1",
    input="Summarize the causes of the 1970s oil shocks.",
)

print(response.id)
print(response.output_text)
```

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:

```json
{
  "id": "resp_ffbdb0b00b324505b1a46b92d4e1f5d2",
  "object": "response",
  "status": "completed",
  "model": "asi1",
  "created_at": 1784651750.64,
  "completed_at": 1784651751.15,
  "output": [
    {
      "id": "rs_2c8e5a1f9d03",
      "type": "reasoning",
      "status": "completed",
      "summary": [],
      "content": [
        {
          "type": "reasoning_text",
          "text": "The user wants a summary of the causes of the 1970s oil shocks..."
        }
      ]
    },
    {
      "id": "msg_4fb7625285db",
      "type": "message",
      "role": "assistant",
      "status": "completed",
      "content": [
        {
          "type": "output_text",
          "text": "The 1973 embargo followed...",
          "annotations": []
        }
      ]
    }
  ],
  "usage": {
    "input_tokens": 237,
    "input_tokens_details": { "cached_tokens": 0 },
    "output_tokens": 1144,
    "output_tokens_details": { "reasoning_tokens": 689 },
    "total_tokens": 1381
  }
}
```

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
  in the `message` item, at `content[0].text`. Reasoning and tool calls arrive as
  their own items in the same array, reasoning first as above, 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`](/documentation/build-with-asi-one/chat-completions).

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

| Mode        | Set                  | You get                                                |
| ----------- | -------------------- | ------------------------------------------------------ |
| Synchronous | nothing              | The finished response on the POST                      |
| Streaming   | `"stream": true`     | Server-sent events on the POST                         |
| Background  | `"background": true` | An 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.

> **Info**
>
> `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

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

#### Python

```python
response = client.responses.retrieve("resp_ffbdb0b00b324505b1a46b92d4e1f5d2")

print(response.status)
print(response.output_text)
```

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:

| Event                 | Meaning                                    |
| --------------------- | ------------------------------------------ |
| `response.completed`  | The run finished and the response is final |
| `response.failed`     | The run stopped on an error                |
| `response.incomplete` | The 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:

```python
with client.responses.stream(
    model="asi1",
    input="Summarize the causes of the 1970s oil shocks.",
) as stream:
    for event in stream:
        if event.type == "response.output_text.delta":
            print(event.delta, end="", flush=True)

    final = stream.get_final_response()

print()
print(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

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

#### Python

```python
started = client.responses.create(
    model="asi1",
    input="Summarize the causes of the 1970s oil shocks.",
    background=True,
)

for event in client.responses.retrieve(started.id, stream=True):
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)
```

> **Info**
>
> 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

```bash
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"
  }'
```

#### Python

```python
followup = client.responses.create(
    model="asi1",
    input="Now compare that to the 2008 spike.",
    previous_response_id=response.id,
)

print(followup.output_text)
```

This is the main practical difference from
[`/v1/chat/completions`](/documentation/build-with-asi-one/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

#### cURL

```bash
# 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"
```

#### Python

```python
# Stop a run that is still in progress
client.responses.cancel("resp_ffbdb0b00b324505b1a46b92d4e1f5d2")

# Remove a stored response
client.responses.delete("resp_ffbdb0b00b324505b1a46b92d4e1f5d2")
```

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:

```json
{
  "model": "asi1",
  "input": [
    { "role": "user", "content": [{ "type": "input_text", "text": "What is agentic AI?" }] },
    { "role": "assistant", "content": [{ "type": "output_text", "text": "Agentic AI describes systems that..." }] },
    { "role": "user", "content": [{ "type": "input_text", "text": "How is that different from a chatbot?" }] }
  ]
}
```

## Image input

Send an image as an `input_image` part alongside your text. Every model takes
image input, under the same rules as
[`/v1/chat/completions`](/documentation/build-with-asi-one/chat-completions#image-input):
inline as a `data:` URI, up to 20 MB per image once decoded, and up to ten
images per request.

```json
{
  "model": "asi1",
  "input": [
    {
      "role": "user",
      "content": [
        { "type": "input_text", "text": "What is in this image?" },
        { "type": "input_image", "image_url": "data:image/jpeg;base64,<BASE64_ENCODED_IMAGE>" }
      ]
    }
  ]
}
```

`detail` is optional and defaults to `auto`; `low`, `high` and `original` are
also accepted. An `input_image` identified by `file_id` is not supported; send
the image in `image_url`.

Images belong on an input role. An `input_image` on an assistant message is
rejected, because an assistant turn has no stored image to read back. Send the
image in the turn that asks about it: when a request replays earlier turns that
also carried images, the per-request limit is spent on the most recent images
first, so the one you are asking about is never the one left out.

An image that cannot be read is rejected rather than quietly left out, so a
`200` means every image you sent was used. The request returns `400` with
`code: "unsupported_content_part"` and `param` naming the part, in this
endpoint's own vocabulary (`input[0].content[1]`), for an `input_image` carrying
no URL, base64 that does not decode, an image over 20 MB once decoded, more
images than the model accepts, a host ASI:One does not fetch from, and any
content part that is not `input_text`, `output_text` or `input_image`. See
[Errors](/documentation/build-with-asi-one/errors).

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](/documentation/build-with-asi-one/reasoning#reasoning-on-the-responses-api).

Tool calling works on both endpoints, and this one additionally accepts the flat
Responses tool shape alongside the nested one. See [Tool Calling](/documentation/build-with-asi-one/tool-calling#on-the-responses-api).

Structured output uses `text.format` here rather than `response_format`, taking
the same JSON schema. See [Structured Data](/documentation/build-with-asi-one/structured-data#on-the-responses-api).

`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](/documentation/build-with-asi-one/planner) runs on
[`/v1/chat/completions`](/documentation/build-with-asi-one/chat-completions).

## Next steps

1. **[Chat Completions API](/documentation/build-with-asi-one/chat-completions)** - The stateless endpoint, and where planner mode runs
2. **[OpenAI Compatibility](/documentation/build-with-asi-one/openai-compatibility)** - Which parameters are supported on each endpoint
3. **[Tool Calling](/documentation/build-with-asi-one/tool-calling)** - Let the model call your own functions
4. **[Errors and Rate Limits](/documentation/build-with-asi-one/errors)** - What each status code means and what to retry