Skip to main content
Point an OpenAI SDK at tokens.flex.ai and, for everything marked Supported below, code written against api.openai.com works by changing only base_url and model. This page is the exact compatibility matrix — the endpoints and request fields the gateway implements at launch.

Spec version we track

The chat-completions request validator at our gateway is authored against openai-python 1.109.1 — specifically the request shape openai.types.chat.completion_create_params.CompletionCreateParamsBase. That same version is pinned in our gateway image, so the SDK you call against and the shape we validate can’t drift apart silently. We validate a focused subset of the SDK’s surface — the fields where validation gaps were causing real problems (off-spec inputs returning 200 with garbage, or 500 with a stack trace). Unknown or unsupported fields are forwarded to the engine and accepted or rejected there. The matrix below is the exact subset. Bumps are deliberate: when we upgrade the pin, a snapshot test fails until we audit the SDK diff, and we update this page in the same PR.

Endpoints

Request fields

Multi-turn tool calls

When a model turn is a pure tool call, the assistant message has no text to carry. OpenAI-compatible clients disagree on how to serialize that: some omit content, some send content: null, and some — notably the Vercel AI SDK — send content: "". Because you resend that assistant turn verbatim on the next request so the model can see its own call, whichever shape your client produces is the shape we receive. The gateway accepts all three on an assistant message that carries tool_calls, and normalizes an empty string to null before the request reaches the model. You don’t need a client-side wrapper to rewrite content — send the message your tool-calling library produces. That empty-string acceptance is scoped to exactly this case. On any message that is not an assistant message carrying tool_calls, an empty content string is still rejected with a 400 (param: "messages[N].content"), since an empty prompt otherwise reaches the model as nothing but chat-template tokens.

Response fields

Tool-result message ordering

A normal agent loop ends a turn with tool results and then a user message — “here are the results, now summarize.” OpenAI accepts a user message placed immediately after a tool message, and so does this gateway, on every served model. A few models ship a chat template that enforces strict role alternation and would otherwise reject that sequence; for those the gateway normalizes the request for you before it reaches the model, so the behavior matches OpenAI uniformly. You do not need to restructure your history or insert a bridge turn yourself — send the message sequence your tool-calling library produces.

Authentication & headers

  • Bearer tokens only (Authorization: Bearer sk-…). No OAuth, no organization header.
  • Our rate limit response includes Retry-After and x-ratelimit-* headers — see errors.
  • CORS is restricted; the API is intended for server-to-server calls, not browsers.

Error shape

Errors use OpenAI’s envelope — { "error": { "message", "type", … } } — in two flavors. Request-validation failures (400) come back verbatim in OpenAI’s shape, with param set to the offending field path (or null when it isn’t field-specific) and code set to null — we reserve non-null code strings the way OpenAI does, for a narrow documented set like context_length_exceeded:
Auth and quota failures add a FlexAI-only doc_url extension so your first failure points you at the dashboard:
See the error reference for the full list of codes.