OpenAI-Compatible SDKs
Codex Pooler provides narrow OpenAI-compatible /v1 support for selected SDK routes. It translates supported requests into Codex-compatible work, then sends them through the same Pool routing, limit checks, account selection, and accounting path used by Codex backend clients.
It doesn’t provide full OpenAI API parity.
For a short citable summary, see OpenAI-compatible Codex gateway.
What OpenAI-Compatible Means Here
Section titled “What OpenAI-Compatible Means Here”OpenAI-compatible means selected SDK request shapes can use a /v1 base URL and a Pool API key. Codex Pooler still routes the request through Codex account Pools, not through a separate OpenAI engine. Unsupported OpenAI API areas remain unsupported, including embeddings, batches, fine-tuning, moderation, response retrieve/cancel/delete, image variations, OpenAI Responses remote MCP tool definitions, and OpenAI Realtime SDK routes.
Base URL and authentication
Section titled “Base URL and authentication”Use the /v1 base URL and a Pool API key:
Base URL:https://codex-pooler.example.com/v1
Authorization:Bearer <pool-api-key>For local setup, use http://localhost:4000/v1.
Service tier compatibility
Section titled “Service tier compatibility”Use service_tier: "priority" for priority processing. fast is an accepted
equivalent request spelling, but priority is the canonical spelling for new
configuration. On /v1, Codex Pooler translates supported OpenAI request and
response shapes while preserving any projected provider service_tier value in
its literal provider vocabulary. Codex backend relay routes under
/backend-api/codex preserve provider bytes, frames, and service-tier
vocabulary unchanged.
ultrafast is a separate tier, not an alias for fast or priority. Use
service_tier: "ultrafast" only with direct /v1/responses requests when the
selected model metadata advertises it:
response = client.responses.create( model="your-model-id", input="Your request input.", service_tier="ultrafast",)JSON, SSE, and narrow Responses WebSocket requests preserve a returned
ultrafast tier literally. POST /v1/chat/completions rejects ultrafast.
Provider availability, access, and price control whether the tier can be used;
model metadata advertisement is not an entitlement or price promise.
Tool-specific setup pages
Section titled “Tool-specific setup pages”Use the dedicated setup pages when configuring an agent or editor that has its own provider shape:
Python SDK
Section titled “Python SDK”import os
from openai import OpenAI
client = OpenAI( api_key=os.environ["CODEX_POOLER_API_KEY"], base_url="https://codex-pooler.example.com/v1",)
response = client.responses.create( model="gpt-5.6-terra", input="Write a short setup confirmation.",)
print(response.output_text)Node SDK
Section titled “Node SDK”import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.CODEX_POOLER_API_KEY, baseURL: "https://codex-pooler.example.com/v1",});
const response = await client.responses.create({ model: "gpt-5.6-terra", input: "Write a short setup confirmation.",});
console.log(response.output_text);Vercel AI SDK
Section titled “Vercel AI SDK”import { createOpenAI } from "@ai-sdk/openai";import { generateText } from "ai";
const pooler = createOpenAI({ apiKey: process.env.CODEX_POOLER_API_KEY, baseURL: "https://codex-pooler.example.com/v1",});
const { text } = await generateText({ model: pooler.responses("gpt-5.6-terra"), providerOptions: { openai: { promptCacheOptions: { mode: "explicit", ttl: "30m" }, }, }, messages: [ { role: "system", content: "Keep setup guidance concise.", providerOptions: { openai: { promptCacheBreakpoint: { mode: "explicit" } }, }, }, { role: "user", content: "Write a short setup confirmation." }, ],});
console.log(text);Codex Pooler accepts these OpenAI cache controls as public input. For
account-backed requests, it adapts the explicit controls automatically before
upstream dispatch while preserving prompt_cache_key independently from Pool
routing affinity. GPT-5.6 model ids are passed through to the Pool catalog and
assignment policy; naming one here does not guarantee that every Pool has an
eligible account for that model.
Programmatic tools and stateless replay
Section titled “Programmatic tools and stateless replay”For Vercel AI SDK programmatic tools, set providerOptions.openai.store: false
on every generation that participates in the tool loop:
const result = await generateText({ model: pooler.responses("gpt-5.6-terra"), providerOptions: { openai: { store: false }, }, // Your programmatic tool and tool-choice configuration goes here. prompt: appPrompt,});Vercel chooses complete stateless replay or a stored item_reference before
the request reaches Codex Pooler. store: false is therefore required for the
complete stateless replay path. Independently, Codex Pooler forces upstream
stream: true and store: false.
This is narrow, closed-world compatibility, not general OpenAI programmatic-tool
or Responses parity. Supported shapes are program and program_output,
caller-enabled function_call and function_call_output, the type-only
programmatic_tool_calling tool and tool choice, plus allowed_callers and
map-only output_schema. Reference-only and ordinary stored-response
continuations are not supported. Codex Pooler does not locally persist raw
program code, results, schemas, or identifiers. A request accepted by the
gateway can still be rejected when its selected upstream model or account lacks
the required capability.
Responses function_call_output replay has two closed forms over HTTP and the
narrow Responses websocket bridge. A paired item requires a nonblank call_id;
its existing output form and paired-only legacy result form are unchanged. A
named standalone item requires a nonblank name and an output field, permits
call_id only when omitted or null, and permits namespace only when omitted,
null, or a nonblank string. Blank or non-string values and standalone result
reject before dispatch. Classification and debug summaries remain metadata-only.
This does not expand general Responses parity or prove provider-live acceptance.
Hosted shell history replay
Section titled “Hosted shell history replay”OpenAI Responses history may include completed shell_call and
shell_call_output items from an earlier response. Codex Pooler accepts this
closed-key replay subset, including direct or program callers, local or container
environment metadata, in_progress, completed, or incomplete status, and
empty command or output arrays where the shape permits them. It forwards accepted
history for stateless replay and semantic tool-output continuations without
requiring call/output pairing or a particular item order.
This is history forwarding only. Codex Pooler does not execute shell commands or
accept a top-level shell tool declaration. local_shell_call history and remote
MCP tool definitions remain unsupported. It also does not implement SDK-local
command-index accumulation or claim general hosted-tool parity.
Responses SSE and the narrow Responses websocket preserve the five hosted-shell
relay event types: command added, command delta, command done, output-content
delta, and output-content done. Existing public sequence normalization and the
websocket stream_id behavior are the only permitted relay adaptations.
Commands and shell output remain transient request data. They are not persisted or rendered in request metadata, logs, or operator views.
For a previous_response_id tool continuation, a native reasoning replay item
may include content with reasoning_text parts. Codex Pooler preserves that
accepted reasoning content for the upstream continuation. Stateless replay drops
reasoning items before dispatch instead.
Assistant-history replay may include output_text URL citations. Codex Pooler
accepts only ordered url_citation annotations with exactly type,
start_index, end_index, url, and title; it preserves accepted values,
explicit empty annotation lists, and omission exactly. Malformed or unsupported
annotation shapes are rejected before dispatch rather than filtered.
GET /v1/models may include context_length for clients that probe OpenAI-compatible model lists, such as Hermes. The official OpenAI SDK request APIs and Vercel AI SDK generation APIs do not expose Codex model-catalog context controls. Use their output-budget fields only when your application needs one: max_output_tokens in OpenAI Responses, max_completion_tokens in Chat Completions, and maxOutputTokens at the Vercel AI SDK layer. Codex Pooler’s public /v1/responses rejects context_management, and direct POST /v1/responses/compact remains unsupported. Explicit compaction is available on the normal /v1/responses route.
Request an explicit compaction turn
Section titled “Request an explicit compaction turn”Provider clients that expose an OpenAI compaction option can use the normal /v1/responses route. Vercel AI SDK serializes the required trigger with providerOptions.openai.compactionTrigger: true in @ai-sdk/openai 4.0.42 or later:
import { createOpenAI, type OpenAILanguageModelResponsesOptions,} from "@ai-sdk/openai";import { generateText } from "ai";
const pooler = createOpenAI({ apiKey: process.env.CODEX_POOLER_API_KEY, baseURL: "https://codex-pooler.example.com/v1",});
const result = await generateText({ model: pooler.responses("gpt-5.6-terra"), providerOptions: { openai: { store: false, compactionTrigger: true, } satisfies OpenAILanguageModelResponsesOptions, }, prompt: "Compact the visible conversation context.",});
const compaction = result.content.find( (part) => part.type === "custom" && part.kind === "openai.compaction",);The serialized request must contain visible input followed by exactly one final {"type":"compaction_trigger"} item. Non-terminal, duplicate, trigger-only, hidden-only, or otherwise malformed placement returns an OpenAI-shaped 400 invalid_request on input before upstream dispatch.
Successful non-streaming HTTP returns a completed Responses JSON object containing the normalized compaction item. Public SSE emits response.output_item.done, then response.completed, then [DONE]. Narrow Responses websocket completion emits the same two Responses events without the HTTP [DONE] sentinel. Direct POST /v1/responses/compact remains unsupported.
If upstream compact output is malformed JSON or does not contain nonblank encrypted compaction content, Codex Pooler returns a sanitized 502 invalid_compaction_response. Other provider failures follow the public error rules documented below.
Replay a remote-compaction item
Section titled “Replay a remote-compaction item”When a compaction turn returns an encrypted type: "compaction" output item, submit that item unchanged at the start of the next POST /v1/responses input, followed by the new user input. Start a new chain by omitting previous_response_id; the compact response envelope id is not a replay anchor.
The stable public replay fields are type, a nonblank opaque encrypted_content string, and optional id. The id key may be absent, a string, or explicitly null, and its presence is preserved. JSON, SSE, and narrow Responses websocket surfaces return the same normalized item. Native-only metadata and unknown fields are removed from output; unknown replay fields or malformed values are rejected before upstream dispatch. Treat encrypted_content as opaque and do not log or persist it in application telemetry.
Supported or translated route support
Section titled “Supported or translated route support”The OpenAI-compatible /v1 surface supports or translates selected routes only:
GET /v1/modelsPOST /v1/responsesGET /v1/responses, narrow Responses websocket compatibility onlyPOST /v1/chat/completionsGET /v1/usageGET /v1/filesPOST /v1/filesGET /v1/files/:file_idPOST /v1/audio/transcriptionsPOST /v1/images/generationsPOST /v1/images/edits
The /v1 surface is compatibility over Codex routing, not a separate OpenAI engine. Supported requests still require a Pool API key and a Pool with eligible upstream capacity for the requested model.
Audio transcriptions
Section titled “Audio transcriptions”POST /v1/audio/transcriptions accepts gpt-transcribe as a caller alias. The
gateway uses the fixed canonical backend identity gpt-4o-transcribe. The alias
is not a model-list entry or a model-discovery guarantee.
import fs from "node:fs";import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.CODEX_POOLER_API_KEY, baseURL: "http://localhost:4000/v1",});
const transcription = await client.audio.transcriptions.create({ file: fs.createReadStream("audio.mp3"), model: "gpt-transcribe", keywords: ["example", "example"], languages: ["en", "it"],});
console.log(transcription.text);import os
from openai import OpenAI
client = OpenAI( api_key=os.environ["CODEX_POOLER_API_KEY"], base_url="http://localhost:4000/v1",)
with open("audio.mp3", "rb") as audio_file: transcription = client.audio.transcriptions.create( file=audio_file, model="gpt-transcribe", keywords=["example", "example"], languages=["en", "it"], )
print(transcription.text)keywords and languages are optional nonempty string lists. Empty lists are
omitted. Nonempty lists retain caller order and duplicate entries. Responses omit
language-detection fields. This documents the accepted request shape only, not
transcription quality, model discovery, or general Audio API coverage.
POST /v1/responses lifts system and developer input-message text into top-level instructions before dispatching to Codex-compatible work. Streaming /v1/responses and /v1/chat/completions return early upstream terminal errors as the first public SSE event or data chunk, without synthetic assistant/success prefixes. Non-streaming failures remain OpenAI-shaped JSON errors.
Streaming /v1/responses clients must also handle a terminal type: "error" SSE event whose flat fields and nested error object both carry the sanitized server_error code.
Streaming /v1/chat/completions clients must separately handle a terminal data: {"error":{...}} chunk with nested server_error fields and no top-level type; no [DONE] chunk follows that terminal.
For public SSE, an absent, blank, or whitespace-only event: label is treated as absent before the label is compared with the JSON type. A nonblank mismatch is still rejected. Ordinary incomplete POST /v1/responses SSE blocks are capped at 8 MiB so single large provider events, such as reasoning items carrying encrypted content, can finish decoding; structurally recognizable terminal candidates may retain up to 64 MiB so a large terminal split across upstream chunks can finish decoding. Crossing the applicable cap sends one bounded sanitized type: "error" event, relays none of that source block, and drops later source frames.
On narrow GET /v1/responses websocket compatibility, malformed JSON and JSON values that are not objects are ignored without consuming a sequence number or creating a local terminal. Native backend websocket behavior is unchanged.
Responses WebSocket stream IDs
Section titled “Responses WebSocket stream IDs”Narrow GET /v1/responses websocket response.create accepts an optional
stream_id only when it is a 1 through 256 byte string matching
^[A-Za-z0-9_.-]+$. A valid accepted ID is echoed on every attributable Open
Responses server event and is stripped before upstream dispatch. It is transient
socket-turn state and is not stored in request metadata, accounting, logs, or
telemetry.
Requests with the same ID are FIFO. Different valid IDs are accepted and echoed,
but Codex Pooler conservatively serializes public turns per connection; it does
not promise cross-ID concurrency or fairness. previous_response_id controls
conversation lineage independently. REST POST /v1/responses, native backend
WebSockets, Chat, compact, and batches do not accept this field.
Relayed terminal errors
Section titled “Relayed terminal errors”When the Codex backend returns a genuine terminal error, Codex Pooler preserves its trimmed error.code only when
the value is at most 80 bytes and matches ^[A-Za-z0-9_.-]+$. Other code values are redacted to upstream_error.
A surviving code may be a Codex-backend value that is not part of the OpenAI platform ResponseError enum.
Upstream error messages are replaced with upstream request failed, and upstream error types are replaced with
server_error. Clients must therefore treat error.code as an open string: handle the values they understand and
keep an unknown-code fallback rather than validating against the platform enum.
Public relays construct each response.failed envelope from a named-field projection. Unknown event, response,
error, and usage siblings are excluded. The response identifier is retained only when it is a valid resp_
identifier; otherwise it becomes resp_failed. Usage is either null or a bounded projection of the declared token
counters. Content-bearing response fields are empty or null: output and tools are empty arrays, output_text
is an empty string, and instructions, metadata, temperature, and top_p are null.
Top-level and nested errors are sanitized independently and are never copied between locations. A safe code remains
unchanged at its original location, while every invalid code becomes upstream_error; message and type remain the
fixed upstream request failed and server_error values.
One narrow exception applies to misalignment_policy_violation. An eligible
direct 400 or 403, or an exact terminal SSE or websocket failure, is
health-neutral and non-retryable. Public clients receive the exact code,
invalid_request_error, and the nonblank provider message when available, or a
fixed safe fallback when it is blank. The error never includes a provider
param, body, or sibling fields. Durable accounting and logs retain only the
exact code, fixed accounting text, and bounded facts. Every other provider error
continues to use the generic redaction above.
Schema-strict clients that enforce the platform enum cannot accept every relayed Codex-backend code. In
openai-python, leave _strict_response_validation at its default false when using this compatibility surface.
Ordinary OpenAI Responses incomplete terminals are preserved. If an upstream returns response.incomplete with status: "incomplete" for output limits or content filtering and no embedded error, /v1/responses streaming and websocket clients receive that incomplete terminal instead of a synthetic failure. Error-coded incomplete terminals, such as context overflow or stale continuation anchors, are still returned through the sanitized failure path.
Accepted POST /v1/responses tool definitions are narrow. OpenAI Responses remote MCP tool definitions are rejected before dispatch.
Declaration-backed allowed tools
Section titled “Declaration-backed allowed tools”In Full serving mode, direct POST /v1/responses and narrow Responses websocket
response.create accept an allowed_tools choice only when every member is
backed by a direct top-level declaration. Named function and custom members
must match the declared kind and name. The only type-only built-ins are
programmatic_tool_calling, web_search_preview, web_search, and
image_generation, each declared at the top level. Caller order and repeated
members are preserved.
This is a narrow declaration-backed contract, not broad OpenAI tool parity. It
doesn’t cover Chat, backend Responses, namespaces, additional tools, deferred
tools, aliases, Realtime, or remote MCP. Invalid or undeclared Full choices fail
before dispatch. Lite rejects any map-shaped allowed_tools choice with the
existing unsupported_parameter error on tool_choice.
Remote MCP remains unsupported in Responses. A top-level MCP declaration is a
tools error, while an MCP member inside allowed_tools is a tool_choice
error. The separate /mcp endpoint is operator metadata access, not a Responses
remote MCP bridge.
Web search domain filters
Section titled “Web search domain filters”web_search.filters accepts only allowed_domains and blocked_domains. Each
supplied field is a list of 1 through 100 nonblank strings without leading-
whitespace, case-insensitive HTTP(S) schemes. Both fields can appear together.
Codex Pooler forwards accepted values unchanged, including their order, case,
duplicates, and bytes.
external_web_access is optional. Codex Pooler guarantees local validation and
forwarding of this accepted shape only. Web search availability and any
allow-list or block-list enforcement can vary by selected upstream model and
account.
Request compression behavior
Section titled “Request compression behavior”Per-Pool request compression can apply to POST /v1/responses, POST /v1/chat/completions, and narrow Responses websocket response.create traffic. It is transparent to SDK clients and does not require a client flag. Operators enable it with the Pool setting request_compression_enabled.
The feature is request-side only. Codex Pooler may compress upstream-bound Responses tool-output payloads before dispatch, but it does not store raw outputs, does not store upstream response bodies, and does not implement CCR/retrieval. Public /v1/responses/compact remains unsupported.
If compression is enabled but savings do not appear, check the request log’s payload_compression status and reason. The UI prefers saved token count and token savings percent when local token counts are available, then falls back to saved bytes and byte savings percent.
Routed but unsupported boundaries
Section titled “Routed but unsupported boundaries”These /v1 routes are unsupported and may return deterministic OpenAI-shaped unsupported endpoint errors when explicitly routed:
POST /v1/responses/compactGET /v1/files/:file_id/content, after ownership checksDELETE /v1/files/:file_id, after ownership checksPOST /v1/images/variationsPOST /v1/content_provenance_checks, deliberately routed to deterministic OpenAI-shapedunsupported_endpointPOST /v1/embeddingsPOST /v1/batchesPOST /v1/moderationsPOST /v1/fine_tuning/jobsGET /v1/responses/:response_idPOST /v1/responses/:response_id/cancelDELETE /v1/responses/:response_id/v1/realtimeand OpenAI Realtime SDK websocket or session routes
GET /v1/responses is narrow Responses websocket compatibility, not /v1/realtime support. OpenAI Realtime SDK websocket and session routes are not supported.
Within POST /v1/responses, OpenAI Responses remote MCP tool definitions are unsupported. A top-level tools entry with type: "mcp", or an input item with type: "additional_tools" whose tools list contains type: "mcp", is rejected before upstream dispatch with an OpenAI-shaped invalid_request error.
MCP stays separate
Section titled “MCP stays separate”The operator MCP endpoint is rooted at /mcp, not under /v1. It uses operator-owned MCP bearer tokens and returns metadata only.
The root /mcp endpoint is not a /v1/responses remote MCP bridge and is not invoked by Responses tools[type=mcp] definitions.
MCP URL:https://codex-pooler.example.com/mcp
Authorization:Bearer <operator-mcp-token>Don’t use Pool API keys, browser sessions, cookies, query tokens, invite tokens, upstream tokens, or custom headers as MCP authentication.
Frequently Asked Questions
Section titled “Frequently Asked Questions”Can I use the official OpenAI SDK with Codex Pooler?
Section titled “Can I use the official OpenAI SDK with Codex Pooler?”Yes, for selected SDK routes. Set the SDK base_url or baseURL to https://codex-pooler.example.com/v1, use a Pool API key as the bearer credential, and keep requests on supported route shapes such as responses, chat completions, models, usage, files, audio transcription, and image generation or edits.
Should Codex backend clients use /v1?
Section titled “Should Codex backend clients use /v1?”No. Codex backend-compatible clients should use /backend-api/codex. The /v1 surface is for selected OpenAI SDK-compatible clients and translates supported requests into Codex-compatible work.
Does /v1 support OpenAI Realtime?
Section titled “Does /v1 support OpenAI Realtime?”No. /v1/realtime and OpenAI Realtime SDK websocket or session routes are unsupported. GET /v1/responses is narrow Responses websocket compatibility only, not OpenAI Realtime support.
Does /v1/responses/compact work?
Section titled “Does /v1/responses/compact work?”No. POST /v1/responses/compact returns a deterministic OpenAI-shaped unsupported_endpoint error. Explicit compaction is supported on normal /v1/responses requests when the client appends exactly one final compaction_trigger after visible input. Backend compact compatibility remains available under /backend-api/codex.
Can I use the same key for /v1 and /mcp?
Section titled “Can I use the same key for /v1 and /mcp?”No. /v1 uses Pool API keys for runtime work. /mcp uses operator-owned MCP bearer tokens for metadata-only lookup. Keep those credentials separate in client configuration and storage.