# Responses API
`POST /v1/responses`, the OpenAI Responses schema, streaming and non-streaming, for every
catalog model. Same key, same models, same prices as chat completions.
This is the request shape agent tools use: [Codex](../codex.md) works with Keln without extra setup (see
below), and so does any SDK or tool built on the Responses schema.
```bash
curl https://api.keln.ai/v1/responses \
-H "Authorization: Bearer $KELN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "moonshotai/kimi-k3",
"input": "Say hello from Keln.",
"stream": true
}'
```
## Stateless by design
Keln serves the stateless Responses subset: conversation state stays in your client, and each
request carries the full `input`. That is exactly how Codex and most agent frameworks already
work (they send `store: false` on every call). Keln refuses the stateful features with named
errors:
| You send | You get |
|---|---|
| `store: true` | `400` with code `store_not_supported` |
| `previous_response_id` | `400` with code `previous_response_id_not_supported` |
| `input` containing an `item_reference` | `400` with code `item_reference_not_supported` |
Everything else about the request either works or fails with a named error.
## Parameters
| Parameters | Contract |
|---|---|
| `model`, `input` | required. `input` is a string or an item array: `message` items (`input_text` / `output_text` / `input_image` parts), `function_call`, and `function_call_output` items, the standard agent turn shape |
| `instructions` | honored, becomes the system context for the request |
| `stream` | typed Responses event stream, see [Streaming events](#streaming-events) |
| `max_output_tokens` | output cap. As everywhere on Keln, the cap covers reasoning and answer together, so do not set it small on a reasoning model |
| `reasoning: {effort, summary}` | same contract as [`reasoning_effort`](chat-completions.md#reasoning) on chat completions; `summary` is accepted (the reasoning trace is returned as reasoning output items either way) |
| `tools`, `tool_choice` | the flat Responses tool shape (`{"type": "function", "name": …, "parameters": …}`); `tool_choice` supports `auto` / `none` / `required` / `{"type": "function", "name": …}` |
| `text: {format: {type: "json_schema", …}}` | structured output, with the same validation contract as chat completions ([details](../capabilities/structured-outputs.md)) |
| `prompt_cache_key` | send it. A stable per-session value keeps a session's turns on the capacity that already has its context cached, so repeated context bills at the per-model [cached rate](../capabilities/caching.md) and starts faster. Codex sends its session id here automatically |
| `temperature`, `top_p`, `parallel_tool_calls`, `metadata`, `user` | same contracts as chat completions (`metadata` and `user` are accepted and never forwarded upstream, [ZDR](../capabilities/privacy.md)) |
| anything else | accepted and ignored, or refused with a named `400` where honoring it matters, an unknown key never breaks your request |
## Response object
Non-streaming requests return a full `response` object: `id`, `status`
(`completed` / `incomplete`), `output` (reasoning items, `message` items, `function_call`
items), and `usage` with `input_tokens`, `output_tokens`, `total_tokens`,
`input_tokens_details.cached_tokens`, and `output_tokens_details.reasoning_tokens`.
An output cut short by `max_output_tokens` comes back as `status: "incomplete"` with
`incomplete_details.reason: "max_output_tokens"`, the spec's shape.
## Streaming events
`stream: true` returns the typed Responses event stream: `response.created` →
`response.in_progress` → `response.output_item.added` → content/argument deltas →
`response.output_item.done` → `response.completed` (or `response.incomplete` /
`response.failed`), each event carrying its `event:` line and a gapless `sequence_number`.
`usage` arrives on the terminal event. A turn is over when you see `response.completed`,
the same rule Codex applies.
## Using Codex
Full walkthrough with screenshots: [Codex with Keln](../codex.md). The short version: add Keln as a
provider in `~/.codex/config.toml`:
```toml
model = "moonshotai/kimi-k3" # any catalog model with tool calling
model_provider = "keln"
[model_providers.keln]
name = "Keln"
base_url = "https://api.keln.ai/v1"
env_key = "KELN_API_KEY" # export KELN_API_KEY=sk-keln-…
```
Notes:
- **Model choice.** Pick a model with tool calling; the [Codex guide](../codex.md) lists them.
- **Retries and timeouts.** Codex defaults are fine. Long prompts stream `response.created`
immediately, so Codex's idle timeout only triggers on a dead connection.
- **Caching.** Codex sets `prompt_cache_key` to its session id on every turn, so long sessions
automatically bill repeated context at the cached rate.
- The ChatGPT desktop app's Codex shares the same `config.toml`, so the block above makes Keln
models available there too.
## Web search
Send OpenAI's hosted tool, `{ "type": "web_search" }`, and Keln runs the search: a `web_search_call`
item per search with its lifecycle events, citations on the answer, `usage.web_search_requests`,
and `action.sources` when you include `web_search_call.action.sources`. Codex sends the tool by
default, so it searches through Keln with no extra setup. Details and price on
[Web search](../capabilities/web-search.md).
Responses API
POST /v1/responses, the OpenAI Responses schema, streaming and non-streaming, for every
catalog model. Same key, same models, same prices as chat completions.
This is the request shape agent tools use: Codex works with Keln without extra setup (see
below), and so does any SDK or tool built on the Responses schema.
Keln serves the stateless Responses subset: conversation state stays in your client, and each
request carries the full input. That is exactly how Codex and most agent frameworks already
work (they send store: false on every call). Keln refuses the stateful features with named
errors:
You send
You get
store: true
400 with code store_not_supported
previous_response_id
400 with code previous_response_id_not_supported
input containing an item_reference
400 with code item_reference_not_supported
Everything else about the request either works or fails with a named error.
Parameters
Parameters
Contract
model, input
required. input is a string or an item array: message items (input_text / output_text / input_image parts), function_call, and function_call_output items, the standard agent turn shape
instructions
honored, becomes the system context for the request
structured output, with the same validation contract as chat completions (details)
prompt_cache_key
send it. A stable per-session value keeps a session's turns on the capacity that already has its context cached, so repeated context bills at the per-model cached rate and starts faster. Codex sends its session id here automatically
temperature, top_p, parallel_tool_calls, metadata, user
same contracts as chat completions (metadata and user are accepted and never forwarded upstream, ZDR)
anything else
accepted and ignored, or refused with a named 400 where honoring it matters, an unknown key never breaks your request
Response object
Non-streaming requests return a full response object: id, status
(completed / incomplete), output (reasoning items, message items, function_call
items), and usage with input_tokens, output_tokens, total_tokens,
input_tokens_details.cached_tokens, and output_tokens_details.reasoning_tokens.
An output cut short by max_output_tokens comes back as status: "incomplete" with
incomplete_details.reason: "max_output_tokens", the spec's shape.
Streaming events
stream: true returns the typed Responses event stream: response.created →
response.in_progress → response.output_item.added → content/argument deltas →
response.output_item.done → response.completed (or response.incomplete /
response.failed), each event carrying its event: line and a gapless sequence_number.
usage arrives on the terminal event. A turn is over when you see response.completed,
the same rule Codex applies.
Using Codex
Full walkthrough with screenshots: Codex with Keln. The short version: add Keln as a
provider in ~/.codex/config.toml:
model="moonshotai/kimi-k3"# any catalog model with tool callingmodel_provider="keln"[model_providers.keln]name="Keln"base_url="https://api.keln.ai/v1"env_key="KELN_API_KEY"# export KELN_API_KEY=sk-keln-…
Notes:
Model choice. Pick a model with tool calling; the Codex guide lists them.
Retries and timeouts. Codex defaults are fine. Long prompts stream response.created
immediately, so Codex's idle timeout only triggers on a dead connection.
Caching. Codex sets prompt_cache_key to its session id on every turn, so long sessions
automatically bill repeated context at the cached rate.
The ChatGPT desktop app's Codex shares the same config.toml, so the block above makes Keln
models available there too.
Web search
Send OpenAI's hosted tool, { "type": "web_search" }, and Keln runs the search: a web_search_call
item per search with its lifecycle events, citations on the answer, usage.web_search_requests,
and action.sources when you include web_search_call.action.sources. Codex sends the tool by
default, so it searches through Keln with no extra setup. Details and price on
Web search.