> ## Documentation Index
> Fetch the complete documentation index at: https://mixroute.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenAI Decisions API

> Use gpt-6-luna with OpenAI-compatible Decisions: predicate, choice and score requests, typed answers, and error handling.

Evaluate shared evidence with named questions and receive typed answers through MixRoute's OpenAI-compatible Decisions endpoint. Use `gpt-6-luna`; this is a separate protocol from Jev System One.

## Access

`POST https://api.mixroute.ai/v1/decisions`

Send `Authorization: Bearer $MIXROUTE_API_KEY` and `Content-Type: application/json`. Keep the key server-side. Model and channel access depend on your account; a model appearing in the catalog does not guarantee every endpoint is available.

> [!WARNING]
>
> Use `input`, not `state`, and send `questions` as an array with optional names, not an object keyed by question IDs. Use `predicate`, not `noul`; use `choices` for categories and `levels` for scores, not Jev's `criteria`. The response `answers` is also an array. For the separate Jev protocol, see [System One](https://mixroute.ai/docs/api-reference/endpoint/jev.md).

## Request parameters

| Field | Type | Requirement | Description |
| - | - | - | - |
| `model` | string | Required | Use gpt-6-luna. Other chat models and Jev IDs are not interchangeable with this model for Decisions. |
| `input` | string / array | Required | Shared evidence: text or an ordered array of messages, function calls/outputs, or custom tool calls/outputs. All questions evaluate the same input. |
| `questions` | object\[] | Required | An array of 1-400 predicate, choice or score questions. Questions are independent; use separate calls if one decision depends on another. |
| `prompt_cache_key` | string / null | Optional | Caller-provided cache partition key, at most 64 characters. |
| `prompt_cache_options` | object / null | Optional | Optional prompt-cache settings. mode: implicit (default) or explicit; ttl: 30m (the currently supported value). Actual cache availability and billing depend on the route. |
| `safety_identifier` | string / null | Optional | Opaque end-user identifier, at most 128 characters. Prefer a stable non-personal identifier. |

## Question fields

| Field | Type | Requirement | Description |
| - | - | - | - |
| `type` | string | Required | predicate, choice or score. |
| `instructions` | string / array | Required | The decision to make. Use a text string or a nonempty array of supported content parts. Text and images can be placed directly in instructions without a message wrapper. |
| `name` | string | Optional | Name echoed in the answer. Give each question a distinct name; an unnamed answer can have name=null. |
| `choices` | object\[] | By type | Required for choice: 2-255 distinct options. Each has a required value (string or boolean) and an optional description (string). String "true" and boolean true are distinct values. |
| `levels` | object\[] | By type | Required for score: 2-10 ordered levels, lowest to highest. Each has a required label (string) and optional description (string). Indices start at 0. |

## Answer types

| Type | Description |
| - | - |
| `predicate` | probability is the estimated chance that the condition is true, from 0 to 1. It is not a boolean. |
| `choice` | choice is one supplied string or boolean. probabilities is an array of value/probability pairs; confidence is returned separately. |
| `score` | score is a probability-weighted average of level indices, from 0 to levels.length-1, and can be fractional. probabilities contains value (index), label and probability. |

Any question may return type=refusal instead of its requested type; other questions may still have answers. Match by name or question order, check type before reading fields, and handle refusal explicitly. Confidence and probabilities are not correctness guarantees.

## Request examples

Set `MIXROUTE_API_KEY` before calling. The examples submit once with no automatic retries. A timeout does not prove that inference was not executed.

> [tab] cURL
>
> ```bash theme={null}
> curl --request POST "https://api.mixroute.ai/v1/decisions" \
>   --header "Authorization: Bearer $MIXROUTE_API_KEY" \
>   --header "Content-Type: application/json" \
>   --data '{
>   "model": "gpt-6-luna",
>   "input": "CSV export fails on every attempt. The customer needs the file today and asks for a human agent.",
>   "questions": [
>     {
>       "type": "choice",
>       "name": "team",
>       "instructions": "Which team should handle this ticket?",
>       "choices": [
>         {
>           "value": "engineering",
>           "description": "Software defects"
>         },
>         {
>           "value": "billing",
>           "description": "Payments and refunds"
>         }
>       ]
>     },
>     {
>       "type": "score",
>       "name": "urgency",
>       "instructions": "How soon does the customer need help?",
>       "levels": [
>         {
>           "label": "No deadline",
>           "description": "No deadline is specified."
>         },
>         {
>           "label": "This week",
>           "description": "Help is needed within a week."
>         },
>         {
>           "label": "Today",
>           "description": "Help is explicitly required today."
>         }
>       ]
>     },
>     {
>       "type": "predicate",
>       "name": "needs_human",
>       "instructions": "Does the customer request a human agent?"
>     }
>   ]
> }'
> ```

