Console
API reference/POST /v1/responses

POST /v1/responses

OpenAI's Responses API: OpenAI models as a passthrough (Codex, the Agents SDK), Claude and other models in preview.

/llms.txt

OpenAI's Responses API. Point an OpenAI SDK at https://api.routerplus.com/v1 and call client.responses.create with your marketplace key. Codex (the CLI, the desktop app and the IDE extension), the OpenAI Agents SDK and other tools built on the Responses API call this route. OpenAI's models are served as a passthrough. Claude and the other chat models are a preview here: the gateway translates them through its chat surface. The chat twins of this route are POST /v1/chat/completions and POST /v1/messages.

POST https://api.routerplus.com/v1/responses
POST https://api.routerplus.com/v1/responses/compact

Which models

ModelsStatusHow the gateway serves them
gpt-6-astra, gpt-6.1-sol, gpt-6-sol, gpt-6-luna, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-4o, gpt-4o-mini, gpt-4.1-miniSupportedPassthrough to OpenAI's own Responses API, on the marketplace route or on your own OpenAI connection. The body goes as you sent it, except for the fields in Request, and the answer and its stream events come back as OpenAI sent them, with usage.cost added.
Claude models (claude-sonnet-5-5, claude-opus-5-5, …, and their rp- names) and the other chat modelsPreviewServed through the chat surface, with every chat rule: the request is translated to a chat request, and the answer back. See Preview: Claude and other models.

A deployed model from Optimize (tm/<name>-v<n>) and a dedicated endpoint are not served here: they are a 400 with code model_not_supported_on_responses, whose message points to /v1/chat/completions. Image models answer on POST /v1/images/generations only, and decision models on POST /v1/decisions only. An id that is not in the catalog is an honest 404, never a silent substitute.

Authentication

HeaderFormat
AuthorizationBearer tm_vk_... (what the OpenAI SDK and Codex send)
x-api-keytm_vk_... — also accepted, same key

A missing or invalid key returns 401 with error_type: "auth". None of your request headers reach the provider: the gateway sends its own key, or your connection's.

Request

The body is OpenAI's Responses API body, capped at 10 MB. The gateway changes these fields:

FieldHandling
modelA catalog id; the provider gets its own id for the model.
storeAlways false: the gateway keeps no responses, and OpenAI does not keep the response for later retrieval. OpenAI's own retention policy still applies (see Data policy). A store: true is dropped and named in x-tm-dropped-params; leave store out or send false.
previous_response_id, conversationNot supported: a 400 invalid_request. Send the whole conversation in input on every turn. To keep the model's reasoning between turns, ask for include: ["reasoning.encrypted_content"] and send the reasoning items back (Codex does both).
backgroundtrue is a 400 invalid_request. Stream long answers instead.
max_output_tokensOptional; when you send one, it must be a whole number from 1 to the model's own maximum, or to 32,768 if the model's maximum is lower: OpenAI's Responses API takes a limit above GPT-4o's 16,384, and the model stops at its own maximum. When you send none, the gateway adds none: OpenAI uses the model's own maximum, and the balance hold counts at most 32,768 output tokens of it. A limit that you send is held in full, up to the model's own maximum.
prompt_cache_keyYour value, else your session id (session_id, or the x-session-id, x-session-affinity or x-claude-code-session-id header).
safety_identifier, userOn the marketplace route, one hash of your organization and your end-user id goes as safety_identifier, never your raw value (see Sessions and prompt caching).
service_tierOn the marketplace route, a tier other than auto or default is dropped and named: the catalog prices the default tier.
temperature, top_pDropped and named on GPT-6 and GPT-5.6, which take only their default sampling (1 passes).
toolsEvery type passes: function, custom, namespace, and OpenAI's hosted web_search. On the marketplace route, the hosted tools code_interpreter, image_generation, file_search and mcp, and a stored prompt template, are a 400 that names them: they are billed outside the token price, or they read data kept in an OpenAI account. Your own OpenAI connection takes them all.
providerThe routing controls of Routing policies; read by the gateway, never forwarded.

Everything else passes to OpenAI as you sent it: input items of every type (reasoning items with encrypted content, custom tool calls), instructions, tool_choice, parallel_tool_calls, reasoning, text, include, metadata, max_tool_calls and truncation.

Web search on the Responses API is OpenAI's hosted web_search tool, billed at OpenAI's price: $10 per 1,000 calls, in addition to the tokens of the search results. The older web_search_preview tool costs $25 per 1,000 calls on GPT-4o, GPT-4o mini and GPT-4.1 mini. The fee is part of usage.cost; see Pricing & billing.

POST /v1/responses/compact is passed to OpenAI the same way and billed from its usage. It never streams, and it takes only the fields OpenAI's compact endpoint takes; the gateway drops and names the others. GET /v1/responses/{id} and the other stored-response endpoints are a 404: stored-response retrieval is unsupported.

Response

On the supported models the response is OpenAI's response object, as OpenAI sent it, with one addition: usage.cost, the exact charge in USD (see Pricing & billing). Usage is OpenAI's: input_tokens (cache reads and cache writes included), input_tokens_details.cached_tokens, input_tokens_details.cache_write_tokens, output_tokens and output_tokens_details.reasoning_tokens, billed with the same math as every other route. The x-request-id response header gives the id for GET https://api.routerplus.com/v1/generation?id=<x-request-id>, and every call appears in GET https://api.routerplus.com/v1/usage.

With stream: true the events are OpenAI's server-sent events, record for record, in OpenAI's order: response.created, response.in_progress, response.output_item.added, response.output_text.delta, …, and response.completed (or response.incomplete), whose response.usage carries the billed usage and cost. There is no [DONE] on this API.

  • The first events wait until the provider sends output, so a provider that fails before that is replaced by the next route without you seeing it.
  • If the provider fails after output started, the stream ends with the gateway's own response.failed event (code server_error, the canonical class in metadata.error_type), and nothing follows it. Codex retries it.
  • If you close the stream early, you pay for the input and the output streamed to you so far, as on the chat surfaces (see Pricing & billing).

