# Responses API

POST /v1/responses: the OpenAI Responses format with stored state, background runs and enforced tool rules.

## Responses API compatibility

`POST /v1/responses` follows the OpenAI Responses API: the same request fields, output items,
streaming events, error shapes and stored state, so OpenAI SDKs (`responses.create`,
`responses.stream`, `responses.parse`, `responses.retrieve`, `conversations`) and the Agents
SDKs work by changing the base URL. As on OpenAI, responses are stored by default, so you can
continue a conversation with `previous_response_id` or a `conversation`, run a response in the
`background` and retrieve it later. Send `store: false` to store nothing and send the whole
conversation as `input` each turn instead. What is stored, for how long, and how to turn storage
off for your account: see Data retention below.

**Tool rules are guaranteed.** When a request sets `strict: true` on a function tool, uses
`tool_choice` (`required`, a named tool or `allowed_tools`) or sets
`parallel_tool_calls: false`, OpenScale checks every tool call before you see it: arguments
match the JSON schema (types, `required`, `additionalProperties: false`, `enum`, `format`,
`pattern`), only allowed tools are called, and at most one call comes back when parallel calls
are off. A reply that breaks a rule is stopped and retried, and never returned; if no valid
reply arrives within three attempts the request fails with `500` and
`code: tool_call_validation_failed` (in a stream: an `error` event, then
`response.failed`). Failed attempts are not billed. Streams with tool rules send reasoning as
it is generated, and text and tool calls once they have been checked. A
`strict: true` schema must follow the Structured Outputs rules (every object sets
`additionalProperties: false` and lists all properties in `required`; no `allOf`, `not` or
`if`), or the request is refused with `400` naming the parameter. Function tools without
`strict` are not checked and read back as `strict: false`.

