Endpoints
Responses API (OpenAI Codex)
OpenAI-compatible Responses API for the Codex CLI and other Responses-format clients.
Pendra implements the OpenAI Responses API at
/v1/responses (also aliased at
/api/v1/responses) so the OpenAI Codex CLI works without
modification when pointed at Pendra. The Python and JavaScript examples
use the official OpenAI SDKs.
model
string
required
404.
input
string | array
required
store
boolean
reasoning
object
{"effort":"low"|"medium"|"high"} to set reasoning depth, or {"effort":"none"} (or the synonym "minimal") to skip thinking entirely. See Thinking.
previous_response_id is not supported and returns a
400.
Response
Pendra returns the OpenAI Responses envelope (see example).
output is an array of items; each message item
carries an array of content blocks. status is
completed on a clean finish, or incomplete when
the model hits a stop condition before exhausting
max_output_tokens.
Prompt caching
Pendra caches prompts automatically. When you send follow-up turns of the same conversation, it reuses the part of the prompt it already processed instead of reprocessing it from scratch, which makes the reply start sooner. You don't have to do anything to switch this on.
usage.input_tokens_details.cached_tokens tells you how many of
that request's input_tokens came from the cache — the same
field OpenAI returns, so a cache hit rate of
cached_tokens / input_tokens is comparable. It's always
present, and 0 on a first, cold request. A higher rate means a
faster first token on that request.
On a streamed response, the cache count lands on
response.completed; the response.created event
reports 0, because nothing is known about the prompt until the
model has read it.
Pin a Pendra model
Codex hard-codes OpenAI model names like gpt-5-codex and
gpt-5.5. Pendra serves open models under their own ids and
won't substitute one for a name it doesn't recognise, so those requests
come back as a 404 telling you to pin a model. Set
model in your Codex config to an id from
/models — see
Integrations → Codex.
Streaming
The Responses API uses its own event taxonomy
(response.output_text.delta, response.completed, etc.).
Pendra emits these events from streamed chat completions; the Codex CLI
consumes them directly.
event: response.created
data: {"type":"response.created","response":{"id":"resp_01HZ8b","object":"response","status":"in_progress","model":"qwen3.6:27b"}}
event: response.output_text.delta
data: {"type":"response.output_text.delta","item_id":"msg_01","output_index":0,"content_index":0,"delta":"Hello"}
event: response.completed
data: {"type":"response.completed","response":{"id":"resp_01HZ8b","status":"completed","usage":{"input_tokens":8,"output_tokens":2,"total_tokens":10,"input_tokens_details":{"cached_tokens":0}}}}
While the model is still reading a long prompt, Pendra sends an SSE comment
line (: keep-alive) every 15 seconds so the connection
isn't dropped as idle before the first event arrives. It carries no event
and no data, so a Server-Sent Events client — the Codex CLI included —
discards it; only hand-rolled parsers need to skip lines starting with
:.
If you pin a worker with X-Pendra-Worker-Id and it can't serve
the request — it doesn't exist, doesn't have the model, or is running a
version too old for this endpoint — you get a normal HTTP error response
instead of a stream, so check the status code before you start reading
events. A worker that's merely busy, or not connected yet, still reports
in-band as an error event once the stream has started. See
Errors & rate limits.
Quick start
The full Codex setup — config file, env vars, and a model pin — lives in Integrations → Codex.