# Responses

Create a model response with the OpenAI Responses format. This is a stateless subset built for coding agents such as Codex, and the official OpenAI SDKs work once you point the base URL at `https://kenari.id/v1`. For Codex configuration, see [Codex](/en/docs/codex).

`POST /v1/responses`

Authenticate with `Authorization: Bearer kn-...`. See [Authentication and API keys](/en/docs/authentication).

## Stateless by design

kenari stores nothing between requests, so send the whole conversation in `input` every time.

- A `previous_response_id` other than `null` returns 400.
- `store` is accepted and ignored.
- There are no endpoints to retrieve, list or delete a response.

## Request

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | Yes | A catalog id, a `:free` id, a route name or a `byok/` id. The same values as in [Chat completions](/en/docs/chat#choose-a-model). |
| `input` | string or array | Yes | A string is one user message. An array holds [input items](#input-items). It must not be empty. |
| `instructions` | string | No | System instructions. Merged with any leading `developer` items. |
| `tools` | array | No | Flat function tools, each with `type` set to `function`, plus `name`, `description` and `parameters`. A function tool without `name` returns 400. kenari server tools such as `{"type": "kenari:web_search"}` also work. Every other tool type is dropped without an error. See [Function calling](/en/docs/function-calling) and [Server tools](/en/docs/server-tools). |
| `tool_choice` | string or object | No | `auto`, `none`, `required` or `{"type": "function", "name": "get_weather"}` to force one function. Anything else returns 400. |
| `reasoning` | object or null | No | `{"effort": "low"}`. Levels are `none`, `minimal`, `low`, `medium`, `high`, `xhigh` and `max`. `summary` is accepted and ignored. See [Reasoning](/en/docs/reasoning). |
| `max_output_tokens` | integer | No | Upper limit on generated tokens, reasoning included. Default 65,536. |
| `temperature` | number | No | Sampling temperature. |
| `top_p` | number | No | Nucleus sampling. |
| `stream` | boolean | No | Set `true` to receive Server-Sent Events. See [Streaming](/en/docs/streaming). |
| `text` | object | No | `{"format": {"type": "text"}}` (the default), `{"format": {"type": "json_object"}}`, or `{"format": {"type": "json_schema", "name": "...", "schema": {...}, "strict": true}}` for JSON output. Any other type returns 400. See [Structured output](/en/docs/structured-output). |
| `prompt_cache_key` | string | No | A stable id for one conversation, so follow-up requests reuse its cached prefix. Up to 512 bytes. Longer or blank values are ignored. See [Prompt caching](/en/docs/prompt-caching). |
| `previous_response_id` | string or null | No | Must be absent or `null`. |
| `store` | boolean | No | Accepted and ignored. |
| `include` | array | No | Accepted and ignored. |
| `metadata` | object | No | Accepted and ignored. |
| `parallel_tool_calls` | boolean | No | Accepted and ignored. |

kenari does not read the routing fields `provider`, `models` and `route`, or `plugins`, on this endpoint. It never passes them on, whether the request runs on your own key or on any other model.

### Input items

| Item | Fields | Notes |
| --- | --- | --- |
| Message | `role`, `content` | `role` is `system`, `developer`, `user` or `assistant`. `type` may be omitted. `content` is a string or an array of parts. |
| Function call | `type` set to `function_call`, `call_id`, `name`, `arguments` | A call the model made earlier. `arguments` is a JSON string. |
| Function result | `type` set to `function_call_output`, `call_id`, `output` | The result for a call. `output` is a string or an array of text parts. |
| Reasoning | `type` set to `reasoning`, `summary` (an array) | Earlier reasoning, handed back to the model. |

Leading `system` and `developer` messages merge into the system prompt. Unknown item types are dropped without an error, and any other `role` returns 400.

Message parts are `input_text` and `output_text`, plus `input_image` with an `image_url` string, which is accepted only in `user` and `assistant` messages (in a `developer` message it returns 400). An `input_file` part returns 400. For documents, use [Chat completions](/en/docs/chat): a `file` part works on any model. See [Images and files as input](/en/docs/vision) and [Read documents](/en/docs/documents).

## Response

The response is a `response` object. Its `output` array holds the items the model produced, in order.

```json
{
  "id": "resp_4f1c9a2e7b3d5a60",
  "object": "response",
  "created_at": 1790000000,
  "status": "completed",
  "model": "step-3-7-flash:free",
  "output": [
    {
      "type": "message",
      "id": "msg_1",
      "role": "assistant",
      "content": [{ "type": "output_text", "text": "Hello! How can I help?" }]
    }
  ],
  "usage": {
    "input_tokens": 9,
    "output_tokens": 12,
    "total_tokens": 21,
    "input_tokens_details": { "cached_tokens": 0 }
  },
  "text": { "format": { "type": "text" } }
}
```

| Output item | Description |
| --- | --- |
| `reasoning` | The model's reasoning as one `summary_text` entry. First in the array, and only present when the model reasoned. |
| `message` | The answer, in an `output_text` part. |
| `function_call` | A function the model wants you to run: `call_id`, `name` and `arguments` as a JSON string. Run it and send a `function_call_output` item with the same `call_id` in the next request. |

`status` is `completed`, or `incomplete` with `incomplete_details.reason` set to `max_output_tokens` when the limit cut the answer short. `text.format` echoes the format you asked for, `{"type": "text"}` by default. `usage.input_tokens_details.cached_tokens` counts input served from cache and is part of `input_tokens`.

After you run a function, send a follow-up request that carries the earlier call and its result in `input`:

```json
{
  "model": "step-3-7-flash:free",
  "input": [
    { "role": "user", "content": "What is the weather in Jakarta?" },
    {
      "type": "function_call",
      "call_id": "call_1",
      "name": "get_weather",
      "arguments": "{\"city\": \"Jakarta\"}"
    },
    { "type": "function_call_output", "call_id": "call_1", "output": "31 C and sunny" }
  ],
  "tools": [
    {
      "type": "function",
      "name": "get_weather",
      "description": "Get the current weather for a city.",
      "parameters": {
        "type": "object",
        "properties": { "city": { "type": "string" } },
        "required": ["city"]
      }
    }
  ]
}
```

## Streaming

Set `"stream": true` to receive the response as Server-Sent Events. The event sequence is in [Streaming](/en/docs/streaming#responses-stream).

## Examples

### curl

```bash
curl https://kenari.id/v1/responses \
  -H "Authorization: Bearer $KENARI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "step-3-7-flash:free",
    "instructions": "You answer briefly.",
    "input": "Hello!"
  }'
```

### Python

```python
import os
from openai import OpenAI

client = OpenAI(base_url="https://kenari.id/v1", api_key=os.environ["KENARI_API_KEY"])

response = client.responses.create(
    model="step-3-7-flash:free",
    instructions="You answer briefly.",
    input="Hello!",
)
print(response.output_text)
```

### JavaScript

```js
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://kenari.id/v1",
  apiKey: process.env.KENARI_API_KEY,
});

const response = await client.responses.create({
  model: "step-3-7-flash:free",
  instructions: "You answer briefly.",
  input: "Hello!",
});
console.log(response.output_text);
```

## Billing

Each request is billed per token at the model's Input, Output, Cache read and Cache write prices. `:free` models cost nothing. See [How billing works](/en/docs/billing).

## Errors

Errors use the OpenAI envelope: an `error` object with `message`, `type`, `param` and `code`. Only the errors specific to this endpoint are listed here. The rest, such as `model_not_found`, `context_length_exceeded` and `subscribers_only`, are in [Errors](/en/docs/errors).

| Status | Code | When |
| --- | --- | --- |
| 400 | `bad_request` | `input` is missing or empty. `previous_response_id` is not `null`. `text.format.type` is not `text`, `json_object` or `json_schema`. A message has an unknown `role`. An `input_file` part is present. A function tool has no `name`. `tool_choice` is not one of the supported forms. |
| 400 | `bad_request` | The model is not a chat model, such as an image or speech model. The message names the endpoint to use instead. |

## See also

- [Codex](/en/docs/codex)
- [Streaming](/en/docs/streaming)
- [Function calling](/en/docs/function-calling)
- [Chat completions](/en/docs/chat)