| Field or tool | Status |
| --- | --- |
| `input` (messages, function, custom, shell, local shell, apply patch and tool search items, reasoning items), `instructions`, `stream`, `temperature`, `top_p`, `max_output_tokens` (16 to the model limit), `text.format`, `reasoning` | Supported |
| `tools`: `function` (with `strict`), `custom`, `namespace`, `shell`, `local_shell`, `apply_patch`, `tool_search` with `execution: "client"`, `defer_loading` | Supported (all run by your client) |
| `tool_choice`: `auto`, `none`, `required`, a named tool, `allowed_tools`; `parallel_tool_calls` | Supported and enforced |
| `top_logprobs`, `include: ["message.output_text.logprobs"]` | Supported |
| `phase` on assistant messages | Supported: every assistant `message` in the output has `phase`: `commentary` for text the model wrote before a tool call in the same response (always, when `tool_choice` forces a call), `final_answer` otherwise. In a stream it is on `response.output_item.done` (and already on `response.output_item.added` when `tool_choice` forces a call). Assistant messages you send back in `input` may keep it |
| `include: ["reasoning.encrypted_content"]` | Supported (reasoning always comes back with `encrypted_content`, readable `reasoning_text` content, and a summary when `reasoning.summary` is set) |
| Images (`input_image` with a URL or data URL, `detail`), including images returned in `function_call_output` | Supported |
| `POST /v1/responses/input_tokens` | Supported (exact counts for text and tools; images and models whose tokenizer we do not carry are estimated) |
| `prompt_cache_options.prewarm` | Supported (fills the prompt cache and returns a completed response with no output) |
| `metadata`, `user`, `safety_identifier`, `prompt_cache_key`, `max_tool_calls` | Accepted and echoed in the response |
| `service_tier` | `priority` runs the request in fast mode when it is available (see Fast mode); any other value runs at the standard tier. The response reports the tier that served it |
| `store` | Supported. Defaults to `true`: the response is stored for 30 days. `false` stores nothing |
| `previous_response_id` | Supported: the earlier responses' input and output items come before your input (their `instructions` do not carry over). An id that is unknown, deleted, past its 30 days or another account's is a `400` with `code: previous_response_not_found` |
| `conversation` and the Conversations API (`/v1/conversations` and its items) | Supported: the conversation's items come before your input, and the input and output are added to it when the response finishes. Cannot be combined with `previous_response_id` |
| `background: true` | Supported: answered at once with a `queued` response that runs to the end whether or not you stay connected (up to 29 minutes). Poll `GET /v1/responses/{id}`, resume its stream with `?stream=true&starting_after=`, or cancel it with `POST /v1/responses/{id}/cancel` |
| `item_reference` items | Supported for items of your stored responses and conversations |
| `GET` and `DELETE /v1/responses/{id}`, `GET /v1/responses/{id}/input_items` | Supported |
| `web_search`, `web_search_preview`, a hosted `tool_search` | Left out of the request and named in the `x-openscale-ignored-tools` response header (deferred tools are then loaded directly) |
| `file_search`, `code_interpreter`, `image_generation`, `computer`, `computer_use_preview`, `mcp`, `programmatic_tool_calling` | Refused with `400` (`param: tools[i].type`); define the capability as a function tool instead |
| `input_file` with `file_data` (a base64 data URL) or `file_url` (public `https` URLs) | Supported for PDFs (their text, page by page), images (read as `input_image`) and text files (txt, md, csv, json, html, xml, yaml, code). Up to 20 MB and 500 pages a file, 20 files and 50 MB a request. Other types (Word, Excel, audio) are a `400` naming the parameter. Stored responses keep the text read from a file, not the file |
| `prompt` (`id`, `version`, `variables`) | Supported with prompts you save through `/v1/prompts` (see Prompts below). Variables fill `{{name}}` placeholders with text, or with an `input_image` or `input_file` in user messages. An unknown prompt or version, a missing variable or one the prompt does not use is a `400` |
| `POST /v1/responses/compact`, `compaction` items | Supported: returns `response.compaction` with your user messages and a `compaction` item that stands for the rest; send that output as the next input. A compaction item that OpenScale did not create, or that was changed, is a `400` |
| `context_management: [{"type": "compaction", "compact_threshold": n}]`, `compaction_trigger` items | Supported: past `n` input tokens the conversation is summarized first; the response starts with a `compaction` item (streamed with `response.compaction.compacting`) that replaces everything before it in later turns, and its usage includes the summary. A final `compaction_trigger` item returns just the compaction |
| `truncation: "auto"` | Supported: when the input does not fit the context window, the oldest items are dropped (a tool call is always dropped with its output, reasoning with its turn); your instructions and newest item are kept. `disabled` (the default) answers `400` with `code: context_length_exceeded` |
| WebSocket mode (`response.create` over `wss://`) | Not available yet; use HTTP streaming (`stream: true`) |
| Images and files by `file_id` | Refused with `400` naming the parameter; send them by URL or as data |

Every refusal uses the OpenAI error shape `{"error": {"message", "type", "param", "code"}}`
with `param` set to the field at fault.

## Endpoints

