Skip to main content
All commands support --help. The CLI targets your sandbox environment unless you pass --prod — see Environments & authentication. 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.
The starter file defines one product, one feature, and one plan using the typed builders from @kelviq/cli/config — see the format reference.

kelviq login

Stores a Server API key for the CLI to use.
What it does:
  1. Prints the dashboard’s API keys page 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).
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.
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.

kelviq logout

Removes stored API keys.
logout only touches the stored config file. Keys supplied via environment variables are unaffected — unset those in your shell or CI configuration.
Removing a key locally does not revoke it. To revoke a key, delete it from the API keys page in the dashboard.

kelviq env

Shows which environment and keys the CLI is configured with. Read-only — it never calls the API.
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.
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.
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:
--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.”
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.

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, 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.
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:
Promotion is one-way — sandbox to production. There is no reverse mode.