---
title: CLI reference
path: reference/cli
status: published
---

# CLI reference

The `scaicontrol` CLI is installed alongside the backend Python package (`pip install -e .` inside `backend/` exposes it as a console script). It wraps three command groups: `setup`, `sync`, and `admin`.

```bash
scaicontrol --help
scaicontrol --version
```

All commands read configuration from the same `.env` / environment variables that the backend reads — see [Configuration](./configuration). The CLI uses a **synchronous** DB connection (via `pymysql`), so `DATABASE_URL` is rewritten internally from `mysql+asyncmy://…` to `mysql+pymysql://…`.

---

## `scaicontrol setup`

First-run bootstrap against ScaiKey. Registers ScaiControl as a ScaiKey application, polls until approval, and writes the resulting OAuth credentials back to `.env`.

### `setup register`

Submit a registration request to ScaiKey.

```bash
scaicontrol setup register \
  --scaikey-url https://scaikey.scailabs.ai \
  --url https://scaicontrol.scailabs.ai \
  --email ops@example.com \
  --scope GLOBAL \
  --name ScaiControl
```

| Flag | Required | Notes |
|---|---|---|
| `--scaikey-url`, `-k` | yes | Base URL of the ScaiKey instance |
| `--url`, `-u` | yes | Public base URL of this ScaiControl deployment (used as OAuth redirect_uri) |
| `--email`, `-e` | yes | Contact email for the registration |
| `--scope`, `-s` | no (default `GLOBAL`) | One of `GLOBAL`, `PARTNER`, `TENANT` |
| `--partner`, `-p` | when scope=PARTNER | Partner slug |
| `--tenant`, `-t` | when scope=TENANT | Tenant slug |
| `--name`, `-n` | no (default `ScaiControl`) | Application display name |

Writes a status token to `~/.config/scaicontrol/registration.json` (mode 0600). The token is printed once — save it.

### `setup status`

Poll ScaiKey for the registration's approval state. On `APPROVED`, writes `SCAIKEY_CLIENT_ID`, `SCAIKEY_CLIENT_SECRET`, `SCAIKEY_APP_ID`, and `SCAIKEY_ISSUER` to the project `.env` file.

```bash
scaicontrol setup status
```

States: `PENDING`, `APPROVED`, `REJECTED`. Run repeatedly until approved.

### `setup show`

Display current ScaiKey configuration (secrets masked).

```bash
scaicontrol setup show
```

---

## `scaicontrol sync`

Pull users and groups from ScaiKey into the local database. Used to populate the `users` and `groups` tables for tenant-isolated query auto-filters and role assignments.

```bash
scaicontrol sync                # apply
scaicontrol sync --dry-run      # show changes without applying
scaicontrol sync --verbose      # per-user detail
```

Run this after `setup status` completes, then again whenever ScaiKey's user list changes materially (or schedule via cron). Idempotent — re-runs converge on the ScaiKey-side state.

---

## `scaicontrol admin`

User and role management on the local database. **All admin operations are local-DB-only** — they don't propagate back to ScaiKey. ScaiKey is the source of truth for identity; `admin` here manages ScaiControl-side role bindings on already-synced users.

### `admin list`

List active users in the local DB.

```bash
scaicontrol admin list
scaicontrol admin list --role super_admin
scaicontrol admin list --role tenant_admin
```

`--role` filters by role; valid values: `super_admin`, `partner_admin`, `tenant_admin`.

### `admin grant`

Assign a role to a user.

```bash
scaicontrol admin grant <user_id> --role super_admin
scaicontrol admin grant --email alice@example.com --role partner_admin
```

User can be identified by ID (positional) or `--email`. The role replaces any existing role on the user (single-role-per-user model).

### `admin revoke`

Remove a user's role. Errors if the user doesn't currently hold the named role.

```bash
scaicontrol admin revoke <user_id> --role super_admin
scaicontrol admin revoke --email alice@example.com --role partner_admin
```

### `admin show`

Print a user's full local record (ID, email, name, tenant, partner, role, sync timestamp).

```bash
scaicontrol admin show <user_id>
scaicontrol admin show --email alice@example.com
```

### `admin find`

Search **ScaiKey** (not the local DB) for users by email or name substring. Useful for discovering a user before running `sync`.

```bash
scaicontrol admin find --email alice
scaicontrol admin find --name "Alice"
```

### `admin templates validate`

Compile every saved designer template (invoice + email) through Jinja and report syntax failures. Exits non-zero on any failure — suitable for CI.

```bash
scaicontrol admin templates validate
scaicontrol admin templates validate --name "Default Invoice"
scaicontrol admin templates validate --lang nl
```

| Flag | Notes |
|---|---|
| `--name` | Filter by template name (exact match) |
| `--lang` | Filter by language code (e.g. `nl`, `de`) |

Validates both `html_content` and `subject` fields where present.

---

## Exit codes

| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Validation/runtime error (`ClickException`) — message printed to stderr |
| 2 | Usage error (`UsageError`) — typically a missing required option |

The `admin templates validate` command additionally exits 1 when any template fails to compile, regardless of whether other operations succeeded.

---

## Common workflows

**First-run deploy**:

```bash
scaicontrol setup register -k https://scaikey.scailabs.ai \
                           -u https://scaicontrol.example.com \
                           -e ops@example.com
# operator approves in ScaiKey UI
scaicontrol setup status         # repeat until APPROVED
scaicontrol sync                 # populate local users/groups
scaicontrol admin grant --email founder@example.com --role super_admin
```

**Promote a user to platform admin**:

```bash
scaicontrol sync                 # ensure their record is current
scaicontrol admin grant --email user@example.com --role super_admin
```

**Pre-deploy template smoke check** (in CI):

```bash
scaicontrol admin templates validate || exit 1
```
