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

# dm1 CLI

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

The package ships the library and the `dm1` binary together. Node 20 or later.

`dm1` reads the key from `MS_API_KEY`. Pass `--key` to override it for one call. Without a key the tool exits 2 and prints the shell answer. Get a key in the [console](https://console.milliseconds.ai/signup).

## Help

**`dm1 --help`**

```text title="dm1 --help"
dm1 — typed decisions over text, from milliseconds.ai

USAGE
  dm1 <capability> [text] [args...] [options]
  <command> | dm1 <capability> [args...]

CAPABILITIES
  classify       <text> <label[=description]>...   Pick one label
  yes-no         <text> <statement>...             True or false, per statement
  rate           <text> <level>...                 Place the text on a low-to-high scale
  answer         <text> <question>...              Quote the answer out of the text
  entities       <text> <type[=description]>...    Find every mention, with offsets
  extract        <text> --schema <file|json>       Fill a JSON Schema
  verify         <text> --field <name> --value <v> Check a value against the text
  classify-tree  <text> --tree <file|json>         Walk a nested label tree
  check                                            Test the key. Print the limits.

TEXT
  [text]              The text itself. Omit it, or pass -, to read stdin.
  -f, --file <path>   Read the text from a file. Repeat it for a batch, in order.
  --lines <path>      One text per line. Sent in chunks of 32, in order.
  Piped input that starts with { becomes the whole request body. Flags still win.

SPECIFICATION
  A positional after the text is a label, statement, level, question or type.
  name=description splits on the first =. A bare name has no description.
  Any list also loads from a file: @labels.json holds an array or a name-to-description object.
  --tree @taxonomy.json   --schema @invoice.json   @- reads stdin.
  --when-true <s> --when-false <s>     yes-no hints
  --field <name[=description]> --value <v>   verify

OPTIONS
  --json              Print JSON, even on a terminal.
  --jsonl             One compact JSON result per line. For pipes.
  --raw               Print the result with its API envelope.
  -q, --quiet         Print the primary value only.
  --check             yes-no and verify only. Exit 3 when the answer is no.
  --min <p>           Exit 3 when probability is below <p>. Not rate or extract.
  --min-confidence <c>  Exit 3 when confidence is below <c>. classify, rate and
                        classify-tree only.
  --key <key>         API key. Default: $MS_API_KEY.
  --base-url <url>    Default: https://api.milliseconds.ai
  --retries <n>       Retries on 429, 502, 529 and network errors. Default 2.
  --timeout <ms>      Per attempt. Default 60000.
  -v, --usage         Print tokens, model time and rate limits to stderr.
  --no-color          No ANSI. NO_COLOR and a non-TTY stdout do the same.
  -h, --help          This text. After a capability, that capability's help.
  -V, --version

DESCRIBE YOUR LABELS
  The label text is the instruction. The model reads it literally.
    dm1 classify "$T" billing shipping                     works
    dm1 classify "$T" billing="charges and refunds" shipping="delivery and tracking"
  The second call scores measurably better. Use name=description everywhere.

EXIT CODES
  0  decided          2  bad usage
  1  API error        3  --check or --min failed

EXAMPLES
  dm1 classify "I was charged twice" billing shipping account
  dm1 yes-no "Ship it today" "The customer expresses urgency." --check
  dm1 rate "This is unacceptable" Calm Annoyed Angry "Threatening to leave"
  dm1 entities "Ada met Grace in Paris" person place --json | jq -r '.[].text'
  dm1 extract -f invoice.txt --schema @invoice.json
  dm1 classify --lines tickets.txt billing shipping account --jsonl > labelled.ndjson
  pbpaste | dm1 classify @labels.json

  No key yet?  https://console.milliseconds.ai  then  export MS_API_KEY=test_sk-...
```

`dm1 <capability> --help` prints that capability's own help, with its arguments, its result fields and its examples.

## Worked examples

Each capability's own help carries its examples. These are they.

**`classify`**

```bash title="classify"
dm1 classify "I was charged twice." billing="charges and refunds" shipping="delivery"
dm1 classify -f ticket.txt @labels.json --json
dm1 classify --lines tickets.txt @labels.json --jsonl | jq -r '.label'
cat ticket.txt | dm1 classify @labels.json --min 0.9 -q
```

**`yes-no`**

```bash title="yes-no"
dm1 yes-no "Fix this today." "The customer expresses urgency."
dm1 yes-no -f reply.txt "The reply promises a refund." --check --min 0.9 -q
dm1 yes-no -f a.txt -f b.txt "The text mentions a price." --jsonl
```

**`rate`**

```bash title="rate"
dm1 rate "I am done with this company." Calm Annoyed Angry "Threatening to leave"
dm1 rate -f email.txt @scale.json --usage
```

**`answer`**

```bash title="answer"
dm1 answer -f press.txt "Who announced the product?" "How much does it cost?"
curl -s https://example.com/press.txt | dm1 answer - "Who announced the product?"
```

**`entities`**

```bash title="entities"
dm1 entities "Ada met Grace in Paris." person="a human name" place="a city or country"
pbpaste | dm1 entities @types.json --json | jq -r '.[] | select(.type=="person") | .text'
```

**`extract`**

```bash title="extract"
dm1 extract -f invoice.txt --schema @invoice.json --json > invoice.json
dm1 extract -f a.txt -f b.txt --schema @invoice.json --jsonl
```

**`verify`**

```bash title="verify"
dm1 verify -f invoice.txt --field invoice_number="the identifier printed on the invoice" \
  --value 4471 --check
dm1 verify -f invoice.txt --field total --value 999 --json | jq .found
```

**`classify-tree`**

```bash title="classify-tree"
dm1 classify-tree -f ticket.txt --tree @taxonomy.json --json
dm1 classify-tree "I want my money back." --tree @taxonomy.json -q
```

> **Tip**
>
> Describe your labels. The label text is the instruction, and the model reads it literally. `dm1 classify "$T" billing shipping` works. `dm1 classify "$T" billing="charges and refunds" shipping="delivery and tracking"` scores measurably better. Use `name=description` everywhere. On a terminal, `dm1 classify` says so when every label arrives bare.

Any list also loads from a file. `@labels.json` holds an array or a name-to-description object. `--tree @taxonomy.json` and `--schema @invoice.json` take a file, an `@file`, inline JSON, or `@-` for stdin.

## Where the text comes from

One source only, in this order:

| Source              | What it does                                                          |
| ------------------- | --------------------------------------------------------------------- |
| `--lines <path>`    | One text per line. A batch, in order.                                 |
| `-f, --file <path>` | The file's text. Repeat the flag for a batch, in order.               |
| a pipe              | All of stdin. Every positional then belongs to the capability's list. |
| `[text]`            | The first positional, when nothing is piped in.                       |

A leading `-` reads stdin explicitly. A trailing newline is a file ending, so `dm1` drops it. It would otherwise bill and print.

When a pipe carries the text and the first positional could have carried it too, `dm1` writes one line to stderr and names the argument that lost. Pass `-` to silence it.

## Piped JSON becomes the request body

Piped input that starts with `{` becomes the whole request body. Copy a curl body out of these docs, pipe it in, and add flags:

```bash
pbpaste | dm1 classify --json
echo '{"text":"I was charged twice","labels":{"billing":"charges and refunds","shipping":"delivery"}}' \
  | dm1 classify
```

Flags still win over the body's own keys, so `dm1` replaces a single key rather than the request. A body with no `text`, no `texts` and no `image` answers `400` at the API. `dm1` refuses that body before it sends it.

## Batches and chunking

`--lines` and repeated `--file` send the texts in chunks of 32, the API's batch limit, in order. This is the one place a client chunks for you, and it says so.

```bash
dm1 classify --lines tickets.txt @labels.json --jsonl > labelled.ndjson
```

A streaming mode flushes each chunk as it lands, so a later failure still leaves usable output. When a chunk fails, `dm1` names the chunk and the text range on stderr, and exits 1. The chunks before it were billed, so `--usage` still prints their totals.

## Output modes

| Flag                | Output                                                                      |
| ------------------- | --------------------------------------------------------------------------- |
| none, on a terminal | An aligned table. Colour follows the terminal, `--no-color` and `NO_COLOR`. |
| none, into a pipe   | Compact JSON, the same as `--json`.                                         |
| `--json`            | JSON, even on a terminal. Indented on a terminal, compact in a pipe.        |
| `--jsonl`           | One compact JSON result per line, with the input text on each line.         |
| `--raw`             | The result with its API envelope, as the wire sends it.                     |
| `-q, --quiet`       | The primary value only: the label, `yes` or `no`, the quoted answer.        |

`--jsonl` writes the input `text` key after the result's own fields, so an extracted field named `text` never takes the join key. A capability that returns a list keeps its envelope on the line: `{"text": ..., "results": [...]}`.

stdout holds results only. `-v, --usage` prints the tokens, the model time and the rate limits to stderr:

```bash
dm1 classify -f ticket.txt @labels.json --usage > out.json
# usage: <chars> chars, <tokens> tokens, <ms> ms inference, <remaining>/<limit> requests left
```

The `requests left` value counts inference request units, not HTTP calls. A batch with 24 texts consumes 24 units; text and statement or question counts multiply.

See [Limits and rate limits](/reference/limits) for the limits by key type and production tier.

## Exit codes and gates

| Code | Meaning                     |
| ---- | --------------------------- |
| 0    | decided                     |
| 1    | API error                   |
| 2    | bad usage                   |
| 3    | `--check` or `--min` failed |

Three flags turn a decision into an exit code. The result still prints.

* `--check` works with `yes-no` and `verify` only. It exits 3 when the answer is no. In a batch, any no fails the run. A gate that verified nothing has not passed, so an empty result also exits 3.
* `--min <p>` exits 3 when `probability` falls below `p`. `rate` and `extract` carry no probability, so they refuse the flag.
* `--min-confidence <c>` exits 3 when `confidence` falls below `c`. `classify`, `rate` and `classify-tree` carry one.

```bash
if dm1 yes-no -f reply.txt "The reply promises a refund." --check --min 0.9 -q; then
  echo "promise found"
fi
```

## dm1 check

**`dm1 check --help`**

```text title="dm1 check --help"
  dm1 check [options]

  Tests the key and prints the limits. It sends one small classify, about 20 tokens.
  Do not run it in a health-check loop.

  EXAMPLES
    dm1 check
    dm1 check --base-url https://api.milliseconds.ai --key test_sk-...
```

`dm1 check` proves the key, the endpoint and the limits in one small classify of about 20 tokens. It prints a table on a terminal and JSON in a pipe.

```bash
dm1 check
dm1 check --json | jq .rate_limit
```

Do not run it in a health-check loop. It bills like any other call. See [Pricing](/reference/pricing).

## Options

| Flag               | Default                                         |
| ------------------ | ----------------------------------------------- |
| `--key <key>`      | `$MS_API_KEY`                                   |
| `--base-url <url>` | `https://api.milliseconds.ai`                   |
| `--retries <n>`    | 2. Retries on 429, 502, 529 and network errors. |
| `--timeout <ms>`   | 60000, per attempt.                             |
| `--no-color`       | `NO_COLOR` and a non-TTY stdout do the same.    |
| `-V, --version`    | Prints the version.                             |