API reference

OpenAI parameter compatibility

Which chat-completions request parameters are implemented, rejected, or silently tolerated — and the response fields every reply carries.

POST /v1/chat/completions targets OpenAI drop-in compatibility: an existing SDK or proxy should keep working unmodified. That does not mean every field is implemented. The engine sorts every request parameter into one of three regimes (internal/features/chat/inbound/dto.go), and this page is the generated-by-hand matrix behind that policy (spec 2026-08-11 DX audit §4.5/§12).

The three regimes

  1. Implemented — present in the request DTO, transmitted to the gateway, and changes the response.
  2. Rejected (400) — not implemented, and the engine's own test says its absence would change what the caller receives or pays for. Refusing is the honest floor: an ignored field here would silently return a cheaper or different answer than the one asked for.
  3. Tolerated — not implemented, but ignoring it changes nothing the caller can observe. Kept ignored on purpose: rejecting every field the engine does not model would break drop-in compatibility on the next field upstream ships.

The test for regime 2 vs. regime 3 is never "do we model it" — it is "does ignoring it change the answer." That is also why this is a short, explicit list rather than DisallowUnknownFields on the request body.

Implemented

ParameterBehavior
model, messages, streamCore request shape.
temperature, top_pSampling controls, forwarded to the gateway; both dialects reject out-of-range values (temperature outside 0..2, top_p outside 0..1) with 400 before calling a provider.
max_tokensForwarded as the completion cap. ⚠️ Honored by the connector's provider type — see the API surface and the connector documentation for per-type caveats.
max_completion_tokensUpstream's replacement for the deprecated max_tokens; its own field, not an alias, because it bounds visible output and reasoning tokens together. Either field alone is honored; both together are accepted only when equal — differing values answer 400 naming both fields, so a spend cap can never silently disappear.
stopForwarded verbatim; a single string or an array of strings.
storeControls conversation persistence: absent or true persists the turn, false opts it out.
tools, tool_choice, response_formatTool-calling (streamed and non-streamed) and structured output (json_object / json_schema), forwarded to the provider; rejected in knowledge-agent mode, which owns its own answer shape.
prompt_cache_key, prompt_cache_retentionForwarded verbatim — they choose which cache a turn reuses, an isolation decision only the caller can make. The gateway strips them for providers that do not support them.
reasoning_effortnone | minimal | low | medium | high | xhigh (the union of provider-accepted values). Tells a reasoning model how hard to think; none is what makes a grounded RAG answer fast and cheap.
stream_options.include_usageWhen true on a streamed request, one final chat.completion.chunk carries a usage object and an empty choices array after the finish-reason chunk, so a streamed call can be metered like a non-streamed one. Present on a non-streaming request answers 400 — OpenAI defines the field as meaningless there.
dataset_ids, project_id, conversation_id, agent_modeDeeplinq extensions: RAG, projects, conversations, and the governed knowledge agent.

Rejected (400, regime 1)

Each of these fails the same test n and best_of always applied: without it being implemented, silently ignoring an explicit ask changes what the caller gets back or pays for. Sending the parameter's own OpenAI-documented default value is not a real ask — it is asking for what the engine already does — so it passes rather than refuses, mirroring the reasoning the n <= 1 guard has always used. An explicit JSON null on a nullable field is treated as absent, not a real ask.

ParameterRejected becauseTolerated (no-op) value
nThe engine always returns exactly one choice; a caller asking for more was silently billed for one anyway (audit L1).n <= 1
best_ofThe engine always generates exactly one candidate.best_of <= 1
logprobsThe response never carries token log probabilities.false (OpenAI default)
top_logprobsMeaningless — and refused by OpenAI itself — without an accompanying logprobs: true; requested probabilities are never returned.Any value, as long as the sibling logprobs is not itself true (OpenAI ties the two together; there is no independent default for top_logprobs)
presence_penaltySampling is not adjusted for token presence.0 (OpenAI default)
frequency_penaltySampling is not adjusted for token frequency.0 (OpenAI default)
parallel_tool_callsThe engine's own tool-call concurrency is always used; false cannot be honored.true (OpenAI default)
seedResponses are not reproducible by seed.None — OpenAI documents no default; any explicit, non-null seed is a genuine ask.
logit_biasRequested per-token biases are not applied.{} (empty — equivalent to OpenAI's default of no bias)

Tolerated (regime 3, unmodeled)

ParameterWhy it stays ignored
userUpstream per-end-user attribution. Superseded, not merely unimplemented: the engine already carries its own caller identity (the token's auth.Principal) on every request.
service_tier, metadataNo observable effect on the response or the bill.
Any unrecognized or future OpenAI fieldRejecting unknown fields would break every SDK and proxy the moment upstream ships something new — the deliberate cost of staying drop-in. A typo such as tempreature is accepted and silently ignored for the same reason; it is an accepted trade-off, not an oversight.

Response contract

Every response — streamed or not — carries the full OpenAI envelope shape:

FieldNon-streamedStreamed (every chunk)
id, object, model
created
choices✔ ([] on the trailing usage chunk, when present)
usageOnly on the trailing chunk, when stream_options.include_usage was true

All chunks belonging to one response share the same id, created, and model. model reports the concrete served model on the non-streamed path; on a streamed response it echoes the requested value (which may be "auto"), since the concrete model is not known chunk-by-chunk.

The Anthropic dialect (POST /v1/messages) has its own, already-conforming contract and is not covered by this page.

On this page