PPactDocs
Sales

Health weights

Tune the per-signal weights that drive each account's composite customer-health score, with tenant overrides layered over shipped defaults.

Health weights

Every account gets a 0-100 composite health score and a risk band. That score is a weighted average of individual signals, and health weights are the knobs that decide how much each signal counts for your tenant. Weights are managed under /v1/health/weights; the scoring math lives in core.customer_health.health_scorer.

Live

Backed by real code and a real table. Defaults live in DEFAULT_WEIGHTS; per-tenant overrides persist to health_score_weights and are merged in at score time by load_tenant_weights.

How the composite score works

The composite is the weighted average of each signal's sub-score, divided by the total weight — so weights do not need to sum to 1.0, and a missing or zero-weight signal doesn't drag the score down. The result maps to a risk band:

code
score >= 75   → healthy
50 – 74       → watch
25 – 49       → at_risk
0 – 24        → critical

The signals and their default weights

DEFAULT_WEIGHTS ships these values, chosen so the score is most influenced by the things a CSM intuitively cares about — are people responding, is there money in play, are execs engaged:

SignalDefault weight
activity_recency0.20
engagement_frequency0.15
pipeline_health0.15
payment_status0.15
contact_count_active0.10
executive_engagement0.10
support_ticket_count0.05
nps_score0.05
account_growth_signal0.05
engagement_velocity_trend0.0
sentiment_trend0.0
multi_thread_coverage0.0

Why three signals default to 0.0

engagement_velocity_trend, sentiment_trend, and multi_thread_coverage are the deal + activity data-layer signals. They default to weight 0.0 so they feed the churn model (via the feature vector and SHAP explanations) without shifting the human-facing composite score. A tenant can opt them into the composite by raising their weight override.

Managing weights

Method & pathPurpose
GET /v1/health/weightsList the effective per-signal weights (defaults + overrides)
PATCH /v1/health/weightsAdmin: upsert one or more weight overrides

Overrides are validated: a weight must be >= 0, and unknown signal names are ignored rather than persisted, so the override set can never drift out of sync with the signal registry. Every call derives tenant_id from the auth context; nothing is read from the request body.

The weights feed the same customer_health module that also exposes the at-risk feed (GET /v1/health/feed), the band histogram (GET /v1/health/distribution), save-task review queue, and the churn model endpoints (POST /v1/health/model/retrain, GET /v1/health/model/info). Weight changes affect the composite score immediately; the churn model is retrained separately.