# Responses (/api-v1/responses)



The Responses API is OpenAI's stateful generation surface. Zumik mirrors its shapes exactly: function tools, tool-call round trips, `previous_response_id` chaining, and Server-Sent Events streaming all work with the official SDK. The `input` field accepts either a bare string or the structured input-item array. Proprietary signal rides on [response headers](/api-reference/headers), never the body.

<Note>
  Raw input items are retained for [`/input_items`](#list-input-items) only when the project opts into full-fidelity retention. The default is metadata-only, so input items return an empty list. See [retention and QoS](/concepts/qos).
</Note>

## Create a response [#create-a-response]

```
POST https://api.zumik.ai/v1/responses
```

<ParamField body="model" type="string">
  A Zumik [alias](/concepts/model-aliases) such as `code.balanced`, or a concrete provider model.
</ParamField>

<ParamField body="input" type="string | array">
  Either a plain string prompt, or an array of structured input items (matching OpenAI's input-item shape). Item arrays may include `message` items, `function_call` items, and [`function_call_output` items](#function-calling) carrying tool results back to the model.
</ParamField>

<ParamField body="previous_response_id" type="string">
  Continue from a prior stored response instead of resending its turns. Its output — including any `function_call` items — is prepended as context.
</ParamField>

<ParamField body="tools" type="array">
  Function tools the model may call, in the Responses flat shape: `{"type": "function", "name": "...", "description": "...", "parameters": {...}, "strict": true}`. Only `function` tools are supported; hosted tool types (`web_search`, `file_search`, …) are rejected with a `400`. See [function calling](#function-calling).
</ParamField>

<ParamField body="tool_choice" type="string | object">
  `"auto"` (the provider default), `"none"`, `"required"`, or `{"type": "function", "name": "..."}` to force one specific function. Forcing a call without `tools` is rejected with a `400`.
</ParamField>

<ParamField body="temperature" type="number">
  Sampling temperature. Optional; passed through to the resolved provider.
</ParamField>

<ParamField body="top_p" type="number">
  Nucleus sampling. Must be greater than 0 and at most 1; out-of-range values are rejected with a `400` naming the parameter.
</ParamField>

<ParamField body="max_output_tokens" type="integer">
  Maximum tokens to generate. Optional; passed through to the resolved provider.
</ParamField>

<ParamField body="stream" type="boolean" default="false">
  When `true`, the response arrives as Responses-API Server-Sent Events (`text/event-stream`) instead of a single JSON body. See [streaming](#streaming).
</ParamField>

### Request [#request]

<CodeGroup>
  ```bash title="curl"
  curl https://api.zumik.ai/v1/responses \
    -H "Authorization: Bearer zk_live_..." \
    -H "Content-Type: application/json" \
    -d '{"model":"code.balanced","input":"Review the latest patch."}'
  ```

  ```python title="OpenAI SDK"
  from openai import OpenAI

  client = OpenAI(base_url="https://api.zumik.ai/v1", api_key="zk_live_...")

  resp = client.responses.create(
      model="code.balanced",
      input="Review the latest patch.",
  )
  print(resp.output_text)
  ```

  ```python title="Zumik SDK"
  from zumik import Zumik

  zk = Zumik(api_key="zk_live_...")

  resp = zk.responses.create(model="code.balanced", input="Review the latest patch.")
  print(resp.output[0].content[0].text)
  ```
</CodeGroup>

### Response [#response]

```json
{
  "id": "resp_01jy7n3q8v6m4k2x...",
  "object": "response",
  "created_at": 1750000123,
  "status": "completed",
  "model": "code.balanced",
  "output": [
    {
      "type": "message",
      "role": "assistant",
      "content": [
        { "type": "output_text", "text": "The patch looks correct; add a test for the empty case." }
      ]
    }
  ],
  "usage": {
    "input_tokens": 6,
    "output_tokens": 11,
    "total_tokens": 17
  }
}
```

<ResponseField name="id" type="string">
  The response id, `resp_...`.
</ResponseField>

<ResponseField name="object" type="string">
  Always `"response"`.
</ResponseField>

<ResponseField name="created_at" type="integer">
  Unix timestamp (seconds).
</ResponseField>

<ResponseField name="status" type="string">
  `completed`, or `cancelled` after a cancel.
</ResponseField>

<ResponseField name="output" type="array">
  Output items. Each `message` item carries a `role` and a `content` array of parts (`type: "output_text"` with `text`). When the model calls a tool, the array also carries [`function_call` items](#function-calling).
</ResponseField>

<ResponseField name="usage" type="object">
  `input_tokens`, `output_tokens`, `total_tokens`.
</ResponseField>

## Function calling [#function-calling]

Pass `tools` and the model can answer with `function_call` output items instead of (or alongside) a message. Execute the call on your side, then send the result back as a `function_call_output` input item. Tool-calling requests run on the same transparent execution path as [`/v1/chat/completions`](/api-v1/chat-completions), so they work on every provider — including Anthropic models via the built-in translation.

```python title="OpenAI SDK"
resp = client.responses.create(
    model="code.balanced",
    input="What is in src/?",
    tools=[{
        "type": "function",
        "name": "list_files",
        "description": "List the files in a directory",
        "parameters": {
            "type": "object",
            "properties": {"path": {"type": "string"}},
            "required": ["path"],
        },
    }],
)
```

The model's call arrives as a `function_call` item:

```json
{
  "output": [
    {
      "type": "function_call",
      "id": "fc_x7k2...",
      "call_id": "call_x7k2...",
      "name": "list_files",
      "arguments": "{\"path\":\"src/\"}",
      "status": "completed"
    }
  ]
}
```

Run the tool, then answer the call. The shortest loop chains from the stored response, so the prior tool calls don't need to be resent:

```python title="OpenAI SDK"
followup = client.responses.create(
    model="code.balanced",
    previous_response_id=resp.id,
    tools=tools,  # resend so the model can keep calling
    input=[{
        "type": "function_call_output",
        "call_id": resp.output[0].call_id,
        "output": "main.rs  lib.rs  providers.rs",
    }],
)
```

Parallel calls come back as multiple `function_call` items; answer each with its own `function_call_output` carrying the matching `call_id`.

<Note>
  Under the default metadata-only retention the *input* of the previous response is not stored, so a chained request rebuilds context from its stored output only. Agents that need the full conversation in view should send the item list back themselves — `message`, `function_call`, and `function_call_output` items in order, without `previous_response_id`. Both forms are accepted.
</Note>

## Streaming [#streaming]

Set `stream: true` to receive the response as the Responses SSE event protocol. Each event is an `event:` line naming the type and a `data:` line carrying the payload with a monotonic `sequence_number`. Billing is settled before the first event, so the usage in `response.completed` matches what you were charged — the same buffered model as [chat streaming](/guides/streaming).

The event sequence for a text response:

```
event: response.created
event: response.in_progress
event: response.output_item.added
event: response.content_part.added
event: response.output_text.delta        (one or more; deltas concatenate to the full text)
event: response.output_text.done
event: response.content_part.done
event: response.output_item.done
event: response.completed
```

A `function_call` item streams as `response.output_item.added`, one or more `response.function_call_arguments.delta` events, `response.function_call_arguments.done`, then `response.output_item.done`. The terminal `response.completed` event carries the full final response object; there is no `[DONE]` sentinel — the Responses protocol ends on `response.completed`.

```python title="OpenAI SDK"
stream = client.responses.create(
    model="code.balanced",
    input="Stream a short answer.",
    stream=True,
)
for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="")
```

## Retrieve a response [#retrieve-a-response]

```
GET https://api.zumik.ai/v1/responses/{response_id}
```

<ParamField path="response_id" type="string">
  The `resp_...` id.
</ParamField>

Returns the stored response object exactly as it was created. `404` if it does not exist for this project.

```bash
curl https://api.zumik.ai/v1/responses/resp_01jy... \
  -H "Authorization: Bearer zk_live_..."
```

## Delete a response [#delete-a-response]

```
DELETE https://api.zumik.ai/v1/responses/{response_id}
```

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

`404` if the response does not exist for this project.

## Cancel a response [#cancel-a-response]

```
POST https://api.zumik.ai/v1/responses/{response_id}/cancel
```

Marks the response `cancelled` and returns the updated object. `404` if it does not exist for this project.

```json
{ "id": "resp_01jy...", "object": "response", "status": "cancelled", "...": "..." }
```

## List input items [#list-input-items]

```
GET https://api.zumik.ai/v1/responses/{response_id}/input_items
```

Returns the input items that produced the response, as a [list](/api-reference/pagination).

```json
{
  "object": "list",
  "data": [
    { "type": "input_text", "text": "Review the latest patch." }
  ],
  "has_more": false
}
```

When the project's retention is metadata-only (the default), inputs were not stored, so `data` is an empty list rather than a `404`. A genuine unknown id returns `404`.

## Compact context [#compact-context]

```
POST https://api.zumik.ai/v1/responses/compact
```

Fold a long prior context into a shorter, **model-generated** summary to stay within a context
budget. The summary is produced through the broker (degrading to a deterministic summary when no
gateway is configured) and persisted as a reusable [`compaction_summary` artifact](/api-v2/artifacts)
you can reference later. Because it makes a model call, this endpoint is budget-gated like inference.

<ParamField body="input" type="string | array">
  The context (string or input items) to compact.
</ParamField>

<ParamField body="model" type="string">
  The model to summarize with. Defaults to `auto.cheapest`.
</ParamField>

```json
{
  "object": "response.compaction",
  "id": "cmp_01jy...",
  "artifact_id": "art_01jy...",
  "model": "gemini/gemini-2.0-flash",
  "summary": { "type": "compaction_summary", "text": "The agent chose Postgres, wrote the schema, ran tests, and fixed two failures." },
  "original_input_tokens": 1840,
  "compacted_input_tokens": 460,
  "live": true
}
```

<ResponseField name="object" type="string">
  Always `"response.compaction"`.
</ResponseField>

<ResponseField name="artifact_id" type="string">
  The persisted `compaction_summary` artifact (`art_...`) holding the summary, so it can be reused or referenced from a session event.
</ResponseField>

<ResponseField name="model" type="string">
  The resolved `provider/model` that produced the summary.
</ResponseField>

<ResponseField name="original_input_tokens" type="integer">
  Token count of the input before compaction.
</ResponseField>

<ResponseField name="compacted_input_tokens" type="integer">
  Token count after compaction; never greater than the original.
</ResponseField>

<ResponseField name="live" type="boolean">
  `true` when a model produced the summary; `false` on the deterministic degrade path.
</ResponseField>

For **session-aware** compaction — folding a branch's older turns into a checkpoint while keeping a
verbatim tail, with recovery — see [compacting a branch](/api-v2/snapshots#compact-a-branch).

## Count input tokens [#count-input-tokens]

```
POST https://api.zumik.ai/v1/responses/input_tokens
```

Estimate the token count of an input without running a generation.

<ParamField body="input" type="string | array">
  The input to count.
</ParamField>

```json
{ "object": "response.input_tokens", "input_tokens": 6 }
```

## Errors [#errors]

| HTTP | `code`              | When                                                                                                                 |
| ---- | ------------------- | -------------------------------------------------------------------------------------------------------------------- |
| 400  | (none)              | `top_p` out of range, an unsupported tool type, or a malformed `tool_choice`. The error names the offending `param`. |
| 401  | `invalid_api_key`   | Missing or invalid bearer key.                                                                                       |
| 402  | `credits_required`  | The prepaid credit balance is empty (create only).                                                                   |
| 404  | (none)              | The response id does not exist for this project.                                                                     |
| 429  | `quota_exceeded`    | Budget reached (create only).                                                                                        |
| 502  | (none)              | The upstream provider failed on a tool-calling request, which cannot degrade to a tool-less answer. Retry.           |
| 504  | `deadline_exceeded` | A QoS deadline elapsed (create only); not charged.                                                                   |

See the full [error reference](/api-reference/errors).