> [tab] Python
>
> ```python theme={null}
> import json
> import os
> import requests
>
> payload = json.loads(r'''{
>   "model": "gpt-6-luna",
>   "input": "CSV export fails on every attempt. The customer needs the file today and asks for a human agent.",
>   "questions": [
>     {
>       "type": "choice",
>       "name": "team",
>       "instructions": "Which team should handle this ticket?",
>       "choices": [
>         {
>           "value": "engineering",
>           "description": "Software defects"
>         },
>         {
>           "value": "billing",
>           "description": "Payments and refunds"
>         }
>       ]
>     },
>     {
>       "type": "score",
>       "name": "urgency",
>       "instructions": "How soon does the customer need help?",
>       "levels": [
>         {
>           "label": "No deadline",
>           "description": "No deadline is specified."
>         },
>         {
>           "label": "This week",
>           "description": "Help is needed within a week."
>         },
>         {
>           "label": "Today",
>           "description": "Help is explicitly required today."
>         }
>       ]
>     },
>     {
>       "type": "predicate",
>       "name": "needs_human",
>       "instructions": "Does the customer request a human agent?"
>     }
>   ]
> }''')
> response = requests.post(
>     "https://api.mixroute.ai/v1/decisions",
>     headers={"Authorization": "Bearer " + os.environ["MIXROUTE_API_KEY"]},
>     json=payload,
>     timeout=60,
>     allow_redirects=False,
> )
> response.raise_for_status()
> result = response.json()
> if not isinstance(result.get("answers"), list):
>     raise ValueError("Expected an answers array")
> for answer in result["answers"]:
>     kind = answer["type"]
>     if kind == "refusal":
>         print(answer.get("name"), "refused")
>         continue
>     field = {"predicate": "probability", "choice": "choice", "score": "score"}.get(kind)
>     if field is None:
>         raise ValueError("Unexpected answer type: " + kind)
>     print(answer.get("name"), answer[field])
> ```

> [tab] JavaScript
>
> ```javascript theme={null}
> const apiKey = process.env.MIXROUTE_API_KEY;
> if (!apiKey) throw new Error("Set MIXROUTE_API_KEY");
> const payload = {
>   "model": "gpt-6-luna",
>   "input": "CSV export fails on every attempt. The customer needs the file today and asks for a human agent.",
>   "questions": [
>     {
>       "type": "choice",
>       "name": "team",
>       "instructions": "Which team should handle this ticket?",
>       "choices": [
>         {
>           "value": "engineering",
>           "description": "Software defects"
>         },
>         {
>           "value": "billing",
>           "description": "Payments and refunds"
>         }
>       ]
>     },
>     {
>       "type": "score",
>       "name": "urgency",
>       "instructions": "How soon does the customer need help?",
>       "levels": [
>         {
>           "label": "No deadline",
>           "description": "No deadline is specified."
>         },
>         {
>           "label": "This week",
>           "description": "Help is needed within a week."
>         },
>         {
>           "label": "Today",
>           "description": "Help is explicitly required today."
>         }
>       ]
>     },
>     {
>       "type": "predicate",
>       "name": "needs_human",
>       "instructions": "Does the customer request a human agent?"
>     }
>   ]
> };
> const response = await fetch("https://api.mixroute.ai/v1/decisions", {
>   method: "POST",
>   headers: {
>     Authorization: "Bearer " + apiKey,
>     "Content-Type": "application/json",
>   },
>   body: JSON.stringify(payload),
>   signal: AbortSignal.timeout(60000),
>   redirect: "error",
> });
> const result = await response.json();
> if (!response.ok) {
>   throw new Error(result.error?.message ?? "HTTP " + response.status);
> }
> if (!Array.isArray(result.answers)) throw new Error("Expected an answers array");
> for (const answer of result.answers) {
>   if (answer.type === "refusal") {
>     console.log(answer.name, "refused");
>     continue;
>   }
>   const field = { predicate: "probability", choice: "choice", score: "score" }[answer.type];
>   if (!field) throw new Error("Unexpected answer type: " + answer.type);
>   console.log(answer.name, answer[field]);
> }
> ```

## Response example

The response is a synchronous JSON object, not a Chat Completions choices array or a task to poll. Probabilities and token usage vary with the request.

