> 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.

# TypeSafe-compatible API

`decision-machine-1` serves `POST https://api.milliseconds.ai/v1/systemone`. The request and response follow the wire format of TypeSafe's `/v1/systemone`. Keep your request builder. Change the base URL, the key and the model name.

Set the base URL to `https://api.milliseconds.ai`, `model` to `decision-machine-1`, and the bearer token to your test or production key. See [Authentication](/authentication).

> **Note**
>
> This route is a compatibility layer over three native capabilities. `noul` runs [yes-no](/capabilities/yes-no), `choice` runs [classify](/capabilities/classify) and `score` runs [rate](/capabilities/rate). Every question runs on the same `state`, in parallel.
>
> milliseconds.ai is not affiliated with TypeSafe AI. The route implements a public wire format.

## The first call

One `state`, three named questions, one call.

#### curl

```bash
curl https://api.milliseconds.ai/v1/systemone \
  -H "content-type: application/json" \
  -H "authorization: Bearer test_sk-..." \
  -d '{
    "state": "Shoes arrived two weeks late and in the wrong size. Also I see two charges on my card. What are you going to do about this?",
    "model": "decision-machine-1",
    "questions": {
      "department": {
        "type": "choice",
        "instructions": "Which team should handle this?",
        "criteria": {
          "returns": "Exchanges, refunds, wrong or damaged items",
          "shipping": "Delivery status, delays, lost packages",
          "billing": "Charges, invoices, payment problems"
        }
      },
      "double_charge": {
        "type": "noul",
        "instructions": "Does the customer report a duplicate charge?",
        "criteria": {
          "true": "The customer says they were charged more than once",
          "false": "No duplicate charge is mentioned"
        }
      },
      "urgency": {
        "type": "score",
        "instructions": "How urgent is this ticket?",
        "criteria": ["can wait", "this week", "today"]
      }
    }
  }'
```

#### Python

```python
import requests

response = requests.post(
    "https://api.milliseconds.ai/v1/systemone",
    headers={"authorization": "Bearer test_sk-..."},
    json={
        "state": "Shoes arrived two weeks late and in the wrong size. Also I see two charges on my card. What are you going to do about this?",
        "model": "decision-machine-1",
        "questions": {
            "department": {
                "type": "choice",
                "instructions": "Which team should handle this?",
                "criteria": {
                    "returns": "Exchanges, refunds, wrong or damaged items",
                    "shipping": "Delivery status, delays, lost packages",
                    "billing": "Charges, invoices, payment problems",
                },
            },
            "double_charge": {
                "type": "noul",
                "instructions": "Does the customer report a duplicate charge?",
                "criteria": {
                    "true": "The customer says they were charged more than once",
                    "false": "No duplicate charge is mentioned",
                },
            },
            "urgency": {
                "type": "score",
                "instructions": "How urgent is this ticket?",
                "criteria": ["can wait", "this week", "today"],
            },
        },
    },
    timeout=30,
)
answers = response.json()["answers"]
print(answers["department"]["choice"], answers["double_charge"]["noul"], answers["urgency"]["score"])
```

#### TypeScript

```typescript
const response = await fetch("https://api.milliseconds.ai/v1/systemone", {
  method: "POST",
  headers: { "content-type": "application/json", authorization: "Bearer test_sk-..." },
  body: JSON.stringify({
    state:
      "Shoes arrived two weeks late and in the wrong size. Also I see two charges on my card. What are you going to do about this?",
    model: "decision-machine-1",
    questions: {
      department: {
        type: "choice",
        instructions: "Which team should handle this?",
        criteria: {
          returns: "Exchanges, refunds, wrong or damaged items",
          shipping: "Delivery status, delays, lost packages",
          billing: "Charges, invoices, payment problems",
        },
      },
      double_charge: {
        type: "noul",
        instructions: "Does the customer report a duplicate charge?",
        criteria: {
          true: "The customer says they were charged more than once",
          false: "No duplicate charge is mentioned",
        },
      },
      urgency: {
        type: "score",
        instructions: "How urgent is this ticket?",
        criteria: ["can wait", "this week", "today"],
      },
    },
  }),
});
const { answers } = await response.json();
console.log(answers.department.choice, answers.double_charge.noul, answers.urgency.score);
```

