> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.milliseconds.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.milliseconds.ai/_mcp/server.

# Decisions and probabilities

A capability never returns prose. It returns a decision your code can branch on, plus the numbers behind that decision. This page explains each number, and what it does not tell you.

The API rounds every probability and score to three decimals. Offsets and `level` stay integers.

## The four numbers

| Number          | Returned by                                                   | Range                   | Meaning                                                                                     |
| --------------- | ------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------- |
| `probability`   | yes-no, classify, answer, verify, and each entity in entities | 0 to 1                  | How strongly the text supports this decision                                                |
| `confidence`    | classify, rate                                                | 0 to 1                  | `1 − normalized entropy` over all options. 1 is one clear winner, 0 is a flat distribution. |
| `score`         | rate                                                          | 0 to `scale.length − 1` | Probability-weighted position on the scale                                                  |
| `start` / `end` | answer, entities                                              | character offsets       | Where the span sits in your text                                                            |

`scores` accompanies `classify` (an object keyed by label name) and `rate` (an array in scale order). Both sum to 1.

## Probability

`probability` measures how strongly the text supports one decision. The formula differs per capability.

| Capability                                | How `probability` is produced                                               |
| ----------------------------------------- | --------------------------------------------------------------------------- |
| `yes-no`, no hints                        | The raw score for the statement. It is **not** normalized against anything. |
| `yes-no`, with `when_true` / `when_false` | `p_true / (p_true + p_false)` over the two hint labels                      |
| `classify`                                | The winning label's share after normalizing all label scores                |
| `answer`, `entities`                      | The span's own confidence. `answer` returns `0` when the answer is `null`.  |
| `verify`                                  | The confidence of the matching span, or `0` when nothing matches            |

Two booleans come straight from `probability`. `yes-no` sets `answer` to `true` at `probability >= 0.5`. `verify` sets `matches` the same way.

A real response, `classify` over a support ticket, three labels:

```json
{"label":"billing","probability":0.942,"confidence":0.797,"scores":{"billing":0.942,"shipping":0,"account":0.058}}
```

> **Warning**
>
> A `yes-no` probability without hints is an independent score, not a share of a pair. Do not compare it to a normalized `classify` probability on the same scale. Set a threshold per capability and per action.

## Confidence

`confidence` is `1 − H(p) / ln(n)`. `H` is the Shannon entropy of the normalized scores, and `n` is the number of options. It measures the shape of the whole distribution, not the strength of the winner.

* **Confidence near 1**: one option dominates. The rest are near zero.
* **Confidence near 0**: the options split the mass. The winner won by very little.
* `confidence` returns `1` when fewer than two options exist.

Read `probability` and `confidence` together. A high probability with low confidence means two labels both scored high, and normalization gave one of them the lead.

> **Note**
>
> `yes-no`, `answer`, `entities`, `extract` and `verify` do not return `confidence`. Use `probability` on those capabilities.

## Score and level (rate)

`rate` returns two views of the same distribution. `level` is the index of the most likely level. `score` is `Σ p_i · i`, the probability-weighted position across the whole scale. `rate` returns no `probability` field.

A real response, scale `["Calm","Annoyed","Frustrated","Threatening to leave"]`:

```json
{"score":1.585,"level":2,"label":"Frustrated","confidence":0.371,"scores":[0,0.471,0.472,0.057]}
```

`level` is 2, but it beat level 1 by 0.001. `score` 1.585 sits between the two, and `confidence` 0.371 reports the ambiguity. Use `score` for trends and sorting. Use `level` only when `confidence` is high.

## Spans

`answer` and `entities` return spans of your input text, never generated words. `start` and `end` are character offsets into the text you sent, so you can highlight or redact the exact characters.

A real response, `answer` over a batch of questions, including one the text cannot answer:

