How to Use the OpenAI Responses API Endpoint Instead of Chat Completions in OmniRoute

OmniRoute exposes the OpenAI Responses API at POST /v1/responses, allowing you to send input[] arrays instead of messages[] while reusing the same unified chat handler and routing logic that powers Chat Completions.

The OmniRoute project (diegosouzapw/OmniRoute) implements the Responses API as a first-class alternative to the legacy Chat Completions interface. By routing requests to /v1/responses rather than /v1/chat/completions, you can leverage the modern input[] payload structure while maintaining full compatibility with OmniRoute’s provider-agnostic translation layer, Codex CLI integration, and unified streaming infrastructure.

Architecture of the Responses API Route

Route Definition and Entry Point

The Responses API surface is defined in src/app/api/v1/responses/route.ts. Unlike legacy routes that directly invoke provider-specific logic, this endpoint acts as a thin orchestration layer that prepares the request before delegating to the shared chat pipeline.

The route handler performs three critical operations before forwarding execution:

  • Injection guarding: Parses and validates the JSON body via withInjectionGuard to prevent malformed payloads from reaching upstream providers.
  • Model resolution: Invokes withCodexPreferredModel to determine if the requested model ID should be rewritten to the Codex namespace.
  • Transport negotiation: Inspects the Accept header to decide between SSE streaming (text/event-stream) and standard JSON responses.

When the model resolves to a Codex provider, the route applies resolveResponsesApiModel from src/app/api/internal/codex-responses-ws/modelResolution.ts to rewrite bare model IDs (e.g., gpt-5.5) to their Codex-prefixed equivalents (codex-gpt-5.5). This ensures the Codex CLI’s fallback paths function correctly without client-side configuration changes.

Unified Chat Handler Integration

After preprocessing, the route invokes handleChat from src/sse/handlers/chat.ts. This unified handler auto-detects the request format—whether OpenAI Chat Completions, Anthropic Messages, Gemini, or Responses—and executes the appropriate translator.

Inside handleChat, the logic branches based on apiFormat === "responses". When detected, the handler routes the payload through the OpenAI Responses translator (e.g., src/lib/providers/xai/translators/openai-responses.ts for xAI targets) before executing the provider-specific request. The output is translated back to the Responses schema for the return journey, ensuring format consistency regardless of which upstream provider fulfills the request.

Streaming and Keep-Alive Mechanisms

Early Keep-Alive for SSE Streams

For clients that request streaming via Accept: text/event-stream, OmniRoute injects withEarlyStreamKeepalive from src/open-sse/utils/earlyStreamKeepalive.ts. This wrapper emits periodic keep-alive frames until the upstream provider produces its first token, preventing aggressive intermediate proxies or CLI clients from terminating connections during cold-start latency.

The keep-alive logic is particularly important for the Codex CLI integration, where the codex-responses-ws internal API expects persistent connections while waiting for model resolution and provider handshakes to complete.

How to Migrate from Chat Completions to Responses

The primary difference between the two APIs is the payload structure: Chat Completions uses a messages[] array, while the Responses API uses an input[] array with an identical schema shape. OmniRoute handles this translation internally, but your client requests must conform to the Responses specification to hit the correct route.

cURL Example (SSE Streaming)

curl -N -X POST \
  -H "Accept: text/event-stream" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OMNIRoute_API_KEY" \
  -d '{
    "model": "gpt-5.5",
    "input": [{"role": "user", "content": "Explain quantum tunneling"}],
    "max_output_tokens": 256
  }' \
  https://your-omniroute-instance.com/v1/responses

Node.js Example (JSON Response)

const response = await fetch("https://your-omniroute-instance.com/v1/responses", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${process.env.OMNIRoute_API_KEY}`,
    "Accept": "application/json", // Omit for SSE
  },
  body: JSON.stringify({
    model: "gpt-5.5",
    input: [{ role: "user", content: "Explain quantum tunneling" }],
    max_output_tokens: 256,
  }),
});

const data = await response.json();
console.log(data);

Python Example

import os
import requests

url = "https://your-omniroute-instance.com/v1/responses"
headers = {
    "Authorization": f"Bearer {os.getenv('OMNIRoute_API_KEY')}",
    "Content-Type": "application/json",
}
payload = {
    "model": "gpt-5.5",
    "input": [{"role": "user", "content": "Explain quantum tunneling"}],
    "max_output_tokens": 256,
}

r = requests.post(url, json=payload, headers=headers)
print(r.json())

Summary

  • Route Location: The Responses API is implemented in src/app/api/v1/responses/route.ts as a POST endpoint.
  • Payload Structure: Use input[] instead of messages[]; OmniRoute translates this automatically to upstream provider formats.
  • Model Rewriting: Bare model IDs are automatically rewritten to Codex-prefixed variants via src/app/api/internal/codex-responses-ws/modelResolution.ts when applicable.
  • Streaming Support: SSE is supported via the Accept: text/event-stream header, with withEarlyStreamKeepalive preventing premature connection drops.
  • Unified Handler: All requests flow through src/sse/handlers/chat.ts, ensuring consistent behavior across Chat Completions and Responses APIs.

Frequently Asked Questions

What is the difference between the Responses API and Chat Completions in OmniRoute?

The Responses API uses an input[] payload structure and exposes the endpoint at /v1/responses, whereas Chat Completions uses messages[] at /v1/chat/completions. Functionally, both are handled by the same handleChat core in src/sse/handlers/chat.ts, but the Responses API provides a modernized interface that aligns with OpenAI’s latest specification while maintaining OmniRoute’s provider-agnostic routing.

How does OmniRoute handle model resolution for the Responses API?

When a request arrives at the Responses endpoint, withCodexPreferredModel invokes resolveResponsesApiModel from src/app/api/internal/codex-responses-ws/modelResolution.ts. If the model maps to a Codex provider, the logic rewrites the bare model ID (e.g., gpt-5.5) to include the codex- prefix before forwarding the request to the unified handler. This happens transparently without requiring client-side changes.

Can I use SSE streaming with the Responses API endpoint?

Yes. Set the Accept header to text/event-stream when calling /v1/responses. The route automatically wraps the request with withEarlyStreamKeepalive from src/open-sse/utils/earlyStreamKeepalive.ts, which emits periodic keep-alive frames until the upstream provider generates its first token. This prevents timeouts during provider cold-start or model resolution delays.

Does the Responses API support all providers available in OmniRoute?

Yes. Because the Responses API routes through the unified handleChat handler, it inherits support for all configured providers including OpenAI, Anthropic, Gemini, and xAI. The handler detects the apiFormat as "responses" and invokes the appropriate translator (such as src/lib/providers/xai/translators/openai-responses.ts) to convert the payload to the provider-specific schema before execution.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →