# Overview

> The official CLI for the Qualia robotics data and VLA platform

The official command-line interface for the [Qualia](https://junction.qualiastudios.dev) robotics data and VLA platform. Built on the [Python SDK](https://junction-docs.qualiastudios.dev/sdk/overview.md).

## Installation

```bash
pip install 'qualia-cli==0.3.0rc14'

# or with uv
uv tool install 'qualia-cli==0.3.0rc14' --with 'qualia-sdk==0.8.0rc14'
```

This deployment runs ahead of the public release, so it needs the matching pre-release wheel — `0.3.0rc14`. Pin it rather than upgrading: the upload protocol is negotiated exactly, so the client and the endpoint it talks to have to be the same generation. A plain `pip install qualia-cli` resolves to the current public release and will be turned away by this deployment.

## Authentication

:::caution[Point the client at this environment]
The SDK and CLI talk to `https://api.qualiastudios.dev` unless told otherwise. Your account lives on `https://junction-api.qualiastudios.dev`, so set:

```bash
export QUALIA_BASE_URL=https://junction-api.qualiastudios.dev
```
:::

You need an API key and the API origin of the environment your account is on.
The key names its own account, so no command needs your account id.

### 1. Create an API key

In the [Qualia dashboard](https://junction.qualiastudios.dev), open **Settings**,
then **Account** › **API keys**. Click **New key**, then **Create key**, and
copy the key from the **API key created** dialog. The name starts as the day
(`Key 4 Nov 2026`); type over it to name the machine or pipeline instead. The
key is shown once. If you lose it, revoke it and make a new one. The page lists
your keys in every organization you belong to.

A key acts as you, in the whole organization (every workspace you can reach)
unless you open **More options** and pick one workspace. It carries every scope except the admin scopes
below, including the `data:read` and `data:write` that uploads and curation
need. It acts with your role in each workspace it reaches, so where you are a
Viewer it can only read. Keys expire after 90 days by default.

#### Admin scopes

Three scopes let a key do admin work. None is on by default: open **More
options** and tick it under **Admin (opt-in)** when you make the key. A key with `*` does not get them,
and no wildcard such as `members:*` exists; a key holds one only by its exact
name. Each still acts within your role, so a Contributor's key with
`members:write` cannot change roles. A guest's key cannot use them.

| Scope                | Lets the key                                                  |
| -------------------- | ------------------------------------------------------------- |
| `members:read`       | See people, roles and invitations                             |
| `members:write`      | Invite people, change their roles, remove them                |
| `integrations:write` | List, add and remove integration keys; never reads a key back |

`members:read` is for `qualia member list` and `show` and
`qualia invitation list`. `members:write` is for `qualia member role` and
`remove`, and `qualia invitation send` and `withdraw`.

No key can make another key.

### 2. Log in

```bash
qualia auth login --base-url https://junction-api.qualiastudios.dev
```

The CLI prompts for the key and hides what you type. You can also pass it
with `--api-key <YOUR_KEY>`. `--base-url` is the API
origin of your environment, with no path. Production is
`https://api.qualiastudios.dev`, which is also the default when you leave the
flag out. Dev is `https://dev-api.qualiastudios.dev`.

`qualia auth login` writes the key, the base URL and the account to a config
file that only you can read:

- **Linux**: `~/.config/qualia/config.toml`
- **macOS**: `~/Library/Application Support/qualia/config.toml`
- **Windows**: `%APPDATA%\qualia\config.toml`

Each login replaces the key, base URL and account in that file: a login
without `--account` clears a stored account. Check what is stored with:

```bash
qualia auth status
```

```text
API key: configured
Base URL: https://junction-api.qualiastudios.dev
Account: the API key's own
Scope: Whole organization
```

It never prints the key. `Account: the API key's own` means no account is
stored, which is the normal case. `Scope`
is the key's reach, asked of the server: `Whole organization` (every workspace
you can reach), `Workspace <name>` (one workspace only) or `Restricted` (a
custom list). When the server cannot be reached it prints `Scope: unknown` and
the reason.

### 3. Your account

A new user has a personal account: an organization of one, with one
workspace. Your datasets, tasks, robots and checks live under that account, at
`accounts/{account}/…`. An organization you create or join later is a
different account, with its own id.

An API key belongs to exactly one account, so no command asks for it. The CLI
and SDK name it `accounts/-` in every call, and the API reads `-` as the key's
own account. Nothing is looked up first, and nothing is stored.

Workspace lists (`qualia status list`, `qualia activity list`) need no
`--workspace` either: they ask the server once what the key reaches
(`GET /v1/whoami`). An organization-wide key lists the whole organization; a
workspace key lists its own workspace.

`--account`, `QUALIA_ACCOUNT` and `qualia auth login --account` still exist as
an explicit override. A key acts only in its own account, so they are never
needed with a key.

Where a command takes an id, a full name such as
`accounts/<ACCOUNT_UUID>/datasets/<DATASET_UUID>` also works: it carries its
account.

**Finding your account id.** Ask the server who your key is (over the API,
`GET /v1/whoami` answers the same, for any valid key):

```bash
qualia auth whoami
```

```text
Account: Acme Robotics (0b6c6f3e-1f43-4c55-9a3e-1d0c3f5a2b7e)
Workspace: Arms (5b1e0f6a-7c2d-4e8f-9a1b-2c3d4e5f6a7b)
Key: laptop (qla_1a2b3c4d…), scope: Whole organization
Expires: 2027-01-01T00:00:00+00:00
```

The workspace is the one the key acts in: the key's own, or the one
`--workspace` names. `--json` prints the server's whole answer. A revoked or
expired key exits 1 with the server's message. The id is a lowercase UUID; the
CLI refuses any other form.

Without a key at hand: in an empty workspace, the dashboard's getting
started steps fill it into the login command. The dashboard also keeps the
account you work in as the `q-account` cookie. While signed in, open
your browser's developer tools, go to **Application** (Chrome, Edge) or
**Storage** (Firefox, Safari), then **Cookies**, and copy the value of
`q-account`.

### Scripts and CI

Pass the key on the command line, or set it in the environment:

```bash
qualia --token <API_KEY> credit
export QUALIA_API_KEY="your-api-key"
export QUALIA_BASE_URL="https://junction-api.qualiastudios.dev"
```

When `QUALIA_API_KEY` is set, the CLI ignores the config file, including its
base URL and account. Set `QUALIA_BASE_URL` next to it, or the CLI talks to
production. The account is the key's own.

## Quick Start

```bash
# Log in and check what is stored
qualia auth login --base-url https://junction-api.qualiastudios.dev
qualia auth status

# Save a version (tag 1.0); it prints the DatasetVersion id.
qualia dataset pin <DATASET_UUID> 1.0

# Use the dataset slug (or UUID) and tag; the command prints the resolved id
# Inspect the exact camera descriptors training will use
qualia dataset training-features my-dataset:1.0

# Download that version (--format lerobot_v3 | lerobot_v2_1 | mcap_ros2), or with
# --format original (no version) every file uploaded to the dataset, byte for byte
qualia dataset download my-dataset:1.0 ./my-dataset-1.0 --format lerobot_v3
```

## Output Formats

All commands that display data support a `--json` flag for machine-readable output:

```bash
qualia credit --json
qualia dataset training-features my-dataset:1.0 --json
```

Without `--json`, output is displayed as formatted tables using [Rich](https://github.com/Textualize/rich).

## Configuration

| Flag          | Env var            | Config file key | What it sets                                             |
| ------------- | ------------------ | --------------- | -------------------------------------------------------- |
| `--token`     | `QUALIA_API_KEY`   | `api_key`       | API key                                                  |
| `--base-url`  | `QUALIA_BASE_URL`  | `base_url`      | API origin (default `https://api.qualiastudios.dev`)     |
| `--account`   | `QUALIA_ACCOUNT`   | `account`       | Optional account UUID; default the API key's own         |
| `--workspace` | `QUALIA_WORKSPACE` | —               | Workspace UUID to act in, instead of the key's workspace |

These are global options: they go before the command, as in
`qualia --workspace <WORKSPACE_UUID> status list`. `qualia auth login` writes the
config file keys. The workspace is never stored.

Priority: flag > environment variable > config file. The config file's
`base_url` and `account` are read only together with its `api_key`: once
`QUALIA_API_KEY` is set, nothing comes from the file.

## Requirements

- Python 3.10+
- qualia-sdk 0.8.0rc14 (installed automatically)

## Next Steps

- [Commands](https://junction-docs.qualiastudios.dev/cli/commands.md) — Full command reference
- [SDK Documentation](https://junction-docs.qualiastudios.dev/sdk/overview.md) — Use the Python SDK directly for programmatic access
