Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

HTTP API

The proxy wraps llmshim's translation engine in a compact JSON API. It has four endpoints and two response encodings.

Availability: HTTP JSON: non-streaming chat, models, health · HTTP SSE: streaming chat

Endpoints

MethodPathResult
POST/v1/chatA ChatResponse, or typed SSE when stream is true
POST/v1/chat/streamTyped SSE, regardless of the request's stream value
GET/v1/modelsModels whose providers have configured API keys
GET/healthProcess health and configured provider names

The canonical machine-readable contract is api/openapi.yaml.

Chat request

Both chat endpoints accept the same body:

{
  "model": "anthropic/claude-sonnet-5",
  "messages": [{"role": "user", "content": "Explain ownership briefly."}],
  "stream": false,
  "config": {
    "max_tokens": 300,
    "temperature": 0.2,
    "reasoning_effort": "medium"
  },
  "provider_config": {},
  "fallback": ["openai/gpt-5.4-mini"]
}
FieldTypeMeaning
modelstring, requiredprovider/model or an inferable bare model name
messagesarray, requiredFull conversation history
streambooleanOn /v1/chat, switch the response from JSON to typed SSE; default false
configobjectPortable generation controls
provider_configobjectFields merged into the engine request, including tools and x-* namespaces
fallbackstring arrayOrdered backup models for non-streaming requests only

config accepts exactly these fields:

FieldType
max_tokensunsigned integer
temperaturenumber
top_pnumber
top_kunsigned integer
stoparray of strings
reasoning_effortnone, low, medium, high, xhigh, or max
reasoning_modestandard or pro

Adapters use only controls supported by the selected provider. Reasoning is mapped and clamped by model family; see Reasoning controls.

Messages require role; content defaults to null and may be text or content blocks. Tool turns may also carry tool_calls or tool_call_id. reasoning_content is accepted on a message when prior reasoning must be round-tripped.

provider_config is merged as top-level engine fields. For example, tools go at provider_config.tools, while an OpenAI-native override goes at provider_config["x-openai"]. See the request field map.

Fallback first retries an eligible failure on the current route, then moves through the listed routes. It is ignored by both streaming paths. See Fallback chains.

Non-streaming response

POST /v1/chat returns a compact response when stream is false:

{
  "id": "msg_123",
  "model": "claude-sonnet-5",
  "provider": "anthropic",
  "message": {
    "role": "assistant",
    "content": "Ownership gives each value one owner."
  },
  "usage": {
    "input_tokens": 14,
    "output_tokens": 9,
    "reasoning_tokens": 4,
    "total_tokens": 27
  },
  "latency_ms": 612
}

message.tool_calls appears when the model requests tools. reasoning appears when the provider returns reasoning text. reasoning_tokens is omitted when zero; the other usage fields are always present.

Streaming response

POST /v1/chat/stream always streams. POST /v1/chat does the same when the body contains "stream": true.

Each Server-Sent Event has an SSE event: name and JSON data: whose type matches that name:

event: reasoning
data: {"type":"reasoning","text":"..."}

event: content
data: {"type":"content","text":"Ownership gives each value one owner."}

event: tool_call
data: {"type":"tool_call","id":"call_1","name":"lookup","arguments":"{\"id\":7}"}

event: usage
data: {"type":"usage","input_tokens":14,"output_tokens":9,"total_tokens":23}

event: done
data: {"type":"done"}
EventJSON fields
contenttype, text
reasoningtype, text
tool_calltype, id, name, arguments
usagetype, input_tokens, output_tokens, optional reasoning_tokens, total_tokens
donetype
errortype, message

Admission failures happen before SSE begins and return normal HTTP 429 or 503 responses. A failure after the stream starts is an error event. For consumption patterns, see Streaming.

Models and health

GET /v1/models returns the registry entries for configured providers:

{
  "models": [
    {"id": "openai/gpt-5.4-mini", "provider": "openai", "name": "gpt-5.4-mini"}
  ]
}

This is discovery, not an allowlist: an arbitrary provider model ID can still be routed explicitly. See Model discovery.

GET /health reports that the process can serve requests and names its configured providers:

{"status":"ok","providers":["openai","anthropic"]}

It does not probe upstream provider availability.

Errors

Before a stream begins, errors use an HTTP status and this JSON envelope:

{"error":{"code":"rate_limited","message":"Upstream provider rate limit reached; retry after the suggested delay"}}

Proactive 429 and 503 responses include Retry-After in whole seconds. Provider failures normally retain the provider's status. See Errors and retries for the complete mapping.