kelviq.config.ts is a plain TypeScript file that declares your pricing catalog. You author it by hand (starting from kelviq init) or generate it from a live environment with kelviq pull — the two are interchangeable starting points.
The format follows one design idea: the file describes the desired state of your catalog, and nothing else. Anything that’s an artifact of a particular deployment — UUIDs, draft/published status, server-generated timestamps — stays out, which is what makes the same file portable across sandbox and production and meaningful to diff over time. It’s TypeScript rather than YAML or JSON for one reason: types. Your editor autocompletes every field, invalid enums fail to compile, and a config that type-checks is a config the CLI can read.
A config file imports three builders from @kelviq/cli/config and exports the results:
product() / feature() / plan() is collected, regardless of its export name. Other exports (helper constants, functions) are ignored, so you can organize the file however you like — split values, comment freely, generate plans in a loop. Duplicate identifiers within a kind are a hard error.
The identity rule
Every cross-reference in the format —plan.product, entitlements[].feature — is an identifier slug, never a UUID. Identifiers are lowercase URL-safe slugs matching ^[a-z0-9][a-z0-9-_]*$ (for example pro, api-calls, seats_v2).
The Kelviq API sometimes wants UUIDs internally; the CLI resolves identifiers to UUIDs at sync time so your config file stays portable — the same file works against sandbox and production, where the same catalog has different UUIDs.
The plan lifecycle (draft → published) is also deliberately absent: the config describes desired state, not deployment state. Lifecycle is handled at sync time.
product()
feature()
plan()
Entitlements
Each entry grants a feature to the plan:
Entitlements accept additional keys beyond these — the shape varies by feature type, and unrecognized keys are passed through to the API rather than rejected.
Some enum values (for example
EVERY_28_DAYS) are only available when enabled for your account. The CLI’s enum fields autocomplete the standard values but accept any string: values it doesn’t recognize are passed through verbatim, and the API validates them when you push. This means new server-side values work without a CLI update.License keys
When license keys are enabled on a plan, every order generates a key. Thelicense object holds the plan-level issuance settings — the same controls as the dashboard’s License keys section:
The object is compared and sent as one unit:
kelviq push shows the old and new license config side by side and sends the whole object when anything in it differs. A plan that omits license leaves the remote license settings untouched, so a config file that doesn’t mention license keys never disables them. Additional keys are passed through to the API.
Individual issued keys — activating, validating, or deactivating a customer’s key — are per-customer runtime operations, not catalog state. They live in the API, the SDKs, and the MCP server, not in this file.
Prices
The price envelope is validated; the charge structures inside it are deliberately loose and passed through to the API as-is:A pulled config reproduces your remote prices in full, including nested charge structures. To author one by hand, start from
kelviq init --template saas|usage|license or pull an existing plan and adapt it. Fields you leave out — freeTrial, taxBehavior, a charge’s paymentType or reset — take the server’s defaults, and kelviq push compares prices only on the fields you set, so leaving them out never shows up as a change.Schemas as values
Everything the builders validate against is exported from@kelviq/cli/config if you want to reuse it — productDefSchema, featureDefSchema, planDefSchema, entitlementDefSchema, priceDefSchema, licenseDefSchema, kelviqConfigSchema, the enums (taxCodeSchema, featureTypeSchema, priceTypeSchema, resetSchema, taxBehaviorSchema, durationUnitSchema, identifierSchema), and their TypeScript types (ProductDef, FeatureDef, PlanDef, LicenseDef, and so on). They’re standard Zod schemas.