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 against Keln out of the box (setup below), and so does any SDK or tool built on the Responses schema.
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 lives 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). The stateful features are refused 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 |
max_output_tokens |
output cap. As everywhere on Keln, the cap covers reasoning and answer together, don't set it small on a reasoning model |
reasoning: {effort, summary} |
same contract as reasoning_effort 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) |
prompt_cache_key |
send it, a stable per-session value keeps a session's turns on warm capacity, 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¶
Add Keln as a provider in ~/.codex/config.toml:
model = "moonshotai/kimi-k3" # any /v1/models id with the Tools badge
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 that matter in practice:
- Model choice: pick a model with the Tools badge on Models & pricing.
- Retries and timeouts: Codex defaults are fine. Long prompts stream
response.createdimmediately, so Codex's idle timeout only fires on a genuinely dead connection. - Caching: Codex stamps
prompt_cache_keywith 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, 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.