Config Reference
Define event schemas, lifecycle, journeys, rules, segments, health, and signals in TypeScript.
@kitesdk/config is the typed DSL for a Kite project. Definitions are ordinary TypeScript
files that the CLI compiles into a deterministic configuration snapshot.
npm install --save-dev @kitesdk/config
kite validatekite init creates a working example of every definition. This reference describes the
available fields and how they compose.
Project structure
.kite/config.json tells the CLI which source files belong to the project:
{
"project": "acme",
"projectId": "proj_acme",
"defaultEnvironment": "test",
"stack": "node",
"configFiles": {
"main": "kite.config.ts",
"states": "states/lifecycle.ts",
"journeys": ["journeys/onboarding.ts"],
"rules": ["rules/activation.ts"],
"segments": ["segments/at-risk.ts"],
"health": "health/overall.ts",
"signals": ["signals/engagement.ts"]
}
}Paths must be relative to the project root. A project has one main config, one lifecycle, and at most one health definition; journeys, rules, segments, and signals can be split across multiple files.
Event schemas
Use defineConfig to declare accepted events and customer traits.
import { defineConfig } from '@kitesdk/config';
export default defineConfig({
project: 'acme',
strictSchema: true,
events: {
'account.created': {
description: 'A customer account was created',
properties: {
plan: { type: 'string', enum: ['starter', 'growth'] },
employees: { type: 'number', optional: true },
},
},
'profile.updated': {
properties: {
fields: { type: 'array', items: { type: 'string' } },
},
},
},
customerTraits: {
mrr: { type: 'number', optional: true },
renewal_date: { type: 'date', optional: true },
},
});Property schemas
| Field | Values | Description |
|---|---|---|
type | string, number, boolean, date, array | Required value type. |
optional | boolean | Allows the property to be omitted. |
enum | string[] | Restricts a scalar string to known values. |
items | scalar schema | Required item schema when type is array. |
strictSchema defaults to false. Enable it when producers and schemas are coordinated and
unknown event properties should be rejected.
Conditions
Lifecycle transitions, journeys, rules, and segments use the same recursively composable
Condition type.
Composition
{ all: [{ lifecycle: 'trial' }, { event: 'feature.used' }] }
{ any: [{ inactiveDays: 14 }, { healthScore: { max: 40 } }] }
{ not: { traits: { plan: 'free' } } }State and behavior
| Condition | Example |
|---|---|
| Lifecycle state | { lifecycle: 'activated' } |
| Journey status | { journey: 'onboarding', status: 'complete' } |
| Event occurrence | { event: 'feature.used', minCount: 3, days: 7 } |
| Event count | { eventCount: { event: 'feature.used', days: 30, min: 5, max: 50 } } |
| Customer traits | { traits: { plan: 'growth' } } |
| Inactivity | { inactiveDays: 14 } or { inactiveDays: { min: 14 } } |
| Account age | { accountAgeDays: { min: 60 } } |
| Overall health | { healthScore: { min: 60, max: 100 } } |
| Health component | { healthComponent: 'adoption', minScore: 80 } |
| Component map | { healthComponent: { adoption: { min: 80 } } } |
| Unused plan features | { featuresUnused: { plan: 'growth', minCount: 2 } } |
Numeric ranges accept min, max, or both.
Period comparison
Compare event frequency with an earlier window:
{
metric: 'event_count',
event: 'feature.used',
period: '14d',
comparison: 'less_than',
referenceMetric: { period: 'previous_14d', multiplier: 0.5 },
}Comparisons are less_than, less_than_or_equal, greater_than, and
greater_than_or_equal. Durations use a number followed by m, h, d, or w, such as
30m, 12h, 7d, or 4w.
Lifecycle
defineLifecycle models mutually exclusive customer states.
import { defineLifecycle } from '@kitesdk/config';
export default defineLifecycle({
initialState: 'new',
states: {
new: {},
trial: {
description: 'Evaluating the product',
enteredWhen: { event: 'account.created' },
},
activated: {
enteredWhen: { journey: 'onboarding', status: 'complete' },
},
at_risk: {
enteredWhen: { any: [{ inactiveDays: 14 }, { healthScore: { max: 40 } }] },
exitWhen: { eventCount: { event: 'feature.used', days: 7, min: 3 } },
},
cancelled: { terminal: true },
},
emits: {
onEnter: { activated: 'customer.activated' },
onExit: { at_risk: 'customer.churn_risk.resolved' },
},
});Each state supports description, terminal, enteredWhen, and exitWhen. Emission maps use
state IDs as keys. Validation rejects an unknown initial state and transition cycles.
Journeys
defineJourney describes ordered milestones and expected timing.
import { defineJourney } from '@kitesdk/config';
export default defineJourney({
id: 'onboarding',
name: 'Onboarding',
startsWhen: { event: 'account.created' },
expiresAfter: '30d',
steps: [
{
id: 'first_value',
name: 'Reached first value',
completedWhen: { event: 'feature.used' },
expectedWithin: '3d',
onComplete: { emit: 'journey.onboarding.first_value' },
},
{
id: 'integration',
name: 'Connected an integration',
completedWhen: { event: 'integration.connected' },
optional: true,
},
],
onComplete: { emit: 'journey.onboarding.completed' },
onExpire: { emit: 'journey.onboarding.expired' },
});Journey statuses are in_progress, complete, and expired. Steps are evaluated in order;
each requires an id, name, and completedWhen condition.
Rules
defineRules returns an array of deterministic trigger-condition-action rules.
import { defineRules } from '@kitesdk/config';
export default defineRules([
{
id: 'activation_check',
name: 'Activation check',
triggers: [
{ event: 'integration.connected' },
{ journey: 'onboarding', status: 'complete' },
],
conditions: {
all: [
{ lifecycle: 'trial' },
{ journey: 'onboarding', status: 'complete' },
],
},
action: { type: 'lifecycle_transition', to: 'activated' },
},
]);Triggers support { event }, { journey, status }, and { schedule }, where schedules use a
cron expression. Actions are:
{ type: 'lifecycle_transition', to: 'activated' }
{ type: 'emit_signal', signal: 'expansion.ready', metadata: { source: 'rule' } }
{ type: 'add_to_segment', segment: 'high_intent' }Segments
defineSegments computes dynamic cohorts from customer state.
import { defineSegments } from '@kitesdk/config';
export default defineSegments([
{
id: 'at_risk',
name: 'At risk',
color: '#f59e0b',
filter: {
any: [{ lifecycle: 'at_risk' }, { healthScore: { max: 40 } }],
},
onEnter: { emit: 'segment.at_risk.entered' },
onExit: { emit: 'segment.at_risk.exited' },
},
]);color accepts a hex color. Membership follows filter; onEnter and onExit emit when that
membership changes.
Health
defineHealth combines weighted components into a score. Component weights must total 1.0,
and thresholds must descend from excellent through critical.
import { defineHealth } from '@kitesdk/config';
export default defineHealth({
id: 'overall',
name: 'Overall health',
components: {
engagement: {
weight: 0.7,
name: 'Engagement',
compute: {
type: 'event_frequency',
event: 'feature.used',
period: '30d',
target: 20,
minScore: 5,
},
},
stability: {
weight: 0.3,
name: 'Stability',
compute: {
type: 'inverse_event_count',
event: 'api.error',
period: '30d',
penaltyPerEvent: 5,
maxScore: 100,
},
},
},
thresholds: { excellent: 80, good: 60, poor: 30, critical: 0 },
recomputeOn: [{ event: 'feature.used' }, { schedule: '0 0 * * *' }],
});Compute strategies
| Type | Required fields | Purpose |
|---|---|---|
event_frequency | event, period, target | Scores progress toward a target event count. |
feature_adoption | features, period | Scores adoption across named features. |
inverse_event_count | event, period, penaltyPerEvent | Subtracts score as undesirable events occur. |
custom | formula | Computes a deterministic numeric score. |
feature_adoption also supports respectPlanFeatures. Frequency supports minScore, and
inverse count supports maxScore.
Custom formulas receive only deterministic context:
compute: {
type: 'custom',
formula: ({ events, traits, computed }) =>
Math.min(
100,
events.count('team.member_invited', { days: 60 }) * 10 +
(traits.seats ?? 0) +
(computed.engagement ?? 0),
),
}Formulas cannot close over external bindings or use nondeterministic globals such as
Date.now() or Math.random().
Signals
defineSignals detects meaningful activity patterns and emits named facts.
import { defineSignals } from '@kitesdk/config';
export default defineSignals([
{
id: 'engagement_spike',
name: 'Engagement spike',
detect: {
metric: 'event_count',
event: 'feature.used',
period: '7d',
threshold: { multipleOfAverage: 2, averagePeriod: '28d', minAverage: 2 },
},
emit: 'signal.engagement.spike',
},
{
id: 'first_use',
name: 'First feature use',
detect: { type: 'first_occurrence', event: 'feature.used' },
emit: 'signal.feature.first_use',
metadata: { include: ['feature'] },
},
]);Metric thresholds require averagePeriod and either multipleOfAverage or
lessThanFractionOfAverage. minAverage avoids noisy comparisons against a tiny baseline.
Validation workflow
kite validate
kite dev
kite config diff --env test
kite deploy --env testValidation checks exact fields, types, duplicate IDs, event and state references, lifecycle
cycles, duration formats, health weights and thresholds, and custom formula safety. Use
kite validate --strict in CI to treat warnings as errors.