Skip to main content
A decision model answers questions about an input with probabilities. It writes no text. You send the input and named questions, and you get one answer back per question.

Two endpoints

  • POST /v1/decisions takes OpenAI’s Decisions format.
  • POST /v1/systemone takes the System One format, which Microsoft and Cloudflare use.
Any decision model works on either endpoint. When the model speaks the other format, we translate your request and its answer. The answer always comes back in the format of the endpoint you called.

The models

GET /v1/models marks each one with a decisions block. It lists the routes, the question limit, and the price on each route. gpt-6-luna is also a text model, so it keeps serving the text endpoints. The other three write no text, so a text endpoint refuses them with 400 model_surface_mismatch. A text model sent to a decision endpoint gets the same error.

Your own keys only

Decision models run on the provider keys you add in the dashboard.
  • openai uses your OpenAI key.
  • foundry uses your Azure AI Foundry key and resource.
  • cloudflare uses your Workers AI token and account ID.
If your organization serves a route on managed keys, a decision request for that route returns 400 decisions_byok_only. A missing key returns that route’s own error, such as 400 no_foundry_key. On Foundry, the model field names a deployment, and we send the model ID you used. Name the deployment after the model: gpt-6-luna or microsoft-decision-1.

No Flex Race

Decision requests never race. OpenAI’s Decisions API refuses a flex tier, and System One has no tiers at all.
  • Send "start_within": "default" in the body, or x-start-within: default as a header.
  • A duration returns 400 flex_model_not_capable.
  • priority or auto returns 400 tier_unsupported_on_decisions.
  • An API key with a deadline default works. Its fallback tier applies.
x-flexinference-routing-reason reads tier_direct, or chain_advanced when a backup served. Name backups in provider, the same as on the text endpoints. A route that answers 429, 5xx, or no connection passes the request to the next one. Neither endpoint streams. "stream": true returns 400 unsupported_parameter.

OpenAI’s Decisions format

The answer looks like this:

The System One format

The answer looks like this:
A score is the probability-weighted level, so it can land between two levels.

How we translate

  • A noul is a predicate. Its answer is a probability in both formats.
  • A choice’s criteria object becomes choices, one value per label.
  • A score’s criteria list becomes levels. Each level’s label is its position, "0", "1", and so on, the same key the answer’s legend and probabilities use.
  • System One names each question with its key. OpenAI names it with name and answers in a list, in question order.
  • A noul’s true and false criteria have no OpenAI field, so we add them to the instructions.
  • A System One question with no instructions gets one for its type, such as “Which option best fits the state?”. OpenAI requires instructions.
  • Structured state becomes one input_json part. OpenAI’s text and JSON parts join into one state string, and a single JSON part stays structured.
  • An OpenAI level becomes "label: description" in System One.
  • OpenAI can refuse to answer a question. On /v1/systemone that answer is { "type": "refusal" } under the question’s name.
  • For a System One model we drop safety_identifier and the three prompt-cache fields. None of them changes an answer.
We refuse a request that can’t be translated with 400 decision_translation_unsupported, before we call the provider.
  • Images. Send them in the model’s own format: input_image parts for gpt-6-luna on /v1/decisions, or images for clef on /v1/systemone.
  • A field the other format has no place for.
  • A choice with one option, or a score with one level, for an OpenAI model. OpenAI needs at least two.
  • More than 255 options for a System One model.
When the request and the model share a format, we forward your body as you wrote it, minus our own fields: start_within, provider, stream, and include_cost. The answer is the provider’s own body.

What we check first

400 invalid_decision_request names the field that breaks a rule of its format:
  • input or state is missing.
  • A question count is outside the model’s range.
  • Two questions share a name.
  • A choice offers true and "true" as two options.
  • The body nests deeper than 64 levels, or holds a number JSON can’t carry.

Cost

Decision models bill input tokens only.
  • gpt-6-luna on openai: $0.10 per million input tokens. Cache reads bill at $0.01 and cache writes at $0.125. All three double above 272,000 input tokens. We report the cost in x-flexinference-cost.
  • clef costs $0.24 and `clef-flash` $0.038 per million input tokens on Cloudflare’s price list. A serve on your own Cloudflare account carries no cost header, the same as every cloud route.
  • Microsoft publishes no rate for either Foundry model yet, so a Foundry serve carries no cost header.
A translated answer also carries usage.cost and usage.routing in its body. A same-format answer is the provider’s own body, so it reports cost on the header only. Errors on /v1/systemone come back as { "detail": "...", "param": ..., "flexinference": { ... } }, where param names the field at fault. See Errors for every code.