Health score
Design, compute, calibrate, and explain a customer health score.
A health score is a current, explainable summary, not a churn probability. Start with a small number of independent product signals that a CS team can act on. Keep commercial facts such as renewal date outside the score unless they truly describe health.
export default defineHealth({
id: 'overall',
name: 'Overall health',
components: {
adoption: {
name: 'Feature adoption',
weight: 0.4,
compute: {
type: 'feature_adoption',
features: ['exports', 'dashboards', 'alerts'],
period: '30d',
respectPlanFeatures: true,
},
},
engagement: {
name: 'Engagement',
weight: 0.4,
compute: {
type: 'event_frequency',
event: 'feature.used',
period: '30d',
target: 20,
minScore: 5,
},
},
stability: {
name: 'Stability',
weight: 0.2,
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' }, { event: 'api.error' }, { schedule: '0 0 * * *' }],
});Kite supports one health definition. Component weights must total 1. Scores and their weighted total are rounded and clamped to 0–100.
Computation types
| Type | Calculation |
|---|---|
event_frequency | count / target * 100, with optional minScore. |
inverse_event_count | maxScore - count * penaltyPerEvent. |
feature_adoption | Distinct used features divided by eligible configured features. |
custom | A deterministic formula using event counts, numeric traits, and prior components. |
Feature adoption reads feature or features event properties. With respectPlanFeatures, the
eligible set comes from the customer's availableFeatures trait. Keep that trait synchronized
with entitlement changes.
Custom components are evaluated by component ID in lexical order, so only reference a computed
component whose ID sorts earlier. Formulas run in a restricted process and must be deterministic;
Date and Math.random are unavailable.
Thresholds are validated metadata. The runtime does not assign labels from them, so define any
segment or UI label explicitly from healthScore ranges.
Calibration workflow
- Choose a window matching the product's natural use frequency.
- Calculate a baseline from representative live customers.
- Compare component distributions for retained and churned cohorts.
- Remove redundant components and cap signals one customer can inflate.
- Run
kite recompute --all --dry-runbefore persisting a changed model. - Inspect a customer with
kite health explain <customerId>after deployment.
Scheduled recomputation is needed for time-based decay when no new event arrives. Manual
recompute always evaluates health, independently of recomputeOn.