> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kelviq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Command reference

> Every Kelviq CLI command, flag by flag.

All commands support `--help`. The CLI targets your **sandbox** environment unless you pass `--prod` — see [Environments & authentication](/cli/environments).

Output is colorized on interactive terminals (green creates, yellow updates, red archives) and automatically plain when piped. Disable colors explicitly with the global `--no-color` flag or the `NO_COLOR` environment variable.

***

## kelviq init

Writes a starter `kelviq.config.ts` to the current directory.

```shell theme={null}
kelviq init
kelviq init -o pricing/kelviq.config.ts
kelviq init --force
```

| Flag                  | Description                                                                                            |
| --------------------- | ------------------------------------------------------------------------------------------------------ |
| `-o, --output <path>` | Where to write the file. Defaults to `kelviq.config.ts` in the current directory.                      |
| `--force`             | Overwrite the file if it already exists. Without it, `init` refuses rather than clobbering your edits. |

The starter file defines one product, one feature, and one plan using the typed builders from `@kelviq/cli/config` — see the [format reference](/cli/kelviq-config).

***

## kelviq login

Stores a Server API key for the CLI to use.

```shell theme={null}
kelviq login          # store a sandbox key (default)
kelviq login --prod   # store a production key
```

| Flag     | Description                                                      |
| -------- | ---------------------------------------------------------------- |
| `--prod` | Store the key for the production environment instead of sandbox. |

What it does:

1. Prints the dashboard's [API keys page](https://app.kelviq.com/settings/api-keys) URL and tries to open it in your default browser — already switched to the environment you're logging into (sandbox by default, production with `--prod`), so the key on screen is the key the CLI expects.
2. Prompts you to paste the key. In an interactive terminal each pasted character shows as `*` — so you can see the paste landed — and a `Received a N-character key.` line confirms the length. The key itself is never echoed or logged.
3. Stores it in `~/.config/kelviq/config.json` with owner-only permissions (`0600`).

<Warning>
  Paste a **Server API Key**, not a Client API Key. Server keys have full API access — treat the config file like any other credential store, and never commit it anywhere.
</Warning>

Kelviq has no key-introspection endpoint yet, so `login` cannot verify the key is valid or show which organization it belongs to — the first `pull` is where a bad key surfaces.