```json
{
  "results": [
    {"question":"Who announced the product?","answer":"John Ternus","probability":0.998,"start":0,"end":11},
    {"question":"How much does it cost?","answer":"$1,999","probability":0.989,"start":89,"end":95},
    {"question":"What is the CEO salary?","answer":null,"probability":0,"start":null,"end":null}
  ]
}
```

When nothing fits, `answer`, `start` and `end` are all `null`, and `probability` is `0`. Check for `null` before you slice the text.

`entities` sorts its spans by `start` ascending. An entity always carries `start` and `end`, never `null`.

`verify` returns no offsets. It returns `found`, the raw spans the model read for that field. Compare `found` against your value when `matches` is `false`.

A real response, `verify` of the value `4417` against a text that says `#4471`:

```json
{"matches":false,"probability":0,"found":["#4471"]}
```

## Read the numbers in code

#### curl

```bash
curl -s https://api.milliseconds.ai/v1/decision-machine-1/classify \
  -H "Content-Type: application/json" \
  -H "authorization: Bearer test_sk-..." \
  -d '{
    "text": "I was charged twice for my subscription this month and support has not replied.",
    "labels": {"billing": "payments, invoices, charges, refunds", "shipping": "delivery, tracking, returns", "account": "login, password, profile settings"}
  }'
```

#### Python

```python
import requests

r = requests.post(
    "https://api.milliseconds.ai/v1/decision-machine-1/classify",
    headers={"authorization": "Bearer test_sk-..."},
    json={
        "text": "I was charged twice for my subscription this month and support has not replied.",
        "labels": {
            "billing": "payments, invoices, charges, refunds",
            "shipping": "delivery, tracking, returns",
            "account": "login, password, profile settings",
        },
    },
)
d = r.json()

if d["probability"] >= 0.9 and d["confidence"] >= 0.7:
    route(d["label"])          # act
else:
    queue_for_review(d["scores"])  # a person decides
```

#### TypeScript

```typescript
const res = await fetch(
  "https://api.milliseconds.ai/v1/decision-machine-1/classify",
  {
    method: "POST",
    headers: { "Content-Type": "application/json", authorization: "Bearer test_sk-..." },
    body: JSON.stringify({
      text: "I was charged twice for my subscription this month and support has not replied.",
      labels: {
        billing: "payments, invoices, charges, refunds",
        shipping: "delivery, tracking, returns",
        account: "login, password, profile settings",
      },
    }),
  },
);
const d = await res.json();

if (d.probability >= 0.9 && d.confidence >= 0.7) {
  route(d.label);
} else {
  queueForReview(d.scores);
}
```

That call returns:

```json
{"label":"billing","probability":0.995,"confidence":0.972,"scores":{"billing":0.995,"shipping":0,"account":0.005}}
```

These numbers differ from the capture at the top of the page, because the label descriptions differ. Label wording moves every number.

Every request needs an API key. See [Authentication](/authentication).

## What the numbers do not mean

* **They are not measured accuracy.** A probability of 0.94 is the model's score for that decision. It is not a promise that 94 of 100 such decisions are correct. Measure your own rates on a labelled set.
* **They do not transfer between capabilities.** A 0.8 from `yes-no` and a 0.8 from `classify` come from different formulas. Tune each one separately.
* **They do not transfer between label sets.** Change a label, a description, or a hint, and every number moves. Re-check your thresholds after any wording change.
* **`confidence` is not correctness.** It reports the shape of the distribution. A confident wrong label is still wrong, and usually means the label text describes the wrong case.
* **A high `classify` probability with a tiny label set proves little.** Normalization spreads the mass over the labels you sent, and nothing else.
* **`extract` returns no probabilities.** It returns `data` with `null` for any value the text does not carry. Use [verify](/capabilities/verify) when a field must be trusted.

## Next

* [Thresholds and confidence routing](/concepts/thresholds) turns these numbers into act, confirm, and escalate bands.
* [Writing good statements and labels](/concepts/writing-good-questions) shows why label wording moves every number on this page.
* [Golden sets](/evaluate/golden-sets) gives you the labelled data to measure real accuracy.