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

# CLI reference

The `scaiflow` CLI is a thin wrapper over the compiler and ScaiGrid client. Install it from `apps/cli` (each app has its own `.venv`):

```bash
cd apps/cli && pip install -e .
scaiflow --version
```

Or use the per-tenant package distribution channel your operator provides.

All subcommands share these implicit environment variables when relevant:

- `SCAIGRID_BASE_URL` — ScaiGrid base URL (defaults to the public endpoint).
- `SCAIGRID_API_KEY` — `sgk_*` API key for ScaiGrid auth.

## `scaiflow validate FLOW`

Validate a flow JSON file against the v2 JSON Schema.

```bash
scaiflow validate my-flow.flow.json
scaiflow validate my-flow.flow.json --json    # machine-readable
```

Exit code 0 on success, 1 on any error. Errors are printed one per line.

**Options:**

- `--json` — emit `{ok, errors}` JSON to stdout instead of pretty text.

## `scaiflow compile FLOW`

Compile a flow JSON to YAML (default), SCIR binary, or `.scai` text.

```bash
scaiflow compile my-flow.flow.json                    # writes my-flow.yaml
scaiflow compile my-flow.flow.json --format ir        # writes my-flow.scir
scaiflow compile my-flow.flow.json --out manifest.yaml
scaiflow compile my-flow.flow.json --format ir --no-verify
```

**Options:**

- `--format yaml|ir|scai` — output format. Defaults `yaml`.
- `--out PATH` — output file. Defaults to the input filename with the format's extension.
- `--no-verify` — skip the ScaiCore verifier pass (IR format only; YAML doesn't verify).

Exit code 0 on success. Compile errors print to stderr and exit non-zero.

## `scaiflow deploy FLOW`

Compile and deploy to ScaiGrid in one shot.

```bash
scaiflow deploy my-flow.flow.json
scaiflow deploy my-flow.flow.json --publish-as-model
scaiflow deploy my-flow.flow.json --api-key sgk_xxx --base-url https://scaigrid.example
```

The CLI uses the ScaiGrid SDK directly — bypassing ScaiFlow's backend. Useful for CI pipelines, scripted deploys, environments without a ScaiFlow API instance.

**Options:**

- `--publish-as-model / --no-publish-as-model` — toggle the optional post-deploy publish step. Defaults to the flow's `config.publish_as_model` setting.
- `--api-key TEXT` — ScaiGrid API key. Defaults to env `SCAIGRID_API_KEY`.
- `--base-url TEXT` — ScaiGrid base URL. Defaults to env `SCAIGRID_BASE_URL`.

Output: `core_id=core_xxx` (and `published_slug=...` if published).

## `scaiflow publish FLOW`

Package a flow into a `.scaipkg`. Doesn't upload — just writes the file to disk.

```bash
scaiflow publish my-flow.flow.json
scaiflow publish my-flow.flow.json \
  --package-id customer-support \
  --name "Customer Support" \
  --version 0.2.0 \
  --description "Triage + KB + HITL review" \
  --readme README.md \
  --out customer-support-v0.2.0.scaipkg
```

**Options:**

- `--package-id TEXT` — tenant-scoped slug. Defaults to a slug of FLOW's name.
- `--name TEXT` — display name. Defaults to FLOW's name.
- `--version TEXT` — defaults `0.1.0`.
- `--description TEXT` — defaults empty.
- `--readme PATH` — markdown file embedded in the package.
- `--out PATH` — output file. Defaults `<package_id>-v<version>.scaipkg`.

To publish to your tenant's catalog, use the canvas UI or `POST /v1/catalog/publish` — see [Catalog REST API](./rest-api/catalog).

## `scaiflow import MANIFEST`

Import a YAML manifest into a flow JSON.

```bash
scaiflow import my-core.yaml > my-flow.flow.json
scaiflow import my-core.yaml --out my-flow.flow.json
```

For ScaiFlow-emitted YAML, the import is lossless (uses the per-step `meta:` keys). For hand-edited YAML, unrecognized steps land under `metadata.unknown_steps`. Layout is auto-generated (nodes stacked 120 px apart in YAML step order); you'll likely want to rearrange in the canvas.

**Options:**

- `--out PATH` — output file. Defaults to stdout.

## `scaiflow test FLOW`

Run test fixtures defined on the flow.

```bash
# Compile-only — sanity check the fixtures compile
scaiflow test my-flow.flow.json

# Against a deployed Core — actually invoke and check expectations
scaiflow test my-flow.flow.json --core-id core_xxx

# Filter to one test by name
scaiflow test my-flow.flow.json --core-id core_xxx --name "Refund request"

# Override ScaiGrid base
scaiflow test my-flow.flow.json --core-id core_xxx --base-url https://scaigrid.example
```

**Options:**

- `--core-id TEXT` — deployed Core to test against. Without this, the command only sanity-checks that the test fixtures compile.
- `--name TEXT` — run only tests matching this substring.
- `--base-url TEXT` — ScaiGrid base URL override.

Test fixtures are defined under `flow.tests[]`:

```jsonc
{ "tests": [
  { "name": "Refund request",
    "input": { "message": "I want a refund" },
    "expect": { "intent": "complaint" } }
] }
```

`expect` is a shallow `==` against the response's `output`. For more complex checks, use `expect_expressions[]` (free-form expressions evaluated server-side; surface TBD).

## `scaiflow scaikey-register`

Bootstrap a ScaiKey application + dev API key for a fresh tenant. Used once per tenant during initial setup.

```bash
scaiflow scaikey-register --tenant acme --base-url https://scaikey.scailabs.ai \
  --admin-email admin@acme.example --admin-password '...'
```

Creates the ScaiFlow application registration, requests an API key with the right scopes, prints them to stdout. Subsequent operations use the printed key.

See `scaiflow scaikey-register --help` for the full flag set; this is more an ops step than a daily-use command.
