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

# Decision Models (Beta)

> Use the beta API to route support requests, evaluate yes/no conditions, and rate urgency with the TypeSafe-compatible Telnyx decision model API.

<Note>
  Decision Models is in **beta**. Choose between Flash and Pro using the `model` field.
</Note>

`POST https://api.telnyx.com/v2/ai/typesafe/v1/systemone` evaluates shared context against
named questions and returns structured answers. Use `choice` to select a
category, `noul` to evaluate a yes/no condition, and `score` to rate an ordered
rubric. A request can combine all three question types.

The endpoint supports a subset of the
[TypeSafe System One API](https://docs.typesafe.ai/concepts/system-one)
request format and preserves its typed answer shapes. It returns one complete
JSON response. See the [API reference](/api-reference/decision-models/evaluate-decision-models-typesafe-compatible)
for the full request and response schemas.

## Choose a model

| Model | Use case |
| - | - |
| `telnyx/decision-flash` | Lowest cost and latency for high-volume decisions. |
| `telnyx/decision-pro` | Decisions that need long context, including inputs beyond Jev’s 32k per-decision limit. |

Set `model` once per request; every question uses that model. If omitted, it
defaults to `telnyx/decision-flash`. Other model values are rejected. Telnyx
manages the underlying models behind these public aliases.

Compare both aliases using the same state and questions on representative
examples. Measure answer quality and response time for the application;
confidence scores alone do not establish which model is more accurate.

## Use Pro for decisions over long context

Choose `telnyx/decision-pro` when the decision depends on more context than Jev
can accept in one question. Examples include routing a case using its complete
support history, checking an exception against a policy and its amendments, or
rating an incident from a long transcript and operational records.

[Jev 1.13 documents a 32k-token limit for state plus the longest question](https://docs.typesafe.ai/models),
with a separate 64k limit for state plus all questions. Pro’s larger context
capacity lets an application keep relevant evidence together when a decision
would otherwise require splitting or summarizing the input to fit Jev.

Include the question, criteria, and formatting when sizing requests. More context
does not guarantee a more accurate answer: keep the evidence relevant and test
against labeled examples from the application. Use Flash when cost and latency
matter most and the decision fits its supported context.

## Classify a support incident

Set `TELNYX_API_KEY` to a Telnyx API key. Send the incident as `state`, then
use named questions to select the team, identify a production incident, and
rate urgency in one request.

```bash theme={null}
curl --fail-with-body --max-time 100 \
  'https://api.telnyx.com/v2/ai/typesafe/v1/systemone' \
  -H "Authorization: Bearer ${TELNYX_API_KEY}" \
  -H 'Content-Type: application/json' \
  --data '{
    "model": "telnyx/decision-flash",
    "state": "Our production calls are failing. Every customer is affected.",
    "questions": {
      "team": {
        "type": "choice",
        "instructions": "Choose the team that should handle this incident.",
        "criteria": {
          "billing": "Payments and refunds",
          "technical_support": "Service faults and technical problems",
          "sales": "New purchases"
        }
      },
      "production_incident": {
        "type": "noul",
        "instructions": "Does the message describe an active production incident?"
      },
      "urgency": {
        "type": "score",
        "instructions": "Rate operational urgency.",
        "criteria": ["Low", "Normal", "High", "Critical"]
      }
    }
  }'
```

The response contains `model`, `answers`, and `usage` directly, with no `data`
wrapper. The `model` value identifies the public alias used for the request.
Each key in `answers` matches a key in
`questions`. The following example rounds values for readability; results and token counts can vary.

```json theme={null}
{
  "model": "telnyx/decision-flash",
  "answers": {
    "team": {
      "type": "choice",
      "choice": "technical_support",
      "probabilities": {
        "billing": 0.002472,
        "technical_support": 0.997267,
        "sales": 0.000261
      },
      "confidence": 0.982052
    },
    "production_incident": {
      "type": "noul",
      "noul": 0.999196
    },
    "urgency": {
      "type": "score",
      "score": 2.997424,
      "legend": {"0": "Low", "1": "Normal", "2": "High", "3": "Critical"},
      "probabilities": {"0": 0.000335, "1": 0.000035, "2": 0.001501, "3": 0.998129},
      "confidence": 0.989420
    }
  },
  "usage": {"input_tokens": 267, "output_tokens": 4}
}
```

Read `answers.team.choice` to choose a destination. Interpret
`answers.production_incident.noul` as a numeric yes-score, and
`answers.urgency.score` on the requested 0–3 rubric. Token usage includes
shared-context preparation and question evaluation, so it can exceed the
size of the unique input text.

## Choose question types

Every question requires `type` and `instructions`. Instructions and `state`
can be strings, JSON objects, or arrays. Text-only conversation histories
are supported; image and audio inputs are not supported.

| Type | Criteria | Answer |
| - | - | - |
| <code style={{ whiteSpace: "nowrap" }}>choice</code> | Object with 2–64 option keys. Values are strings or <code style={{ whiteSpace: "nowrap" }}>null</code>; <code style={{ whiteSpace: "nowrap" }}>null</code> uses the key as the option description. | <code style={{ whiteSpace: "nowrap" }}>choice</code> contains the winning key, <code style={{ whiteSpace: "nowrap" }}>probabilities</code> contains all option scores, and <code style={{ whiteSpace: "nowrap" }}>confidence</code> describes concentration. |
| <code style={{ whiteSpace: "nowrap" }}>noul</code> | Optional object with string descriptions for <code style={{ whiteSpace: "nowrap" }}>true</code> and <code style={{ whiteSpace: "nowrap" }}>false</code>. Defaults to <code style={{ whiteSpace: "nowrap" }}>\{"true":"Yes","false":"No"}</code>. | <code style={{ whiteSpace: "nowrap" }}>noul</code> is the positive outcome's score from 0 to 1. There is no separate confidence field. |
| <code style={{ whiteSpace: "nowrap" }}>score</code> | Ordered array of 2–64 description strings. | <code style={{ whiteSpace: "nowrap" }}>score</code> is the expected zero-based index; <code style={{ whiteSpace: "nowrap" }}>legend</code> and <code style={{ whiteSpace: "nowrap" }}>probabilities</code> use stringified indices. |

A `choice` question selects one option. Use separate `noul` questions when
several independent conditions can be true at once. For example, a support
message can both request a refund and report a service fault.

A `score` answer can be fractional. For criteria `["Low", "Normal", "High",
"Critical"]`, the range is 0–3. Compute the expected score as
`sum(index * probability)`; do not treat it as a 0–1 probability or an array
index without an application-specific decision rule.

## Use scores in application logic

Option scores are normalized relative preferences across the supplied
choices. Changing the choices or their wording can change the distribution.
They are not calibrated probabilities that a decision is correct.

For `choice` and `score`, `confidence` is normalized entropy:
`1 - H(p) / ln(N)`, where `H(p) = -sum(p * ln(p))` and `N` is the number of
options. It approaches 0 for a uniform distribution and 1 when the score is
concentrated on one option. It is different from the winning option's
probability.

Choose review thresholds using representative examples from the application.
The following Python example uses direct HTTP, selects a team, and falls back
to manual review when the winning option has a low relative score. The `0.8`
threshold is illustrative and must be evaluated for the application's data.

```python theme={null}
import json
import os
from urllib.error import HTTPError
from urllib.request import Request, urlopen

payload = {
    "model": "telnyx/decision-pro",
    "state": "The invoice looks right, but the usage page counts calls twice.",
    "questions": {
        "team": {
            "type": "choice",
            "instructions": "Choose the team for the underlying issue.",
            "criteria": {
                "billing": "Incorrect charges or refunds",
                "technical_support": "Service faults or software defects",
                "sales": "New purchases",
            },
        }
    },
}
request = Request(
    "https://api.telnyx.com/v2/ai/typesafe/v1/systemone",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {os.environ['TELNYX_API_KEY']}",
        "Content-Type": "application/json",
    },
    method="POST",
)
try:
    with urlopen(request, timeout=100) as response:
        result = json.load(response)
except HTTPError as error:
    raise RuntimeError(f"Decision model failed with HTTP {error.code}") from error

answer = result["answers"]["team"]
selected = answer["choice"]
winning_score = answer["probabilities"][selected]
destination = selected if winning_score >= 0.8 else "manual_review"
print(destination)
```

## Migrate a TypeSafe request

Point HTTP requests to `https://api.telnyx.com/v2/ai/typesafe/v1/systemone` and authenticate
with a Telnyx Bearer API key. Set `model` to a supported Telnyx alias and send
`state` and `questions`, keeping the question IDs used by the application.

Compatibility applies to the supported JSON request subset and typed answer
shapes. It does not imply identical model predictions, confidence calibration,
pricing, or token accounting.

| Area | Telnyx behavior |
| - | - |
| Route | `POST /v2/ai/typesafe/v1/systemone`. |
| Model selection | Set `model` to `telnyx/decision-flash` or `telnyx/decision-pro`. Omitted values default to Flash; unsupported values are rejected. The response returns the selected alias. |
| Question instructions | Required for every question. TypeSafe also accepts omitted or null instructions; this subset does not. |
| Criterion descriptions | Strings for all question types; `choice` also accepts `null`. TypeSafe's object and array descriptions are not supported here. |
| Question and option limits | 1–64 questions; 2–64 options for each `choice` or `score`. TypeSafe's one-level score rubric is not supported. |
| Answers | Named `choice`, `noul`, and `score` answers, with `model` and token `usage` at the top level. |
| SDK base URL | Set `base_url="https://api.telnyx.com/v2/ai/typesafe"`. The SDK appends `/v1/systemone`; no route adapter is required. |

The [official TypeSafe Python SDK](https://github.com/typesafe-ai/typesafe-sdk-python)
appends `/v1/systemone` to the configured base URL. The route preserves that
behavior. Its `client.models.list()` method uses a separate TypeSafe route and
is outside this endpoint's compatibility scope. The SDK automatically sends a
model value, so changing only the base URL is not sufficient: replace its
default or existing model with a supported Telnyx alias.

## Use the TypeSafe Python SDK

Install the [official SDK](https://github.com/typesafe-ai/typesafe-sdk-python):

```bash theme={null}
pip install typesafe-sdk
```

Set the Telnyx base URL, API key, and model alias. Existing `system_one()`
calls using the supported question subset keep the same method and answer
accessors. This example uses the SDK's `Choice`, `Noul`, and `Score` types.

```python theme={null}
import os
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

with TypeSafeClient(
    api_key=os.environ["TELNYX_API_KEY"],
    base_url="https://api.telnyx.com/v2/ai/typesafe",
    timeout=100,
) as client:
    result = client.system_one(
        model="telnyx/decision-flash",
        state="Our production calls are failing. Every customer is affected.",
        questions={
            "team": Choice(
                instructions="Choose the team that should handle this incident.",
                criteria={
                    "billing": "Payments and refunds",
                    "technical_support": "Service faults and technical problems",
                    "sales": "New purchases",
                },
            ),
            "production_incident": Noul(
                instructions="Does the message describe an active production incident?",
            ),
            "urgency": Score(
                instructions="Rate operational urgency.",
                criteria=["Low", "Normal", "High", "Critical"],
            ),
        },
    )
    print(result.choices["team"].choice)
    print(result.nouls["production_incident"].noul)
    print(result.scores["urgency"].score)
```

The SDK sends a Bearer authorization header from `api_key`. Use a Telnyx key
for Telnyx requests. Keep `/v1/systemone` out of `base_url`; the SDK adds it.

## Limits and failures

Use up to 64 named questions against one shared `state`. Split larger workloads
into separate requests and bound client concurrency. Responses arrive after
the complete evaluation; there is no streaming or per-question partial-success
envelope. Request-body and token limits also apply, so question count alone
does not guarantee that a request fits.

The public request fields are `model`, `state`, and `questions`. `model` is
optional and defaults to `telnyx/decision-flash`; `state` and `questions` are
required. Unsupported model values and unknown fields are rejected.
Do not send `input`, `labels`, `tier`, `stream`,
`temperature`, or `max_tokens` to this endpoint.

| Status | Action |
| - | - |
| `401` | Check the Telnyx API key. |
| `413` | Reduce or fix the request body. |
| `422` | Correct the schema or split context that exceeds token limits. |
| `429`, `529` | Reduce concurrency and retry with bounded exponential backoff and jitter. |
| `502`, `503` | Retry transient service failures with bounded backoff. |
| `504` | Reduce request size or concurrency before retrying. |

Check the HTTP status before parsing an answer. Honor `Retry-After` when
present. Correct validation failures before retrying; retries repeat evaluation
work. A decision model service error uses this shape, with a message describing
the failure:

```json theme={null}
{
  "error": {
    "message": "Each question requires text/JSON instructions and 2–64 options"
  }
}
```