- [Count input tokens](#count-input-tokens): `POST https://api.openscale.so/v1/responses/input_tokens`
- [Create a response](#create-a-response): `POST https://api.openscale.so/v1/responses`
- [Retrieve a response](#retrieve-a-response): `GET https://api.openscale.so/v1/responses/{response_id}`
- [Delete a response](#delete-a-response): `DELETE https://api.openscale.so/v1/responses/{response_id}`
- [Cancel a background response](#cancel-a-background-response): `POST https://api.openscale.so/v1/responses/{response_id}/cancel`
- [List input items](#list-input-items): `GET https://api.openscale.so/v1/responses/{response_id}/input_items`
- [Compact a conversation](#compact-a-conversation): `POST https://api.openscale.so/v1/responses/compact`

### Count input tokens

`POST https://api.openscale.so/v1/responses/input_tokens`

Count the input tokens a Responses request would use, without running the model. Takes the same body as Create a response and counts it with the model tokenizer: exact for text and tools, estimated for images and for models whose tokenizer we do not carry. Free.

#### Example request

```bash
curl https://api.openscale.so/v1/responses/input_tokens \
  -H "Authorization: Bearer $OPENSCALE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "deepseek/deepseek-v4.1-flash",
  "input": "Hello from OpenScale"
}'
```

#### Response

`200`: The input token count.

| Field | Type | Description |
| --- | --- | --- |
| `object` | string | Always `response.input_tokens`. |
| `input_tokens` | integer |  |

#### Errors

| Status | Meaning |
| --- | --- |
| 400 | The request cannot be served; `param` names the field. |
| 401 | Missing or invalid API key. |

### Create a response

`POST https://api.openscale.so/v1/responses`

Create a response from a string or an array of input items (messages, tool call and tool output items, and `reasoning` items from an earlier turn). Function, custom, namespace, shell, local shell, apply patch and client tool search tools are supported, and `strict`, `tool_choice` (including `allowed_tools`) and `parallel_tool_calls: false` are enforced. Reasoning comes back as a `reasoning` item with `encrypted_content` you can send back unchanged, readable `reasoning_text` content, and a summary when `reasoning.summary` is set. Responses are stored for 30 days by default (`store`), so a later request can continue from one with `previous_response_id`, or keep its turns in a `conversation`; with `background: true` the response runs on its own and is retrieved later. See the Responses API compatibility table and Data retention in the introduction.

#### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | Yes | One of `deepseek/deepseek-v4.1-flash`, `z-ai/glm-5.3-flash`, `deepseek/deepseek-v4.1-flash-1017`, `deepseek/deepseek-v4.1-flash-0910`, `z-ai/glm-5.3-flash-0828`, `deepseek/deepseek-v4.1-flash:fast`, `deepseek/deepseek-v4.1-flash-1017:fast`, `deepseek/deepseek-v4.1-flash-0910:fast`. |
| `input` | string or array of object | Yes |  |
| `instructions` | string | No |  |
| `tools` | array of object | No | `function` (with `strict`), `custom`, `namespace`, `shell`, `local_shell`, `apply_patch` and client `tool_search` tools. Web search is left out (see `x-openscale-ignored-tools`); other hosted tools are refused. |
| `tool_choice` | any | No | `auto`, `none`, `required`, a named tool, or `allowed_tools`. Enforced: a reply that breaks it is retried and never returned. |
| `parallel_tool_calls` | boolean | No |  |
| `text` | object | No | Structured outputs: `{ "format": { "type": "json_schema", ... } }` or `json_object`. `json_object` works whether or not your prompt mentions JSON. |
| `max_output_tokens` | integer | No |  |
| `temperature` | number | No | From 0 to 2. |
| `top_p` | number | No | From 0 to 1. |
| `stream` | boolean | No | Default `false`. |
| `metadata` | object | No |  |
| `store` | boolean | No | Store the response for 30 days, for `previous_response_id`, retrieval and `input_items`. `false` stores nothing. Always `false` when the account has zero data retention on. Default `true`. |
| `previous_response_id` | string | No | Continue from a stored response: its input and output (and those of the responses before it) come before `input`. Its `instructions` do not carry over. |
| `conversation` | string or object | No | A conversation id (or `{ "id": ... }`): its items come before `input`, and the input and output are added to it when the response finishes. |
| `background` | boolean | No | Run the response on its own: answered at once with a `queued` response; retrieve it, stream it, or cancel it by id. Default `false`. |
| `user` | string | No |  |
| `reasoning` | object | No | Reasoning control: `effort` (`none` to `xhigh`) and `summary` (`auto`, `concise` or `detailed` returns a readable summary). |

#### Example request

```bash
curl https://api.openscale.so/v1/responses \
  -H "Authorization: Bearer $OPENSCALE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "deepseek/deepseek-v4.1-flash",
  "input": "Hello from OpenScale",
  "store": false
}'
```

#### Response

`200`: The response, or a typed SSE stream when `stream` is true.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Example: `resp_example`. |
| `object` | string | Always `response`. |
| `created_at` | integer |  |
| `status` | string | One of `completed`, `incomplete`, `failed`, `in_progress`, `queued`, `cancelled`. |
| `model` | string | One of `deepseek/deepseek-v4.1-flash`, `z-ai/glm-5.3-flash`, `deepseek/deepseek-v4.1-flash-1017`, `deepseek/deepseek-v4.1-flash-0910`, `z-ai/glm-5.3-flash-0828`, `deepseek/deepseek-v4.1-flash:fast`, `deepseek/deepseek-v4.1-flash-1017:fast`, `deepseek/deepseek-v4.1-flash-0910:fast`. |
| `output` | array of object |  |
| `output[].type` | string | Always `message`. |
| `output[].id` | string | Example: `msg_example`. |
| `output[].role` | string | Always `assistant`. |
| `output[].status` | string | Always `completed`. |
| `output[].content` | array of object |  |
| `output[].phase` | string | `commentary`: text written before a tool call in the same response. `final_answer`: the reply. Assistant messages sent back in `input` may keep it. One of `commentary`, `final_answer`. |
| `usage` | object |  |
| `usage.input_tokens` | integer |  |
| `usage.input_tokens_details` | object |  |
| `usage.output_tokens` | integer |  |
| `usage.output_tokens_details` | object |  |
| `usage.total_tokens` | integer |  |
| `service_tier` | string | The tier that served the response: `priority` in fast mode. One of `default`, `priority`. |

```json
{
  "id": "resp_example",
  "object": "response",
  "created_at": 1790078324,
  "status": "completed",
  "model": "deepseek/deepseek-v4.1-flash",
  "output": [
    {
      "type": "message",
      "id": "msg_example",
      "role": "assistant",
      "status": "completed",
      "content": [
        {
          "type": "output_text",
          "text": "Hello!",
          "annotations": []
        }
      ],
      "phase": "final_answer"
    }
  ],
  "usage": {
    "input_tokens": 5,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens": 2,
    "output_tokens_details": {
      "reasoning_tokens": 0
    },
    "total_tokens": 7
  }
}
```

With `stream: true`: Typed events: response.created, response.in_progress, response.output_item.added and .done, response.content_part.added and .done, response.output_text.delta and .done, response.reasoning_summary_part and response.reasoning_summary_text events, response.function_call_arguments.delta and .done, response.custom_tool_call_input.done, and response.completed, response.incomplete or response.failed.

#### Errors

| Status | Meaning |
| --- | --- |
| 400 | Invalid request; `param` names the field. `previous_response_not_found` when the previous response is unknown, deleted or past its 30 days. |
| 401 | Missing or invalid API key. |
| 402 | Empty balance, or the key reached its spend limit. |
| 404 | Unknown model id. |
| 429 | Rate limit crossed or model at capacity. |
| 502 | The model is temporarily unavailable. |
| 504 | The model did not respond in time. |

### Retrieve a response

`GET https://api.openscale.so/v1/responses/{response_id}`

A stored response, as it was returned (status `queued` or `in_progress` while a background response runs). With `stream=true` (background responses only), its stream events from the start, or after `starting_after`, then each new one as it happens.

#### Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `response_id` | string | Yes |  |

#### Query parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `stream` | boolean | No | Stream a background response as SSE events. |
| `starting_after` | integer | No | With `stream`: the sequence number to resume after. |

#### Example request

```bash
curl https://api.openscale.so/v1/responses/$RESPONSE_ID \
  -H "Authorization: Bearer $OPENSCALE_API_KEY"
```

#### Response

`200`: The response, or its events when `stream` is true.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Example: `resp_example`. |
| `object` | string | Always `response`. |
| `created_at` | integer |  |
| `status` | string | One of `completed`, `incomplete`, `failed`, `in_progress`, `queued`, `cancelled`. |
| `model` | string | One of `deepseek/deepseek-v4.1-flash`, `z-ai/glm-5.3-flash`, `deepseek/deepseek-v4.1-flash-1017`, `deepseek/deepseek-v4.1-flash-0910`, `z-ai/glm-5.3-flash-0828`, `deepseek/deepseek-v4.1-flash:fast`, `deepseek/deepseek-v4.1-flash-1017:fast`, `deepseek/deepseek-v4.1-flash-0910:fast`. |
| `output` | array of object |  |
| `output[].type` | string | Always `message`. |
| `output[].id` | string | Example: `msg_example`. |
| `output[].role` | string | Always `assistant`. |
| `output[].status` | string | Always `completed`. |
| `output[].content` | array of object |  |
| `output[].phase` | string | `commentary`: text written before a tool call in the same response. `final_answer`: the reply. Assistant messages sent back in `input` may keep it. One of `commentary`, `final_answer`. |
| `usage` | object |  |
| `usage.input_tokens` | integer |  |
| `usage.input_tokens_details` | object |  |
| `usage.output_tokens` | integer |  |
| `usage.output_tokens_details` | object |  |
| `usage.total_tokens` | integer |  |
| `service_tier` | string | The tier that served the response: `priority` in fast mode. One of `default`, `priority`. |

#### Errors

| Status | Meaning |
| --- | --- |
| 404 | No stored response with this id in your account (never stored, deleted or past its 30 days). |

### Delete a response

`DELETE https://api.openscale.so/v1/responses/{response_id}`

Delete a stored response, its input and its output at once. A running background response is cancelled.

#### Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `response_id` | string | Yes |  |

#### Example request

```bash
curl -X DELETE https://api.openscale.so/v1/responses/$RESPONSE_ID \
  -H "Authorization: Bearer $OPENSCALE_API_KEY"
```

#### Response

`200`: Deleted.

```json
{
  "id": "resp_example",
  "object": "response.deleted",
  "deleted": true
}
```

#### Errors

| Status | Meaning |
| --- | --- |
| 404 | No stored response with this id. |

### Cancel a background response

`POST https://api.openscale.so/v1/responses/{response_id}/cancel`

Stop a queued or running background response; it ends with status `cancelled` and is not billed for unfinished work. Cancelling again, or cancelling a finished response, returns it unchanged.

#### Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `response_id` | string | Yes |  |

#### Example request

```bash
curl -X POST https://api.openscale.so/v1/responses/$RESPONSE_ID/cancel \
  -H "Authorization: Bearer $OPENSCALE_API_KEY"
```

#### Response

`200`: The response.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Example: `resp_example`. |
| `object` | string | Always `response`. |
| `created_at` | integer |  |
| `status` | string | One of `completed`, `incomplete`, `failed`, `in_progress`, `queued`, `cancelled`. |
| `model` | string | One of `deepseek/deepseek-v4.1-flash`, `z-ai/glm-5.3-flash`, `deepseek/deepseek-v4.1-flash-1017`, `deepseek/deepseek-v4.1-flash-0910`, `z-ai/glm-5.3-flash-0828`, `deepseek/deepseek-v4.1-flash:fast`, `deepseek/deepseek-v4.1-flash-1017:fast`, `deepseek/deepseek-v4.1-flash-0910:fast`. |
| `output` | array of object |  |
| `output[].type` | string | Always `message`. |
| `output[].id` | string | Example: `msg_example`. |
| `output[].role` | string | Always `assistant`. |
| `output[].status` | string | Always `completed`. |
| `output[].content` | array of object |  |
| `output[].phase` | string | `commentary`: text written before a tool call in the same response. `final_answer`: the reply. Assistant messages sent back in `input` may keep it. One of `commentary`, `final_answer`. |
| `usage` | object |  |
| `usage.input_tokens` | integer |  |
| `usage.input_tokens_details` | object |  |
| `usage.output_tokens` | integer |  |
| `usage.output_tokens_details` | object |  |
| `usage.total_tokens` | integer |  |
| `service_tier` | string | The tier that served the response: `priority` in fast mode. One of `default`, `priority`. |

#### Errors

| Status | Meaning |
| --- | --- |
| 400 | The response was not created in the background. |
| 404 | No stored response with this id. |

### List input items

`GET https://api.openscale.so/v1/responses/{response_id}/input_items`

The input items of a stored response, each with its id. Paged with `limit` (1 to 100, default 20), `order` (`desc` by default) and `after` (an item id).

#### Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `response_id` | string | Yes |  |

#### Query parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | integer | No | From 1 to 100. Default `20`. |
| `order` | string | No | One of `asc`, `desc`. Default `"desc"`. |
| `after` | string | No |  |

#### Example request

```bash
curl https://api.openscale.so/v1/responses/$RESPONSE_ID/input_items \
  -H "Authorization: Bearer $OPENSCALE_API_KEY"
```

#### Response

`200`: A list of items.

| Field | Type | Description |
| --- | --- | --- |
| `object` | string | Always `list`. |
| `data` | array of object |  |
| `first_id` | string or null |  |
| `last_id` | string or null |  |
| `has_more` | boolean |  |

#### Errors

| Status | Meaning |
| --- | --- |
| 404 | No stored response (or `after` item) with this id. |

### Compact a conversation

`POST https://api.openscale.so/v1/responses/compact`

Summarize a conversation so it can continue in fewer tokens. Takes `model` and the conversation as `input` (and/or `previous_response_id`, plus `instructions`). The output keeps your user messages and ends with a `compaction` item that stands for everything else; send the output, followed by new items, as the next input. Nothing is stored. Billed like a response.

#### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | Yes |  |
| `input` | any | No |  |
| `previous_response_id` | string | No |  |
| `instructions` | string | No |  |

#### Example request

```bash
curl https://api.openscale.so/v1/responses/compact \
  -H "Authorization: Bearer $OPENSCALE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "deepseek/deepseek-v4.1-flash",
  "input": [
    {
      "role": "user",
      "content": "My locker number is 7731."
    },
    {
      "type": "message",
      "role": "assistant",
      "content": [
        {
          "type": "output_text",
          "text": "Noted."
        }
      ]
    }
  ]
}'
```

#### Response

`200`: The compacted conversation.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string |  |
| `object` | string | Always `response.compaction`. |
| `created_at` | integer |  |
| `output` | array of object |  |
| `usage` | object |  |

#### Errors

| Status | Meaning |
| --- | --- |
| 400 | The request cannot be served; `param` names the field. |
| 401 | Missing or invalid API key. |

## More of the API reference

- [API reference](https://openscale.so/docs): base URLs, authentication and a first request.
- [Chat Completions](https://openscale.so/docs/chat-completions): POST /v1/chat/completions: the OpenAI Chat Completions format, streamed or not, with reasoning and tools.
- [Conversations](https://openscale.so/docs/conversations): Keep the turns of a Responses conversation on our side and continue it by id.
- [Prompts](https://openscale.so/docs/prompts): Save instructions, messages and settings once, then use them by id in the Responses API.
- [Anthropic Messages](https://openscale.so/docs/messages): POST /anthropic/v1/messages: the Anthropic Messages format, for Claude Code and Anthropic SDKs.
- [Models and pricing](https://openscale.so/docs/models): Model ids, prices per 1M tokens, context windows, output limits, versions and the live catalog endpoint.
- [Batch](https://openscale.so/docs/batch): Upload a JSONL file of chat completions, create a batch and collect the results when it finishes.
- [Errors](https://openscale.so/docs/errors): The error shape, every status code and code, and which ones to retry.
- [Rate limits](https://openscale.so/docs/rate-limits): Requests and tokens per minute, how a 429 tells you when to retry, and how to get more.
- [Data retention](https://openscale.so/docs/data-retention): What is stored, for how long, how to delete it, and zero data retention.
- [Interactive reference](https://openscale.so/docs/reference): try every endpoint in the browser.
- [OpenAPI document](https://openscale.so/openapi.json): the whole API as OpenAPI 3.1.
