---
title: "Confidence-gated routing"
type: pattern
tags: [patterns, confidence, routing, safety, thresholds]
created: 2026-09-17
updated: 2026-09-17
confidence: high
sources:
  - raw/docs/patterns__confidence-routing.md
  - raw/docs/patterns.md
  - raw/github/skills/skills/typesafe-ai/SKILL.md
jev_version: "jev-1.13.0"
summary: "Treat confidence as a second decision axis: one floor below which nothing is automated, then a per-action threshold sized to the consequences of being wrong."
---

# Confidence-gated routing

> **TL;DR** "The answer tells you what; confidence tells you whether to act." Put a global floor under everything (the example uses `0.6` → human), then give each action its own threshold scaled to its blast radius (the example uses `> 0.85` before auto-approving a money transfer, and confirm-with-the-user otherwise).

## Problem

A classifier gives you one answer and one number. Acting on the answer alone treats "almost certainly a balance check" and "probably an approval, maybe not" identically — even though one costs a wasted sentence and the other moves money.

From `raw/docs/patterns__confidence-routing.md`:

> One of TypeSafe's most powerful features is [confidence](/confidence). By being intentional with the way you gate decisions on confidence, you can build systems that are both reliable and safe.

> While you always want to have reasonable confidence in interpreting the user's intent, some actions are riskier than others and thus demand a higher confidence threshold.

## Pattern

Use `confidence` as a second axis alongside the answer:

1. **A floor.** Below it, nothing is automated — hand off to a person.
2. **Per-action thresholds above the floor.** Each branch's threshold is set by the cost of acting on a misclassification, not by the model.
3. **A middle band for high-stakes actions.** Between the floor and the action's threshold, confirm rather than act or escalate.

Confidence is reported on Choice and Score answers only; Noul returns a probability (`noul`) and no separate confidence. See [[concepts/confidence]].

## Implementation

The documented example is a voice banking interface.

### Step 1: determine the user's intent

Questions, verbatim from the source (the example sends no `state`; in a real request the transcribed utterance is the state — add `"state": ...` and `"model": "jev-latest"`, see [[reference/http-api]]):

```json title="questions"
{
  "intent": {
    "type": "choice",
    "instructions": "What action is the user requesting?",
    "criteria": {
      "check_balance": "Check the balance of an account",
      "approve_transfer": "Approve the pending transfer request",
      "other": "Something else"
    }
  }
}
```

Note the `other` option: a no-match outcome so the model is not forced to pick between two wrong answers.

### Step 2: confidence-gated routing

```python
action = response.answers["intent"]

# Below 0.6 confidence on any action, route to a human
if action.confidence < 0.6:
    route_to_support_agent(account_id)

elif action.choice == "check_balance":
    # Low stakes. 0.6 confidence is sufficient.
    show_balance(account_id)

elif action.choice == "approve_transfer":
    if action.confidence > 0.85:
        # High stakes, but high confidence. Safe to act automatically.
        approve_transfer(account_id)
    else:
        # High stakes, moderate confidence. Verify intent first.
        ask_user_to_confirm("Just to confirm: you would like to approve this transfer, is that correct?")

else:
    route_to_support_agent(account_id)
```

The same gate in TypeScript with `@typesafe-ai/sdk` 0.6.0 — **adapted from the Python sample (not in upstream docs)**, same thresholds and branches. `choice()` builds the question and the answer carries `choice`, `probabilities`, and `confidence`; see [[reference/javascript-sdk]].

