--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 starterkelviq.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:
- 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. - Prompts you to paste the key. In an interactive terminal each pasted character shows as
*— so you can see the paste landed — and aReceived a N-character key.line confirms the length. The key itself is never echoed or logged. - Stores it in
~/.config/kelviq/config.jsonwith owner-only permissions (0600).
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.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 akelviq.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 akelviq.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 amanaged-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 aprices 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.”
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, runkelviq 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-climarker 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-prunein CI).--yesalone never unlocks it.
kelviq promote
Moves your sandbox catalog to production in one command — the same engine and safety rails aspush, 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: