# CLI

> cymatics: every API operation from your terminal, plus browser login, a local simulator, scenario control and webhook forwarding. Built for people and agents alike.

## Install

```sh
npm install -g @cymos/cli
```

Requires Node.js 20.19 or later. Check it with `cymatics --version`.

## Log in

`cymatics login` opens the dashboard in your browser. Confirm there and the CLI receives a new key on a one-time local callback. It checks the key with `GET /v1/project` before saving it to `profiles.json`, readable only by you. In CI, pipe an existing key to `--key-stdin` so it never appears in your shell history.

`terminal`

```sh
$ cymatics login               # confirm in the browser
$ cymatics login --live        # a live key instead
$ cymatics login --no-browser  # print the URL instead
$ printf '%s' "$KEY" | cymatics login --key-stdin --profile ci
```

## Profiles and modes

Test mode is the default; `--live` selects live credentials. The key comes from, in order, `CYMO_TEST_API_KEY` or `CYMO_LIVE_API_KEY`, then `CYMO_API_KEY`, then the selected profile. The API decides a key's project and mode, and the CLI rejects responses from the other mode.

`terminal`

```sh
$ cymatics auth status            # verify the key online
$ cymatics auth status --offline  # which key, no request
$ cymatics projects current       # the key's project
$ cymatics profiles list          # never shows the keys
$ cymatics profiles use staging   # the new default
$ cymatics logout --profile staging
```

## Quickstart

`terminal`

```sh
$ cymatics login
$ cymatics vitals get --room room_bedroom --json
$ cymatics vitals stream --room room_bedroom --max-events 3
```

## Commands

Commands are `cymatics <group> <action>`, generated from the API contract, so they always match the [API reference](https://docs.cymatics.tech/api). Parameters are flags in kebab case, such as `--room-id` or its alias `--room`. A single path parameter can be passed positionally. `cymatics api` runs any operation by its ID.

`terminal`

```sh
$ cymatics rooms get --room room_bedroom --json
$ cymatics results list --room room_bedroom --limit 100 --paginate
$ cymatics webhooks create --input webhook.json \
    --idempotency-key "$(uuidgen)"
$ cymatics api get-room --room room_bedroom --dry-run
```

- `cymatics project` — get
- `cymatics capabilities` — list
- `cymatics models` — list, get
- `cymatics sensing-profiles` — list
- `cymatics retention-profiles` — list
- `cymatics coordinate-frames` — list
- `cymatics skeleton-definitions` — list
- `cymatics rooms` — list, create, get, update, archive, zones get, zones replace, calibration get, calibration replace, processing-config get, processing-config replace, stream
- `cymatics devices` — list, get, update, health, sensing-config get, sensing-config replace, observations list, raw-stream
- `cymatics scenes` — list, create, get, update, delete
- `cymatics vitals` — get, history
- `cymatics presence` — get
- `cymatics movement` — get
- `cymatics stress` — get
- `cymatics skeleton` — get
- `cymatics classifications` — get
- `cymatics results` — list
- `cymatics people` — results list, stream
- `cymatics sleep` — sessions list, summary get
- `cymatics activity` — summary get
- `cymatics analyses` — list, get
- `cymatics events` — list
- `cymatics webhooks` — list, create, get, update, delete, test, deliveries list
- `cymatics scenarios` — list, run
- `cymatics scenario-runs` — get, control
- `cymatics raw-data` — exports create, exports get, exports download
- `cymatics usage` — get
- `cymatics request-logs` — list

## Flags and output

Results go to stdout as JSON, the same envelope as the API; streams and `listen` print one NDJSON line per message. A failure prints one `{"error": {"code", "message"}}` object on stderr and nothing on stdout.

- `--test, --live` · default --test — Choose the mode's credentials. Both at once is an error.
- `--profile <name>` — Use a saved profile instead of the default one.
- `--json` — Print the response envelope as JSON on stdout.
- `--input <file|->` — The JSON request body, from a file or stdin. Invalid input fails before any request.
- `--dry-run` — Validate and print the request, with the key redacted, without sending it.
- `--paginate` — Print every page, one envelope each, until the cursor stops advancing.
- `--max-events <n>, --duration-ms <ms>` — Bound a stream or listen; it then exits 0.
- `--agent` — Bound a stream to ten events or ten seconds, whichever comes first.
- `--read-only` — Refuse any command that writes. The API still enforces the key's scopes.
- `--idempotency-key <key>` — Make a write safe to retry: the API replays the first response.
- `--timeout-ms <ms>` — Per-request timeout.
- `--output-file <path>, --max-bytes <n>` — Write binary data to a new file, up to a size limit.
- `--base-url <url>` · default https://api.cymatics.tech — The API origin: HTTPS, or HTTP on loopback.

## Streams and downloads

Stream commands run until Ctrl-C unless you bound them with `--max-events` or `--duration-ms`. Binary data is only ever written to a new `--output-file`.

`terminal`

```sh
$ cymatics rooms stream --room room_bedroom --max-events 5
$ cymatics vitals stream --room room_bedroom --duration-ms 10000
$ cymatics raw-data exports download EXPORT_ID \
    --output-file capture.bin --max-bytes 1048576
```

## Local simulator

`cymatics mock start` runs the real API gateway on 127.0.0.1 with the test simulator, in memory and offline. It has built-in keys: `cym_test_local` with every scope, `cym_test_readonly` with `results:read`, and `cym_test_other` for a second project.

`terminal`

```sh
$ cymatics mock start --port 8787

# in another terminal
$ export CYMO_API_URL=http://127.0.0.1:8787
$ export CYMO_TEST_API_KEY=cym_test_local
$ cymatics rooms get --room room_bedroom --json
```

## Scenarios

Start a preset or custom timeline with `scenarios run`, then pause, resume or advance it with `scenario-runs control`. The run's rooms answer every read and stream. See [Scenarios](https://docs.cymatics.tech/test-and-live#scenarios) for the presets.

`terminal`

```sh
$ echo '{"scenario_id":"bed_exit","seed":42}' | cymatics scenarios run --input -
$ echo '{"action":"pause"}' | cymatics scenario-runs control RUN_ID --input -
$ echo '{"action":"advance","duration_ms":6000}' \
    | cymatics scenario-runs control RUN_ID --input -
$ cymatics events list --room ROOM_ID --json
```

## Forward webhooks

`listen` forwards your project's new events to a local URL (localhost, 127.0.0.1 or \[::1\]) with the production body and headers, signed with a fresh `whsec_` secret it prints first. Verify them with `verifyWebhookSignature` exactly as in production. Deliveries add `Cymo-Forwarded: cli`, and `--events` or `--webhook-id` filter the types.

`terminal`

```sh
$ cymatics listen --forward-to http://localhost:3000/api/cymo-webhook
$ cymatics listen --forward-to http://localhost:3000/hook \
    --events bed.exited,fall.detected
$ cymatics listen --forward-to http://localhost:3000/hook \
    --webhook-id wh_123 --max-events 10
```

## For AI agents

Agents in a terminal can discover everything offline, without a key. `agent guide` returns the recommended workflow and `agent schema` every command's JSON Schema with a `readOnlyHint`. Combine `--dry-run`, `--read-only` and `--agent` to keep an agent's calls safe and bounded. For agents that speak MCP, use the [MCP server](https://docs.cymatics.tech/mcp).

`terminal`

```sh
$ cymatics agent guide                 # workflow, exit codes
$ cymatics agent schema --group rooms  # JSON Schemas
$ cymatics schema get-room             # one operation
$ cymatics rooms stream --room room_bedroom --agent
```

## Exit codes

- `0` — Success, including a stream or listen that ended at its bound or on Ctrl-C.
- `1` — API or response error, such as resource\_not\_found, live\_unavailable or an invalid response.
- `2` — Invalid arguments or input, an unknown command, an invalid origin or configuration file.
- `3` — Credentials or permissions: a missing, invalid, expired or revoked key, 401 or 403, a mode mismatch, or a cancelled or failed login.
- `4` — Network: connection failure, timeout, 503 service\_unavailable or a login timeout.
- `5` — 429: rate\_limit\_exceeded after the SDK's automatic retries (with retry\_after\_ms), or quota\_exceeded.

`quota_exceeded` is never retried: the message says how to raise the key's live-hour cap in the dashboard.

## Environment variables

- `CYMO_TEST_API_KEY, CYMO_LIVE_API_KEY` — The key for that mode. Checked first.
- `CYMO_API_KEY` — A key for whichever mode is selected, checked after the mode's own variable and before saved profiles.
- `CYMO_API_URL` · default https://api.cymatics.tech — The API origin, like --base-url. Point it at cymatics mock start to work offline.
- `CYMO_SITE_URL` · default https://cymatics.tech — The website that browser login opens, like --site-url.
- `CYMO_CONFIG_DIR` · default \~/.config/cymatics — Where profiles.json and config.json live. $XDG\_CONFIG\_HOME is honoured.
- `CYMO_NO_UPDATE_CHECK` · 1 to disable — Skip the daily background check for a newer release. It never runs in CI or when stdout isn't a terminal.