The other response headers are the same as on POST /v1/chat/completions: x-request-id, x-tm-provider, x-tm-attempts, x-tm-dropped-params and the rest.

Preview: Claude and other models

For a model that OpenAI does not serve (Claude, by its id or its rp- name, and the other chat models), the gateway translates the request into a chat request and serves it with the chat surface's own rules: routing and failover, the output default and its hold, automatic caching on Claude, billing and the ledger. The answer comes back as a response object or as Responses stream events. Codex works this way with Claude models.

Responses inputChat form
instructions and developer messagesSystem messages
Message items with input_text, output_text, input_image, input_file and input_audio partsChat messages with text, image_url, file and input_audio parts
function_call and function_call_output itemsAn assistant tool call and its tool message. A tool output with an image (Codex's view_image) goes into a user message right after the tool messages
custom tools, custom_tool_call and its outputA function tool with one string parameter, input; the call comes back as a custom_tool_call
namespace toolsFunction tools named <namespace>__<name> (with a short hash when that name is taken or too long); the call comes back under its own namespace and name
max_output_tokens, reasoning.effort, text.formatmax_tokens, reasoning_effort, response_format
tool_choice, parallel_tool_calls, prompt_cache_key, temperature, top_pTheir chat forms, or as sent
Hosted tools (web_search, file_search, code_interpreter, image_generation, computer_use_preview, mcp, shell, apply_patch and others), and reasoning items you send backDropped and named in x-tm-dropped-params, for example tools[3].web_search and input[*].reasoning. Web search is not mapped to Claude's own search

Also dropped and named: reasoning.summary (Codex asks for one on every request), a text.verbosity other than medium, store: true, and a truncation, max_tool_calls, service_tier or top_logprobs value that asks for something. Refused with a 400 that names the field: previous_response_id, conversation, prompt, background: true, item_reference items, an uploaded file_id, a file_url, and a part or item type that a chat body cannot carry. With "provider": {"require_parameters": true}, a drop is a 400 too.

The answer:

  • Its model is the name you sent. Its id is resp_ and the request id; the x-request-id header carries the request id for GET /v1/generation.
  • Text comes back as a message item, a tool call as a function_call or custom_tool_call item, and the model's reasoning as a reasoning item with its text.
  • status is incomplete when the answer reached max_output_tokens or was filtered, and completed otherwise.
  • Usage: input_tokens counts cache reads and writes, as on the chat surface, and cost is inside it.

Limits of the preview:

  • Claude routes refuse reasoning.effort and text.format with a 400 that names them. In Codex, do not set model_reasoning_effort for a Claude model.
  • The Claude 5 models refuse a forced tool choice (tool_choice: "required", or a named tool) with a 400. Codex sends auto.
  • A reasoning item that you send back is dropped, so the model's reasoning is not carried from turn to turn. Morph's and Mistral's routes are the exception: a reasoning item with the model's reasoning text, as this surface answers it, goes back to them as that turn's reasoning and is not dropped.
  • Annotations and logprobs are not carried, and a custom tool's input arrives whole when its call ends.
  • POST /v1/responses/compact takes OpenAI's models only: for any other model it is a 400 with code model_not_supported_on_responses.

Use /v1/chat/completions or /v1/messages for production traffic to Claude.

Errors

Every error uses OpenAI's error envelope, with the canonical class in error.metadata.error_type, as on POST /v1/chat/completions. A provider's refusal carries the provider's own words in the message.

  • On OpenAI's models, error.code is the canonical class, as on the chat surface. A prompt too long for the model has code: "context_length_exceeded".
  • On the preview path, error.code takes OpenAI's own names where clients act on them: rate_limit_exceeded (class rate_limit), context_length_exceeded (context_overflow), model_not_found (model_unavailable) and invalid_prompt (content_policy). A failure in the middle of a stream is a response.failed event, and any other code there is server_error, which Codex retries. A 400 for a field that the translation wrote names your field: reasoning.effort, text.format or max_output_tokens.

Codex compacts a conversation when it sees context_length_exceeded. The full table is on Errors.

Examples

curl
curl -N https://api.routerplus.com/v1/responses \
  -H "Authorization: Bearer $TM_API_KEY" \
  -H "content-type: application/json" \
  -d '{"model":"gpt-6-luna","stream":true,"store":false,"max_output_tokens":200,"input":"Say hello in one sentence."}'
python
The official openai SDK with the base URL swapped
import os
from openai import OpenAI

client = OpenAI(base_url="https://api.routerplus.com/v1", api_key=os.environ["TM_API_KEY"])

resp = client.responses.create(
    model="gpt-6-luna",
    store=False,
    max_output_tokens=200,
    input="Say hello in one sentence.",
)
print(resp.output_text)
print(resp.usage)  # input, cached, output and reasoning tokens, and cost (USD)
typescript
The official openai package, streamed
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.routerplus.com/v1",
  apiKey: process.env.TM_API_KEY,
});

const stream = await client.responses.create({
  model: "gpt-5.6-terra",
  store: false,
  stream: true,
  input: "Count to five.",
});

for await (const event of stream) {
  if (event.type === "response.output_text.delta") process.stdout.write(event.delta);
  if (event.type === "response.completed") console.log("\n", event.response.usage);
}

To set up Codex, see Coding agents.

See also: POST /v1/chat/completions · Errors · Wire compatibility · Coding agents

Markdown source for agents: /docs/api-responses.md · index at /llms.txt