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 provides an inbound adapter that accepts this format and translates it into internal model calls, so you can attach Codex CLI (and the OpenAI Responses SDK) to PleumRouter as-is.
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.
export OPENAI_BASE_URL="https://apirouter.pleum.ai/v1"
export OPENAI_API_KEY="plm_..."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-4.1",
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-4.1",
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-4.1",
"input": "Why is the sky blue?"
}'POST /v1/responses/{request_id}/feedback is a separate routing feedback endpoint (JWT), not Responses create.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. Maps to the internal max_tokens. |
| 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 | Arbitrary key-value metadata. |
curl https://apirouter.pleum.ai/v1/responses \
-H "Authorization: Bearer $PLEUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4.1",
"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. Extra fields Codex sends (reasoning, text, store, previous_response_id, include, prompt_cache_key) are accepted but ignored.
{
"id": "resp_gpt-4.1-841ms",
"object": "response",
"created_at": 1735689600,
"status": "completed",
"model": "gpt-4.1",
"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 → response.in_progress → output_item.added / content_part.added / output_text.delta → output_text.done / output_item.done → response.completed with the full usage. Codex reads completed items from output_item.done and determines termination from response.completed.
data: {"type":"response.created","response":{"id":"resp_abc","status":"in_progress"}}
data: {"type":"response.in_progress","response":{"id":"resp_abc"}}
data: {"type":"output_item.added","item":{"id":"msg_abc","type":"message","role":"assistant"}}
data: {"type":"content_part.added","item_id":"msg_abc","part":{"type":"output_text","text":""}}
data: {"type":"response.output_text.delta","item_id":"msg_abc","delta":"Hi"}
data: {"type":"response.output_text.delta","item_id":"msg_abc","delta":"!"}
data: {"type":"response.output_text.done","item_id":"msg_abc","text":"Hi!"}
data: {"type":"output_item.done","item":{"id":"msg_abc","type":"message","role":"assistant","content":[{"type":"output_text","text":"Hi!"}]}}
data: {"type":"response.completed","response":{"id":"resp_abc","status":"completed","usage":{"input_tokens":12,"output_tokens":5,"total_tokens":17}}}web_search are not forwarded and are silently dropped. Only {type:"function"} tools are converted and passed to the model.