The response carries one answer per question, keyed by your question names. This is a real capture.

```json
{
  "model": "decision-machine-1",
  "answers": {
    "department": {
      "type": "choice",
      "choice": "billing",
      "probabilities": {"returns": 0.242, "shipping": 0.133, "billing": 0.625},
      "confidence": 0.438
    },
    "double_charge": {"type": "noul", "noul": 0.592},
    "urgency": {
      "type": "score",
      "score": 0.684,
      "legend": {"0": "can wait", "1": "this week", "2": "today"},
      "probabilities": {"0": 0.602, "1": 0.112, "2": 0.286},
      "confidence": 0.658
    }
  },
  "usage": {"input_tokens": 394, "output_tokens": 0}
}
```

## The request

| Field       | Type                    | Meaning                                                                                                                                                     |
| ----------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state`     | string, object or array | The content to evaluate. The API renders an object or array to text: keys become labels, array items become lines. At most 20,000 characters once rendered. |
| `model`     | string, optional        | `decision-machine-1`. Omit it for the same result. Any other value returns `404 model_not_found`.                                                           |
| `questions` | map of name to question | 1 to 32 named questions. Every question runs on the same `state`.                                                                                           |

Every question carries `type` and `instructions`. `instructions` accepts a string, object or array and renders the same way as `state`.

| `type`   | `criteria`                                                   | Runs as                                                          | Answer fields                                                    |
| -------- | ------------------------------------------------------------ | ---------------------------------------------------------------- | ---------------------------------------------------------------- |
| `noul`   | Optional `{ "true": ..., "false": ... }` descriptions        | [yes-no](/capabilities/yes-no) with `when_true` and `when_false` | `noul`, the probability of yes                                   |
| `choice` | Map of 2 to 64 option names to descriptions, `null` for none | [classify](/capabilities/classify)                               | `choice`, `probabilities` per option, `confidence`               |
| `score`  | Ordered array of 2 to 10 level descriptions, low to high     | [rate](/capabilities/rate)                                       | `score`, `legend`, `probabilities` per level index, `confidence` |

## The answers

* `noul` is the model's probability that the statement holds. It is the `probability` field of yes-no.
* `choice` names the option with the highest probability. `probabilities` is the full distribution over your option names. It sums to 1.
* `score` is the probability-weighted level, from `0` to the number of levels minus one. `legend` maps each level index to the criterion you sent. `probabilities` is keyed by level index as a string.
* `confidence` describes the shape of the distribution, not the probability. For `choice` it is how far the winner sits above a flat distribution: `(K × max − 1) / (K − 1)` for `K` options. For `score` it is one minus the mean distance from the most likely level, scaled to 0 to 1. `noul` answers carry no confidence.

`usage.input_tokens` is the input billed for the call. `usage.output_tokens` is always `0`. The model generates nothing.

## Differences from TypeSafe

* `model` must be `decision-machine-1` or absent. The response echoes that name.
* Validation errors return `400` with the `{ "error": { "code", "message" } }` envelope, not `422`. See [Errors](/reference/errors).
* `choice` accepts 2 to 64 options. `score` accepts 2 to 10 levels. One call carries at most 32 questions.
* This route takes no `image`. Use the [native capabilities](/concepts/images) for images.
* The response carries the `x-input-chars`, `x-input-tokens` and `x-inference-ms` headers, like every capability. See [Limits and rate limits](/reference/limits).

## Billing and rate limits

The call bills its input tokens once, at the standard price. Each question counts as one inference request unit against the requests-per-minute limit, like each statement on yes-no. Three questions spend three units. See [Pricing](/reference/pricing) and [Limits and rate limits](/reference/limits).

## Benchmarks

The route serves the request shape that the open [JevBench](https://github.com/fstandhartinger/jevbench) harness sends through its `typesafe` adapter. Point the adapter at `https://api.milliseconds.ai` with `--model decision-machine-1` and your key. It needs no changes.

## Next

* [Yes / no](/capabilities/yes-no), [Classify](/capabilities/classify) and [Rate](/capabilities/rate) — the native routes behind each question type, with batching over up to 32 texts.
* [Thresholds and confidence routing](/concepts/thresholds) — how to act on `probabilities` and `confidence`.