For CI and other non-interactive contexts, skip `login` entirely and set an environment variable instead — see [Environments & authentication](/cli/environments#environment-variables).

***

## kelviq logout

Removes stored API keys.

```shell theme={null}
kelviq logout          # remove the stored sandbox key
kelviq logout --prod   # remove the stored production key
kelviq logout --all    # remove both
```

| Flag     | Description                                   |
| -------- | --------------------------------------------- |
| `--prod` | Remove the production key instead of sandbox. |
| `--all`  | Remove both sandbox and production keys.      |

`logout` only touches the stored config file. Keys supplied via environment variables are unaffected — unset those in your shell or CI configuration.

<Info>
  Removing a key locally does not revoke it. To revoke a key, delete it from the [API keys page](https://app.kelviq.com/settings/api-keys) in the dashboard.
</Info>

***

## kelviq env

Shows which environment and keys the CLI is configured with. Read-only — it never calls the API.

```shell theme={null}
kelviq env
```

```text theme={null}
Environment : sandbox (default — use --prod to target production)
Sandbox URL : https://sandboxapi.kelviq.com/api/v1
Prod URL    : https://api.kelviq.com/api/v1
Sandbox key : set
Prod key    : missing
Org         : unknown — Kelviq has no key-introspection endpoint yet
```

Key rows show `set` or `missing` only — `env` reports what's configured locally and does not validate keys against the API. `kelviq env --json` emits the same report as JSON (never any part of a key's value).

***

## kelviq pull

Fetches your remote catalog and writes it as a `kelviq.config.ts`.

```shell theme={null}
kelviq pull                    # pull the sandbox catalog
kelviq pull --prod             # pull the production catalog
kelviq pull -o pricing.config.ts
kelviq pull --force            # overwrite an existing file
```

| Flag                  | Description                                                                                                     |
| --------------------- | --------------------------------------------------------------------------------------------------------------- |
| `--prod`              | Pull from the production environment instead of sandbox.                                                        |
| `-o, --output <path>` | Where to write the config. Defaults to `kelviq.config.ts` in the current directory.                             |
| `--force`             | Overwrite the output file if it already exists. Without it, `pull` refuses rather than overwriting local edits. |

What it fetches: all products, all features, the **latest version** of each plan, and each plan's prices and entitlements. Every cross-reference in the generated file uses `identifier` slugs, never UUIDs — the file is stable across environments that share identifiers.

On success, `pull` prints the path it wrote and a **warnings list** for anything it couldn't represent in the config format (for example, plan fields like `license`, `links`, `countries`, and `files` are dropped, with one warning each). Warnings are informational — the file it wrote is still valid.

On failure — missing key, network error, unexpected API response — `pull` writes `pull failed: <reason>` to stderr and exits non-zero without touching the output file.

***

## kelviq push

Deploys a `kelviq.config.ts` to a Kelviq environment: fetches the current remote catalog, computes exactly what would change, shows you the diff, and applies it only after confirmation.

```shell theme={null}
kelviq push --dry-run          # preview only — never writes anything
kelviq push                    # diff, confirm, apply to sandbox
kelviq push --prod             # same, against production
kelviq push --publish          # also publish the plans this run changed
kelviq push pricing.config.ts  # explicit config path
```

| Flag                 | Description                                                                                        |
| -------------------- | -------------------------------------------------------------------------------------------------- |
| `[config]`           | Path to the config file. Defaults to `kelviq.config.ts`.                                           |
| `--prod`             | Target production instead of sandbox.                                                              |
| `--dry-run`          | Print the diff and exit. Guaranteed zero writes.                                                   |
| `--yes`              | Skip the confirmation prompts (required in non-interactive sessions, e.g. CI).                     |
| `--publish`          | Publish the plans this run created, updated, or price-replaced — with its own confirmation.        |
| `--migrate-features` | With `--publish`: migrate existing customers to the published plan's entitlements.                 |
| `--migrate-pricing`  | With `--publish`: migrate existing customers to the published plan's pricing.                      |
| `--prune`            | Archive remote-only plans/features that the CLI created (see below).                               |
| `--allow-prod-prune` | Required in place of the interactive typed confirmation to `--prune` production non-interactively. |
| `--verbose`          | Expand aggregated warnings (e.g. list every stripped server-owned price field individually).       |
| `--json`             | With `--dry-run` only: emit the machine-readable plan-of-record as JSON on stdout.                 |

The diff shows **what actually changes**, value to value — `~ update plan "pro": name "Pro" → "Pro Plan"` — so the confirmation gate is reviewable at a glance.

### Scripting with `--json`

`kelviq push --dry-run --json` prints exactly one JSON document: `{environment, direction, changes, summary, operations, prune, publish, featureDrift, errors, warnings}`. The `changes` boolean makes drift checks a one-liner in CI:

```shell theme={null}
kelviq push --dry-run --json | jq -e '.changes | not' || echo "catalog has drifted"
```

`--json` is valid only with `--dry-run` (JSON mode never mixes with prompts or writes), always exits 0 on a successful render, and emits `{"error": …}` documents on failures. The shape is a documented, versioned contract — see the repo README.

### What push writes — and in what order

Push converges the environment toward your config in dependency order: products → features → plans → entitlements → prices → publish → prune. Cross-references you wrote as identifiers are resolved to UUIDs automatically, including references to resources created earlier in the same run.

**Everything push writes is a draft by default.** Plan creates land in draft; updating a published plan creates a new draft version. Nothing a customer sees changes until a plan is explicitly published. On a mid-run failure, push stops immediately, reports exactly what was applied and what wasn't, and exits non-zero — there's no rollback, but because the writes were drafts, a half-applied push has no customer impact.

Push stamps a `managed-by: kelviq-cli` entry into the `metadata` of every feature and plan it creates (merged with any metadata you declare). That marker is how `--prune` later distinguishes CLI-managed resources from everything else — you'll see it in the dashboard, and it's safe to leave alone.

### Prices are all-or-nothing per plan

A plan **with** a `prices` array in config always pushes that complete array, replacing the plan's entire remote price set. A plan **without** a `prices` key never touches remote prices at all. Omitting `prices` means "leave pricing alone," not "this plan is free."

<Warning>
  If you hand-author prices, set `freeTrial`, `trialPeriod`, `enabled`, and `taxBehavior` explicitly. Omitting them makes your price compare unequal to the server's stored defaults forever, so every push re-replaces that plan's prices (harmless but noisy — each one drafts the plan again). Configs generated by `kelviq pull` don't have this problem.
</Warning>

### Features can't be updated — by design

The Kelviq API deliberately treats feature attributes as immutable; renaming a feature is a dashboard operation. If a feature in your config differs from its remote counterpart, push stops **before writing anything** and lists the drifted fields — fix the feature in the [dashboard](https://app.kelviq.com), run `kelviq pull`, and push again.

### Publishing

`--publish` publishes the plans the run just changed, after a second, separate confirmation listing them. A bare `--publish` never migrates existing customers — they stay on their current entitlements and pricing until you deliberately pass `--migrate-features` and/or `--migrate-pricing`, each of which spells out its consequence before you confirm. `--yes` skips prompts; it never substitutes for passing a migration flag explicitly.

### Pruning

Remote-only resources — things that exist in the environment but not in your config — are normally just listed as warnings. `--prune` archives them, under strict conditions:

* Only plans and features **carrying the `managed-by: kelviq-cli` marker** are ever archived. Anything created in the dashboard, by another tool, or before marker support is listed as *skipped* with the reason — never touched.
* **Products are never pruned**, ever.
* Archiving is the platform's soft-delete — nothing is destroyed.
* Pruning production requires typing the environment name at a dedicated prompt (or `--allow-prod-prune` in CI). `--yes` alone never unlocks it.

***

## kelviq promote

Moves your sandbox catalog to production in one command — the same engine and safety rails as `push`, with sandbox as the source instead of a local file.

```shell theme={null}
kelviq promote --dry-run       # preview what production would change — read-only
kelviq promote                 # diff, confirm, apply drafts to production
kelviq promote --publish       # and publish the changed plans
```

| Flag                                                     | Description                                                                                                      |
| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `--dry-run`                                              | Print the sandbox→production diff and exit. Guaranteed zero writes.                                              |
| `--yes`                                                  | Skip the confirmation prompts.                                                                                   |
| `--publish` / `--migrate-features` / `--migrate-pricing` | Same semantics as `push`.                                                                                        |
| `--prune`                                                | Archive marker-carrying production plans/features absent from sandbox. Always crosses the production-prune gate. |
| `--allow-prod-prune`                                     | Non-interactive stand-in for the typed production confirmation.                                                  |
| `--verbose` / `--json`                                   | Same semantics as `push` (`--json` requires `--dry-run`).                                                        |

Promote reads and writes **no local file** — it fetches the sandbox catalog in memory and reconciles production toward it, matching resources by identifier (which is why the config format bans per-environment UUIDs). It needs both keys configured: sandbox to read, production to write.

Everything you know about push applies: drafts by default, a confirmation showing the full diff first, feature drift between the environments stops the run with dashboard guidance, and prices replace whole-set per plan. The intended workflow is always `--dry-run` first:

```shell theme={null}
kelviq promote --dry-run   # read what would change
kelviq promote             # then do it
```

Promotion is one-way — sandbox to production. There is no reverse mode.
