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

# Quickstart

Every model runs at `https://api.milliseconds.ai`. This page makes one call to [decision-machine-1](/decision-machine-1), then one call to [rakeaudio](/rakeaudio/overview). Both calls use the same key.

> **Note**
>
> **Every request needs an API key.** Sign up in the [console](https://console.milliseconds.ai/signup), then copy the automatically created **Playground** test key from **API keys**.
>
> Send it as `Authorization: Bearer test_sk-...` on every `/v1` route. See [Authentication](/authentication). Test keys include 125 million free input tokens each month. Production keys use purchased credits at \$0.04 per million input tokens and \$0 per output token. See [Pricing](/reference/pricing).

To try a live decision without copying a key, open the [console playground](/playground#console-playground). It uses your workspace's test key automatically.

## Make your first decision

#### Pick a capability

Start with [Classify](/capabilities/classify). It takes text and labels, and returns one label with a probability.

#### Send the request

Copy this call into a terminal. It classifies a support message into three labels.

**`curl`**

```bash title="curl"
curl -X POST 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 or refunds",
      "shipping": "delivery, tracking or returns",
      "account": "login, password or profile settings"
    }
  }'
```

#### Read the decision

The call returns in about a second:

**`Response`**

```json title="Response"
{
  "label": "billing",
  "probability": 0.999,
  "confidence": 0.995,
  "scores": { "billing": 0.999, "shipping": 0, "account": 0.001 }
}
```

No parsing, no prompt, no retry loop. The shape is fixed.

## What each field means

| Field         | Meaning                                                                          |
| ------------- | -------------------------------------------------------------------------------- |
| `label`       | The winning label name. Never the description.                                   |
| `probability` | The winner's normalized share of the scores.                                     |
| `confidence`  | `1 − normalized entropy`. 1 means one clear winner, 0 means a flat distribution. |
| `scores`      | One normalized probability per label name. The values sum to 1.                  |

Three headers come back on every capability response: `x-input-chars` (79 here), the number of input characters, `x-input-tokens` (20 here), the input tokens billed for this call, and `x-inference-ms`, the model processing time for this request.

> **Tip**
>
> Label descriptions improve accuracy. The model reads the label text, so `"billing": "payments, invoices, charges or refunds"` scores better than a bare `billing`. Read more in [writing good statements and labels](/concepts/writing-good-questions).

## The same call from your code

Three clients speak `decision-machine-1`. They turn your label names into your result types. See [SDKs](/sdks/overview).

#### TypeScript

```bash
npm i @cloudraker/milliseconds
export MS_API_KEY=test_sk-...
```

```typescript
import { DecisionMachine } from '@cloudraker/milliseconds'

const dm = new DecisionMachine() // reads MS_API_KEY

const r = await dm.classify(
  'I was charged twice for my subscription this month and support has not replied.',
  {
    billing: 'payments, invoices, charges or refunds',
    shipping: 'delivery, tracking or returns',
    account: 'login, password or profile settings',
  },
)

console.log(r.label, r.probability) // billing 0.989
// r.label: 'billing' | 'shipping' | 'account'. A typo is a compile error.

if (r.confidence < 0.5) {
  console.log('Low confidence. Send this ticket to a person.')
}
```

#### Python

```bash
pip install cloudraker-milliseconds
export MS_API_KEY=test_sk-...
```

```python
from milliseconds import DecisionMachine

dm = DecisionMachine()  # reads MS_API_KEY

r = dm.classify(
    "I was charged twice for my subscription this month and support has not replied.",
    {
        "billing": "payments, invoices, charges or refunds",
        "shipping": "delivery, tracking or returns",
        "account": "login, password or profile settings",
    },
)

print(r.label, r.probability)  # billing 0.989

if r.confidence < 0.5:
    print("Low confidence. Send this ticket to a person.")
```

#### dm1

```bash
npm i -g @cloudraker/milliseconds
export MS_API_KEY=test_sk-...

dm1 classify "I was charged twice for my subscription this month and support has not replied." \
  billing="payments, invoices, charges or refunds" \
  shipping="delivery, tracking or returns" \
  account="login, password or profile settings"
```

`dm1` prints a table on a terminal, and JSON into a pipe. Add `--json` to force JSON.

### Without the SDK

Any HTTP client works. The body and the response are the ones above.

**`Python`**

```python title="Python"
import requests

response = 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 or refunds",
            "shipping": "delivery, tracking or returns",
            "account": "login, password or profile settings",
        },
    },
    timeout=30,
)
response.raise_for_status()
result = response.json()

print(result["label"], result["probability"])  # billing 0.999

if result["confidence"] < 0.5:
    print("Low confidence. Send this ticket to a person.")
```

**`TypeScript`**

```typescript title="TypeScript"
const response = 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 or refunds",
        shipping: "delivery, tracking or returns",
        account: "login, password or profile settings",
      },
    }),
  },
);

if (!response.ok) throw new Error(`HTTP ${response.status}`);
const result = await response.json();

console.log(result.label, result.probability); // billing 0.999

if (result.confidence < 0.5) {
  console.log("Low confidence. Send this ticket to a person.");
}
```

## Ask several questions at once

Every capability accepts `texts` for a batch of inputs. `yes-no` also accepts `statements`, and `answer` accepts `questions`. Statements share one model call, so they cost almost nothing extra.

**`curl`**

```bash title="curl"
curl -X POST https://api.milliseconds.ai/v1/decision-machine-1/yes-no \
  -H "Content-Type: application/json" \
  -H "authorization: Bearer test_sk-..." \
  -d '{
    "text": "I need this fixed today, my launch is tomorrow.",
    "statements": [
      "The customer expresses urgency.",
      "The customer is asking about shipping."
    ]
  }'
```

**`Response`**

```json title="Response"
{
  "results": [
    { "statement": "The customer expresses urgency.", "answer": true, "probability": 1 },
    { "statement": "The customer is asking about shipping.", "answer": false, "probability": 0 }
  ]
}
```

Results come back in input order. The limits are 32 texts, 32 statements and 32 questions per call. [Batching](/capabilities/batching) covers the ordering rules and the fan-out.

## Transcribe an audio file

[rakeaudio](/rakeaudio/overview) takes the audio file itself as the request body. Send the file bytes, not JSON. Set `Content-Type` to the audio type.

**`curl`**

```bash title="curl"
curl -X POST "https://api.milliseconds.ai/v1/rakeaudio/transcribe?diarize=true" \
  -H "authorization: Bearer test_sk-..." \
  -H "Content-Type: audio/wav" \
  --data-binary @call.wav
```

A 19-second support call returned in 1.0 second. This is the real response, with the word lists cut short:

**`Response`**

```json title="Response"
{
  "language": null,
  "segments": [
    {
      "start": 0,
      "end": 2.4,
      "text": "Hello, thanks for calling support.",
      "words": [
        { "word": "Hello,", "start": 0, "end": 0.72, "speaker": "SPEAKER_00" },
        { "word": "thanks", "start": 0.72, "end": 1.04, "speaker": "SPEAKER_00" }
      ],
      "speaker": "SPEAKER_00"
    },
    {
      "start": 4,
      "end": 10.24,
      "text": "Hi, my invoice for September shows two charges of $49, and I only have one account.",
      "words": [
        { "word": "Hi,", "start": 4, "end": 4.56, "speaker": "SPEAKER_01" },
        { "word": "my", "start": 4.56, "end": 4.8, "speaker": "SPEAKER_01" }
      ],
      "speaker": "SPEAKER_01"
    }
  ],
  "duration": 19.0075,
  "text": "Hello, thanks for calling support. How can I help you today? Hi, my invoice for September shows two charges of $49, and I only have one account. I see the duplicate charge. I will refund one of them now. You should see it in three to five business days. Great. Thank you very much for your help."
}
```

The call billed 15,840 input tokens (`x-input-tokens`): 19.0075 seconds at \$0.12 per audio hour. Leave out `diarize` (it defaults to `false`) for a transcript without speakers, at \$0.10 per audio hour. [Transcribe](/rakeaudio/transcribe) describes every field.

## When a call fails

Every endpoint returns errors in one envelope:

**`Error`**

```json title="Error"
{ "error": { "code": "invalid_request", "message": "labels: Too small: expected array to have >=2 items" } }
```

Two codes need a retry, not a code change. `runner_error` (502) means inference failed twice. `overloaded` (529) means every inference slot stayed busy. Retry both with backoff. [Errors](/reference/errors) lists every code.

## Next

#### [All capabilities](/capabilities/overview)

The eight decisions, their latencies, and which one answers your question.

#### [Playground](/playground)

Try every capability in the browser, with no code at all.

#### [Thresholds](/concepts/thresholds)

Turn a probability into an action: act, confirm, or escalate.

#### [SDKs](/sdks/overview)

TypeScript, Python and the dm1 CLI for decision-machine-1. Your labels become your result types.