Skip to content

OpenAI

Route mapped models to the OpenAI Responses API with an OPENAI_API_KEY — translation, effort, token counting, and tool search.

Updated View as Markdown

The built-in openai provider routes a mapped model’s inference to the OpenAI platform API (api.openai.com), billed per token against an OPENAI_API_KEY. It is a kind = "responses" provider: shunt translates Claude Code’s Anthropic Messages request into the OpenAI Responses API, streams the reply back, and translates it again — tools, images, and streaming included.

Quick start

Let a coding agent wire it up for you — shunt add prints an embedded setup blueprint (offline and read-only; the agent edits the config, the command never does):

shunt add upstream openai --print | claude

Or follow the manual steps below.

Configure the upstream

The openai preset supplies kind = "responses", base_url = "https://api.openai.com/v1" (shunt appends /responses), and API-key auth from OPENAI_API_KEY:

[[upstreams]]
name = "anthropic"
provider = "anthropic"   # keep Anthropic as the default for unrouted models (e.g. claude-*)

[[upstreams]]
name = "openai"
provider = "openai"
# effort = "high"          # optional default reasoning effort
# count_tokens = "tiktoken" # default; "estimate" opts out of local counting

Ordered [[upstreams]] replace shunt’s built-in providers, so the config must declare the anthropic default it still falls back to (server.default_provider defaults to anthropic).

Explicit fields override preset defaults. The legacy [providers.openai] table form remains supported — but do not mix [[upstreams]] and [providers.*] in one file.

Credentials

Export the key in the environment that launches shunt — never write it into the config:

export OPENAI_API_KEY='...'

shunt check validates the config’s structure but does not read the key’s value — if OPENAI_API_KEY is unset, the first request routed to openai returns an authentication error.

Models & routing

Route ids exactly, by prefix, or advertise a Claude-named alias to model discovery:

# Recommended: advertise + route + translate in one declaration
[[models]]
id = "claude-gpt-5-4-via-openai"
display_name = "GPT-5.4 (via OpenAI)"

[models.upstream_model]
openai = "gpt-5.4"

# Or catch every gpt-* id a client sends
[[route_prefixes]]
prefix = "gpt-"
provider = "openai"

Verify current model availability on your OpenAI account before selecting a slug. Unlike the ChatGPT-account Codex backend, the platform API accepts its regular public slugs.

Reasoning effort, context, and token counting

The Responses translation supports a per-provider or per-route reasoning-effort dial (lowmax) and local tiktoken-based count_tokens so Claude Code’s context accounting keeps working. Both are shared with the other Responses providers and documented in Effort & Context.

Tool search. With Claude Code’s ENABLE_TOOL_SEARCH=true, shunt emulates deferred tool discovery on the Responses path via a text shim; setting tool_search = true on the provider upgrades it to the Responses API’s native client-executed tool_search protocol on GPT-5.4+ models. See Tool search.

Verify

shunt check    # -> config ok
shunt run
curl -sS http://127.0.0.1:3001/v1/models
curl -sS http://127.0.0.1:3001/v1/messages \
  -H 'anthropic-version: 2023-06-01' \
  -H 'content-type: application/json' \
  -d '{"model":"claude-gpt-5-4-via-openai","max_tokens":16,"messages":[{"role":"user","content":"Reply with OK."}]}'

Confirm the response’s x-gateway-upstream header names openai, then point Claude Code at shunt.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close