Responses (OpenAI)
OpenAI Responses-format inbound adapter. OpenAI's Codex CLI only speaks /v1/responses, so this endpoint lets Codex and the Responses SDK attach.
OpenAI's Codex CLI only speaks the /v1/responses format. PleumRouter accepts requests in this format as-is, so you can attach Codex CLI (and the OpenAI Responses SDK) to PleumRouter.
Connecting#
Set the base URL to https://apirouter.pleum.ai/v1 and the API key to your plm_ key. In Codex, set wire_api = "responses" so it calls this endpoint.
OpenAI Responses API (Codex)
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "plm_xxxxxxxxxxxxxxxx",
baseURL: "https://apirouter.pleum.ai/v1",
});
const response = await client.responses.create({
model: "gpt-5.5",
input: "Why is the sky blue?",
});
console.log(response.output_text);OpenAI Responses API (Codex)
from openai import OpenAI
client = OpenAI(
api_key="plm_xxxxxxxxxxxxxxxx",
base_url="https://apirouter.pleum.ai/v1",
)
response = client.responses.create(
model="gpt-5.5",
input="Why is the sky blue?",
)
print(response.output_text)OpenAI Responses API (Codex)
curl https://apirouter.pleum.ai/v1/responses \
-H "Authorization: Bearer plm_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"input": "Why is the sky blue?"
}'POST /v1/responses/{request_id}/feedback is a separate routing-feedback endpoint, not Responses creation. It requires a dashboard-session JWT; an inference plm_ key cannot call it. request_id is the value of the X-Request-Id response header, and the body must include the required field rating (1 for a thumbs-up, -1 for a thumbs-down).curl https://apirouter.pleum.ai/v1/responses/$REQUEST_ID/feedback \
-H "Authorization: Bearer $PLEUM_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"rating": 1}'
# -> {"ok": true}Request body#
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Required | Model ID. See the full list at GET /v1/models. |
| input | string | array | Required | A string, or an array of items (message / function_call / function_call_output). |
| instructions | string | Optional | Prepended as a system message. |
| max_output_tokens | integer | Optional | Default 4096. A value above the model's maximum output (max_output) is lowered to that maximum instead of failing. The request returns 400 only when input plus output cannot fit the context window. |
| temperature | number | Optional | Sampling temperature. |
| top_p | number | Optional | Nucleus sampling. |
| stream | boolean | Optional | If true, returns a Responses SSE streaming response. |
| tools | array | Optional | Responses flat function tools ({type:"function", name, description, parameters}) are converted. Non-function tools (e.g. web_search) are silently dropped. |
| tool_choice | string | object | Optional | String auto | none | required, or {type:"function", name}. |
| parallel_tool_calls | boolean | Optional | Whether parallel tool calls are allowed. |
| metadata | object | Optional | Accepted but not used. |
modelstringRequiredinputstring | InputItem[]Required
typestringOptionalrolestringOptionalcontentstring | Part[]Optionalcall_idstringOptionalnamestringOptionalargumentsstringOptionaloutputstring | object[]OptionalinstructionsstringOptionalmax_output_tokensintegerOptionalMinimum 1. There is no global upper bound. A value above the model's max_output is lowered to that maximum instead of failing; a 400 is returned only when input plus output cannot fit the context window.
default: 4096
temperaturenumberOptionaltop_pnumberOptionalstreambooleanOptionaldefault: false
toolsobject[]Optional
typestringOptionalnamestringOptionalparametersobjectOptionaltool_choicestring | objectOptionalparallel_tool_callsbooleanOptionalmetadataobjectOptionalAccepted but not used.
reasoningobjectOptional
effortstringOptionalmodestringOptionalenum: standard pro
reasoning_effortstringOptionalreasoning_modestringOptionalenum: standard pro
service_tierstringOptionalPassed only to OpenAI-family providers. Models that support it list service_tier in supported_parameters on GET /v1/models.
enum: auto default flex background priority fast ultrafast
curl https://apirouter.pleum.ai/v1/responses \
-H "Authorization: Bearer $PLEUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"instructions": "You are a helpful assistant.",
"input": [
{
"type": "message",
"role": "user",
"content": "Hello"
}
],
"max_output_tokens": 4096,
"stream": false
}'Response#
A non-streaming response returns the assistant message in the output array. Each message's content consists of output_text parts, and usage includes input_tokens / output_tokens / total_tokens. Cost is returned in the X-Cost-Krw / X-Cost-Usd response headers, not the body. reasoning and prompt_cache_key are applied to the request. The other extra fields Codex sends (text, store, previous_response_id, include) are accepted but ignored.
{
"id": "resp_9f3c2b7a5e1d4c8a9b0e6d7f1a2b3c4d",
"object": "response",
"created_at": 1735689600,
"status": "completed",
"model": "gpt-5.5",
"output": [
{
"id": "msg_abc123",
"type": "message",
"status": "completed",
"role": "assistant",
"content": [
{"type": "output_text", "text": "Hi!", "annotations": []}
]
}
],
"usage": {
"input_tokens": 12,
"output_tokens": 5,
"total_tokens": 17
}
}Streaming#
With stream: true, Responses SSE events are sent — response.created, then response.in_progress, then response.output_item.added / response.content_part.added / response.output_text.delta, then response.output_text.done / response.output_item.done, and finally response.completed with the full usage. Codex reads completed items from response.output_item.done and determines termination from response.completed. There are no cost headers on a stream.
event: response.created
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_abc","object":"response","status":"in_progress","model":"gpt-5.5","output":[]}}
event: response.in_progress
data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_abc","object":"response","status":"in_progress","model":"gpt-5.5","output":[]}}
event: response.output_item.added
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_abc","type":"message","status":"in_progress","role":"assistant","content":[]}}
event: response.content_part.added
data: {"type":"response.content_part.added","sequence_number":3,"item_id":"msg_abc","output_index":0,"content_index":0,"part":{"type":"output_text","text":"","annotations":[]}}
event: response.output_text.delta
data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_abc","output_index":0,"content_index":0,"delta":"Hi"}
event: response.output_text.delta
data: {"type":"response.output_text.delta","sequence_number":5,"item_id":"msg_abc","output_index":0,"content_index":0,"delta":"!"}
event: response.output_text.done
data: {"type":"response.output_text.done","sequence_number":6,"item_id":"msg_abc","output_index":0,"content_index":0,"text":"Hi!"}
event: response.content_part.done
data: {"type":"response.content_part.done","sequence_number":7,"item_id":"msg_abc","output_index":0,"content_index":0,"part":{"type":"output_text","text":"Hi!","annotations":[]}}
event: response.output_item.done
data: {"type":"response.output_item.done","sequence_number":8,"output_index":0,"item":{"id":"msg_abc","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi!","annotations":[]}]}}
event: response.completed
data: {"type":"response.completed","sequence_number":9,"response":{"id":"resp_abc","object":"response","status":"completed","model":"gpt-5.5","output":[{"id":"msg_abc","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi!","annotations":[]}]}],"usage":{"input_tokens":12,"output_tokens":5,"total_tokens":17}}}Compact#
OpenAI server compact (POST /v1/responses/compact) uses opaque encrypted_content items. This gateway does not mint those items. The same chat auth returns 501 with the error code compact_not_supported. Use Codex local compact.
web_search are not forwarded and are silently dropped. Only {type:"function"} tools are converted and passed to the model.