Docs

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 validate

kite 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:

.kite/config.json
{
  "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.

kite.config.ts
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

FieldValuesDescription
typestring, number, boolean, date, arrayRequired value type.
optionalbooleanAllows the property to be omitted.
enumstring[]Restricts a scalar string to known values.
itemsscalar schemaRequired 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

ConditionExample
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.

states/lifecycle.ts
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.

journeys/onboarding.ts
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.

rules/activation.ts
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.

segments/at-risk.ts
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.

health/overall.ts
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

TypeRequired fieldsPurpose
event_frequencyevent, period, targetScores progress toward a target event count.
feature_adoptionfeatures, periodScores adoption across named features.
inverse_event_countevent, period, penaltyPerEventSubtracts score as undesirable events occur.
customformulaComputes 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.

signals/engagement.ts
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 test

Validation 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.

On this page