```json theme={null}
{
  "model": "gpt-6-luna",
  "answers": [
    {
      "type": "choice",
      "name": "team",
      "choice": "engineering",
      "probabilities": [
        {
          "value": "engineering",
          "probability": 1
        },
        {
          "value": "billing",
          "probability": 0
        }
      ],
      "confidence": 1
    },
    {
      "type": "score",
      "name": "urgency",
      "score": 2,
      "probabilities": [
        {
          "value": 0,
          "label": "No deadline",
          "probability": 0
        },
        {
          "value": 1,
          "label": "This week",
          "probability": 0
        },
        {
          "value": 2,
          "label": "Today",
          "probability": 1
        }
      ],
      "confidence": 1
    },
    {
      "type": "predicate",
      "name": "needs_human",
      "probability": 1
    }
  ],
  "usage": {
    "input_tokens": 416,
    "input_tokens_details": {
      "cached_tokens": 0,
      "cache_write_tokens": 0
    },
    "output_tokens": 0,
    "output_tokens_details": {
      "reasoning_tokens": 0
    },
    "total_tokens": 416
  }
}
```

| Field | Description |
| - | - |
| `model` | Model used for the evaluation. |
| `answers` | Array in question order. Inspect type and name before consuming each answer. |
| `usage.input_tokens` | Input-token usage for the request. |
| `usage.input_tokens_details` | cached\_tokens and cache\_write\_tokens are included in input\_tokens; do not add them again. |
| `usage.output_tokens / usage.output_tokens_details` | Output usage and reasoning\_tokens. These are not a generated chat-text response. |
| `usage.total_tokens` | Total reported token usage. |

## Structured and image input

In a user message, content can include input\_text (text), input\_image (image\_url), and input\_json (data). JSON data must be an object or an array of objects; keys inside data are ordinary evidence, not instructions to fetch images or create messages. Input JSON is limited to 32 container levels, 10,000 values and 256 KiB of compact UTF-8 JSON.

```json theme={null}
{
  "model": "gpt-6-luna",
  "input": [
    {
      "role": "user",
      "content": [
        {
          "type": "input_text",
          "text": "Evaluate the ticket record."
        },
        {
          "type": "input_json",
          "data": {
            "ticket": {
              "feature": "CSV export",
              "fails": true,
              "deadline": "today"
            }
          }
        }
      ]
    }
  ],
  "questions": [
    {
      "type": "predicate",
      "name": "deadline_today",
      "instructions": "Does the ticket require action today?"
    }
  ]
}
```

Images use a public HTTP(S) URL or a base64 data URL, not file\_id. Optional detail values are auto, low, high or original. Developer/system messages accept text; assistant messages contain output text or refusals. Match tool outputs to calls using call\_id. Files, audio and item references are not accepted by this protocol. These are protocol shapes; availability of advanced inputs depends on the MixRoute route.

Replace the example image URL with a publicly accessible product image or a base64 data URL.

```json theme={null}
{
  "model": "gpt-6-luna",
  "input": [
    {
      "role": "user",
      "content": [
        {
          "type": "input_text",
          "text": "Inspect this product photo."
        },
        {
          "type": "input_image",
          "image_url": "https://example.com/product.png",
          "detail": "auto"
        }
      ]
    }
  ],
  "questions": [
    {
      "type": "predicate",
      "name": "visible_damage",
      "instructions": "Does the product show a crack, tear, or dent?"
    }
  ]
}
```

## Optional prompt caching

Cacheable shared input needs at least 1,024 tokens. Questions are outside the reusable input prefix. An implicit breakpoint is used by default; explicit boundaries use `prompt_cache_breakpoint: {"mode": "explicit"}` on text/image parts in shared input. With prompt\_cache\_options.mode=explicit and no boundaries, caching is disabled. Question instructions do not accept cache breakpoints. Do not assume the upstream cache discount or price is your MixRoute rate.

## Errors and billing

Check HTTP status first. Gateway errors can contain error.message, error.type, error.code and error.param; param can be empty. Keep x-oneapi-request-id for support, without logging your API key.

| HTTP | Description |
| - | - |
| 400 invalid\_request | Check input and the questions array. A questions object belongs to Jev, not this endpoint. |
| 401 / 403 | Check authentication and endpoint/model access. |
| 503 model\_not\_found | The selected model has no available channel for this key/group. This is not a judgment result. |
| 429 / other 5xx | Honor Retry-After when present and use bounded retries only when appropriate; avoid automatic duplicate inference after uncertain timeouts. |

```json theme={null}
{
  "error": {
    "message": "questions must be a non-empty array",
    "type": "mixroute_error",
    "param": "",
    "code": "invalid_request"
  }
}
```

Use usage and the current [Model Marketplace](https://console.mixroute.ai/models) to understand billing. Responses and Chat Completions rates must not be assumed to apply to Decisions. Account multipliers and route pricing determine actual charges.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.