```ts title="routing.ts"
import { choice, TypeSafeClient } from "@typesafe-ai/sdk";

const client = new TypeSafeClient(); // reads TYPESAFE_API_KEY

const { answers } = await client.systemOne({
  state: transcript, // the transcribed utterance
  questions: {
    intent: choice("What action is the user requesting?", {
      check_balance: "Check the balance of an account",
      approve_transfer: "Approve the pending transfer request",
      other: "Something else",
    }),
  },
});

const action = answers.intent;

// Below 0.6 confidence on any action, route to a human
if (action.confidence < 0.6) {
  routeToSupportAgent(accountId);
} else if (action.choice === "check_balance") {
  // Low stakes. 0.6 confidence is sufficient.
  showBalance(accountId);
} else if (action.choice === "approve_transfer") {
  if (action.confidence > 0.85) {
    // High stakes, but high confidence. Safe to act automatically.
    approveTransfer(accountId);
  } else {
    // High stakes, moderate confidence. Verify intent first.
    askUserToConfirm("Just to confirm: you would like to approve this transfer, is that correct?");
  }
} else {
  routeToSupportAgent(accountId);
}
```

The source's reading of those numbers:

> The 0.6 floor catches anything the model is genuinely uncertain about. Above that floor, each action type has its own threshold based on the consequences of acting on a wrong classification. Checking a balance at 0.6 is fine because the worst case is the user having to listen to the balance read-out. But approving a transfer requires very high confidence (>0.85), otherwise the system should ask the user to confirm.

`0.6` and `0.85` are this example's constants, not model constants. Keep them in one place: the agent-skill guidance is "Put the constants (questions and thresholds) in a single place so they're easy to review."

## When it fails

- **Confidence is not a correctness score.** From the agent skill: "Choice/Score confidence summarizes distribution concentration, not overall workflow correctness or permission to act." A confident wrong answer is possible; see [[concepts/confidence]] and [[concepts/jaggedness-jev-1-13]].
- **Ties between acceptable answers look like uncertainty.** "Several acceptable alternatives can also spread probability; low confidence need not invalidate a harmless preference choice." A gate that escalates on every spread will escalate cases where either branch was fine.
- **Thresholds everywhere.** The agent-skill page names this as a common issue: "If all you care about is choosing the best option, you just need to choose the option with the highest confidence (rather than setting a confidence threshold). If you have a specific statistical algorithm in mind, you should probably be using probabilities instead of confidence."
- **Thresholds set too high or too low.** Also from the agent-skill page: "It's possible that your thresholds are either set too high (causing false negatives) or too low (causing false positives). You may also need to tweak your questions to be more specific." Evaluate thresholds on your own data — see [[guides/testing-and-evaluation]].
- **Gating a Noul on `confidence`.** Noul answers carry no `confidence` field; threshold on `noul` itself, and remember that "A Noul near 0.5 means similar probability for yes and no, not medium intensity."
- **Unpinned model versions.** Tuned thresholds are tied to a model version; `jev-latest` moves. See [[reference/models-and-pricing]].

## Variants

- **Confirm instead of escalate.** The example's middle band asks the user rather than routing to staff — cheaper, and only available when there is a user in the loop.
- **Gate on a second question's confidence too.** [[patterns/intent-routing]] checks `complexity.confidence < 0.5` as well as the intent's, sending unclear-complexity cases to a human.
- **Gate on probabilities rather than confidence.** Where a specific statistical rule applies, use the `probabilities` map directly; see [[cookbooks/classification-using-confidence]].
- **Escalate to a reasoning model rather than a person.** The agent skill's "Verify and escalate" direction: "send uncertain or failing cases to a person or reasoning model." See [[cookbooks/sde-cascade]].

## Related

- [[concepts/confidence]] — what the number is and how it is derived
- [[patterns/overview]] — the four-pattern catalog
- [[patterns/intent-routing]] — routing that layers this gate on a classifier
- [[concepts/choice]] — where `confidence` comes back
- [[guides/testing-and-evaluation]] — choosing thresholds on your own data
- [[concepts/jaggedness-jev-1-13]] — known failure modes to gate against

## Sources

- raw/docs/patterns__confidence-routing.md (https://docs.typesafe.ai/patterns/confidence-routing)
- raw/docs/patterns.md (https://docs.typesafe.ai/patterns)
- raw/github/skills/skills/typesafe-ai/SKILL.md (https://github.com/typesafe-ai/skills/blob/main/skills/typesafe-ai/SKILL.md)
