Default components

Every built-in Product Health signal — what it measures, how to read it, and what to do when it moves.

New projects start from the Product Health template: 23 components, each isolating a distinct churn signal so a single bad number can't hide behind everything else. They are grouped into seven themes below. Expand any component for a full breakdown — how we collect it, how to read it, how to analyze it, and what to do when it turns soft.

How a value becomes a score

Each component computes a raw value, then maps it to a 0–100 score. At its good threshold (or above) it scores 100; at its warn threshold (or worse) it scores 30; in between it is linear. Higher is better signals rise with the value; inverse (lower is better) signals fall as the value rises. The account's overall health is the weight-proportional average of every enabled component, banded as Healthy ≥ 80 · Watch ≥ 60 · Risky ≥ 40 · Cold < 40. Weights are relative, so you can retune one without renormalizing the rest.

Product usage

The core "are they getting value?" signals — and the largest share of the score. Breadth (feature depth, seats) and rhythm (logins, stickiness, sessions) are the earliest evidence an account is embedding the product into real work. Read them together: strong breadth with weak rhythm — or the reverse — is more telling than any single tile.

Evidence: depth and frequency of product use are consistently the strongest leading indicators of renewal in customer-success research (Gainsight, Customer Success benchmarks). Retention itself compounds: Reichheld & Sasser's classic loyalty-economics work (Bain / HBR) found a 5% lift in retention can raise profit 25–95%.

5 signalscombined weight 38≈32% of score
Login frequency (7 / 14 / 30d)login

How often the account logs in, weighting the most recent week the most.

Higher is bettergood 12warn 4weight 10
How we collect it
From app.login (direct API) → metric login. The 7-, 14-, and 30-day counts are blended 0.5 / 0.3 / 0.2, so this week carries the most weight.
How to read it
The raw value is a blended login count: (7-day ×0.5) + (14-day ×0.3) + (30-day ×0.2). A blended value of ≥12 scores 100 (Healthy); ≤4 scores 30 (Cold); around 8 lands mid-band (Watch/Risky). Because the current week is weighted heaviest, a quiet recent week pulls the score down even when the 30-day total still looks large.
How to analyze it
Frequency, not raw volume, is the clearest engagement signal — accounts that log in on a steady rhythm renew far more often than ones with occasional bursts. A falling blended count while monthly totals still look fine means recent weeks are quietly thinning out.
What to do
On a sustained drop, confirm whether it is seasonal or real disengagement, then trigger a check-in or re-onboarding nudge before the account goes silent.
Tune it
Weight 10 is the largest in the template — keep it high if logins are your truest engagement proxy, but lower it for products driven by APIs or integrations where humans rarely log in. Raise good above 12 if your healthy accounts log in more often, or raise warn to flag drift earlier. Weight, thresholds, and the metric spec are all editable in Settings → Components.
DAU / WAU style ratiologin

Stickiness — how active the last 7 days were versus the 30-day pace.

Higher is bettergood 45warn 15weight 8
How we collect it
Distinct active days from login events in the last 7 vs last 30, expressed as a 0–100 ratio.
How to read it
Raw value = (active days in last 7 ÷ 7) ÷ (active days in last 30 ÷ 30) × 100, capped at 100. ≥45 → 100, ≤15 → 30, mid-30s sits in Watch. A value near 100 means this week matched or beat the monthly pace; a value well below it means the account was busier earlier in the month than it is now.
How to analyze it
A habit/stickiness proxy. Roughly 20%+ DAU/WAU is a common healthy SaaS benchmark; a high ratio means the product is part of the daily routine — the hardest thing for a competitor to displace.
What to do
Low stickiness alongside healthy totals points to power-user concentration; broaden adoption across the wider team.
Tune it
Lower the weight if your product is intentionally low-frequency (monthly reporting, quarterly reviews) where daily stickiness isn't the goal. Raise good/warn if your category's healthy stickiness runs higher than the ~20% baseline. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Feature depthfeature_used

Share of your feature catalog the account actually touches.

Higher is bettergood 55warn 20weight 8
How we collect it
Distinct feature names (payload feature / label / name) from app.feature.used and the SDK feature_used event, over a default catalog of 10 features.
How to read it
Raw value is the percentage of your catalog used: distinct features ÷ catalog size × 100. With the default catalog of 10, touching ~6 features → ~55% → 100; touching 2 → ~20% → 30. The score scales directly with catalog size, so an inaccurate catalog count distorts every account's reading.
How to analyze it
Breadth of adoption is a proxy for switching cost — accounts that lean on many features are far stickier than single-feature users. Shallow depth flags accounts that bought more than they use, the classic shape that downsizes at renewal.
What to do
Narrow depth → targeted education on high-value modules the account has never opened.
Tune it
The default catalog is 10 features — if you ship far more or fewer, edit the spec's totalFeatures so the ratio reflects reality, otherwise every score is mis-scaled. Lower the weight for products where most value comes from one or two core features. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Session lengthsession.session_minutes

Average minutes per session.

Higher is bettergood 15warn 3weight 6
How we collect it
Average of payload session_minutes from app.session.end / SDK session_ended over the last 30 days.
How to read it
Raw value is the mean session length in minutes over 30 days. ≥15 min → 100, ≤3 min → 30, ~9 min lands mid-band. Read it with frequency, not alone: short-but-frequent can be perfectly healthy, while a very high average can signal users getting stuck rather than getting value.
How to analyze it
A depth-of-engagement proxy that must be read with frequency: short but frequent can be perfectly healthy (quick habitual checks), while short and infrequent signals disengagement. Unusually long sessions are not automatically good — they can mean confusion, so cross-check frustration signals.
What to do
Interpret alongside feature depth and frustration before acting; the number alone is ambiguous.
Tune it
Lower the weight or widen thresholds if your product is built for short, frequent visits (dashboards, status checks) where long sessions aren't expected. Raise good for tools meant for deep, sustained work. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Seats used vs purchasedseat_used / seat_purchased

License utilization — active seats over purchased seats.

Higher is bettergood 75warn 40weight 6
How we collect it
Latest payload count on billing.seats.used vs billing.seats.purchased (commonly fed by a billing integration).
How to read it
Raw value is used ÷ purchased × 100, clamped 0–100, from the most recent seat payloads. ≥75% → 100, ≤40% → 30. If no purchased-seat figure has ever arrived the component skips (no score) rather than reading 0, so it never punishes accounts you haven't instrumented.
How to analyze it
Utilization is a direct renewal-risk signal — low seat usage is the single most common justification a buyer uses to downsize or cancel. High, sustained utilization is also your clearest expansion opening.
What to do
<40% → drive activation of unused seats well ahead of renewal; >90% sustained → open an expansion conversation.
Tune it
Raise the weight for seat-based pricing where utilization directly drives renewal and expansion; lower or disable it for usage- or consumption-based pricing where seats are irrelevant. Tighten warn toward 50–60 if you want earlier alerts on under-adoption. Weight, thresholds, and the metric spec are all editable in Settings → Components.

Engagement & activity

Recency, reach, and a strong start. These catch who is active and how recently — the dimensions that move first when an account begins to drift. A healthy account is recent, multi-user, and well-onboarded; a gap in any one of the three is an early crack.

Evidence: recency is a core RFM churn predictor, and early activation / time-to-value strongly predicts long-term retention (Amplitude & Reforge product benchmarks). Single-threaded, single-user accounts are structurally more churn-prone than multi-user ones.

3 signalscombined weight 22≈18% of score
Days since last loginlogin

How long since the account last logged in.

Lower is bettergood 7warn 21weight 8
How we collect it
Days between now and the most recent login event.
How to read it
Raw value is the number of days since the last login (inverse — lower is better). ≤7 days → 100, ≥21 days → 30, ~14 days sits mid-band. The score decays linearly through the gap, so it gives early warning rather than flipping only once the account is long gone.
How to analyze it
Recency is one of the oldest and most reliable churn predictors — the 'R' in RFM analysis. A login gap widening past the account's normal cadence is often the first visible step toward cancellation.
What to do
Crossing the warn line should trigger a re-engagement touch before the gap normalizes into churn.
Tune it
Tighten good/warn (e.g., 3/10) for daily-use products so gaps flag faster; loosen them for weekly or monthly tools so a normal cadence isn't penalized. Keep the weight high — recency is among the most reliable churn predictors. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Active users (30d)active_user

How many distinct people used the account this month.

Higher is bettergood 4warn 1weight 7
How we collect it
Distinct external_user_id in app.user.active (and SDK page_viewed) over the last 30 days.
How to read it
Raw value is the count of distinct users seen in 30 days. ≥4 → 100, ≤1 → 30, with 2–3 in between. Read it against the account's seat count: 3 active users is healthy for a 4-seat plan but a warning sign for a 50-seat rollout.
How to analyze it
Multi-user accounts are dramatically stickier — single-threaded accounts churn the moment their one champion leaves. Breadth of active users is a structural retention moat, not just an engagement number.
What to do
One active user on a multi-seat plan is champion risk; broaden the footprint and identify a backup sponsor.
Tune it
Raise good/warn to match your typical account size — '4 users' is healthy for an SMB but low for an enterprise rollout. Increase the weight if multi-user adoption is central to your stickiness story. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Onboarding in first weekonboarding_completed

Did the account complete onboarding within 7 days of signup?

Pass / failweight 7
How we collect it
Whether an onboarding_completed event fell within 7 days of account creation. Binary: yes → 100, no → 30.
How to read it
There is no sliding scale — it is pass (100) or fail (30). The window is fixed at the first 7 days after account creation, so it is effectively frozen once an account is older than a week. That permanence is why it carries a heavy weight despite being binary.
How to analyze it
Time-to-value is among the strongest predictors of long-term retention — accounts that reach their first 'aha' quickly retain far better than slow starters, and a weak first week predicts churn months ahead.
What to do
A miss is a durable risk flag — prioritize hands-on onboarding recovery rather than waiting for usage to decline.
Tune it
Being binary, there are no thresholds to tune — only the weight. Keep it high (or raise it) for products with a meaningful setup phase; lower it if first value is instant and onboarding isn't a distinct step. Weight, thresholds, and the metric spec are all editable in Settings → Components.

Support health

Support load and tone are leading indicators of frustration — they often move weeks before an account says anything. Volume, speed, and tone tell different stories: a quiet queue with a single negative tag can matter more than a busy one that resolves quickly.

Evidence: Dixon, Toman & DeLisi, "Stop Trying to Delight Your Customers" (Harvard Business Review, 2010) showed that reducing customer effort, not exceeding expectations, is what protects loyalty — high-effort support experiences are a powerful predictor of disloyalty.

4 signalscombined weight 20≈17% of score
Open ticket pressureopened − resolved

Roughly how many support tickets are open right now.

Lower is bettergood 1warn 3weight 6
How we collect it
support_ticket_opened minus support_ticket_resolved over 30 days. Sources: direct API, or Intercom / Zendesk / Freshdesk via your event mappings (Intercom ticket topics are pre-mapped).
How to read it
Raw value is opened minus resolved in the window, floored at 0 (inverse). ≤1 open → 100, ≥3 → 30. Note the failure mode: if no support events arrive at all it reads 0 and looks perfectly healthy — a clean score here only means something once tickets are flowing in.
How to analyze it
Standing unresolved load is friction the customer is actively living with. A backlog that grows faster than it clears is the shape that precedes escalations and quiet non-renewals.
What to do
Persistent pressure → assign a clear owner, work the backlog down, and follow up personally.
Tune it
Lower the weight or disable it if no support integration feeds ticket events, otherwise it reads artificially healthy for the wrong reason. Tighten warn for high-touch segments where even a couple of open tickets matter. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Ticket sentiment (negative)support_ticket_negative

Count of negatively-tagged support interactions.

Lower is bettergood 0warn 2weight 5
How we collect it
Count of support.ticket.negative events over the last 30 days.
How to read it
Raw value is the count of negative-tagged events in 30 days (inverse). 0 → 100, ≥2 → 30, so a single negative interaction already pulls the score into Watch. It relies on your source tagging sentiment — without that signal it simply reads 0.
How to analyze it
Tone is a leading indicator that precedes the formal complaint and the silent non-renewal. Even one or two negative interactions on a high-value account are worth attention.
What to do
Any negative tag on a strategic account → personal outreach from an owner, not a queue reply.
Tune it
Raise the weight for high-value or strategic accounts where tone matters more than volume. Set warn to 1 if you want a single negative interaction to register rather than waiting for a pattern. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Ticket velocity spikesupport_ticket_opened

A sudden jump in new tickets versus the prior week.

Lower is bettergood 0warn 3weight 5
How we collect it
This week's opened tickets minus last week's (same window size).
How to read it
Raw value is this week's opened tickets minus last week's, floored at 0 (inverse) — it measures change, not level. 0 (flat or down) → 100, +3 or more → 30. A steady high volume scores well here; only an acceleration is penalized.
How to analyze it
A spike often signals a release regression, a confusing change, or a newly-onboarded team hitting walls. Watching the rate of change surfaces problems before absolute counts look bad.
What to do
Investigate the spike's root cause — release, feature, or new team — rather than just clearing tickets.
Tune it
Lower the weight if your ticket volume is naturally spiky (scheduled launches, seasonal peaks) to avoid false alarms. Raise warn if small fluctuations are normal and only a real surge should count. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Time to resolutionresolution_hours

Average hours to resolve a ticket.

Lower is bettergood 8warn 48weight 4
How we collect it
Average payload resolution_hours on resolved tickets over the last 30 days.
How to read it
Raw value is the mean resolution_hours across resolved tickets in 30 days (inverse). ≤8h → 100, ≥48h → 30, ~28h sits mid-band. It only reflects tickets that carry a resolution_hours payload, so accounts with no resolved tickets contribute nothing here.
How to analyze it
Resolution speed is the most tangible form of customer effort, and effort — not delight — is what protects loyalty. Slow resolutions compound: the longer a problem stays open, the more it colors the renewal decision.
What to do
Sustained slow resolution → review staffing and process for that account's tier.
Tune it
Align good/warn with your published SLA so the score reflects your actual promise, not a generic default. Raise the weight for premium tiers where speed is part of what the customer pays for. Weight, thresholds, and the metric spec are all editable in Settings → Components.

Relationship & lifecycle

Context that tunes urgency and cost-of-churn rather than raw engagement. A renewal next week is more urgent than one next year; a high-MRR account at risk deserves attention first. Use these to decide who to act on first when several accounts look equally soft — not to judge health on their own.

Evidence: Reichheld, "The One Number You Need to Grow" (Harvard Business Review, 2003) established stated satisfaction (NPS) as a loyalty proxy — useful, but lagging and self-selected, so it confirms rather than leads. Churn risk is front-loaded into a customer's first 30–90 days.

4 signalscombined weight 12≈10% of score
Days until renewalaccount.RenewalAt

Time left until the contract renews.

Higher is bettergood 90warn 30weight 4
How we collect it
Derived from the account's RenewalAt date — no events needed.
How to read it
Raw value is days remaining until renewal (higher = more runway). ≥90 days → 100, ≤30 days → 30. The score falls as the date approaches — by design, so a soft account becomes more urgent the closer renewal gets. Accounts with no RenewalAt set are skipped.
How to analyze it
This is an urgency multiplier, not a health signal — a mediocre score two weeks from renewal is far more dangerous than the same score a year out. It tells you when to act.
What to do
As renewal nears, prioritize accounts whose other signals are soft; the runway is shrinking.
Tune it
Raise good/warn (e.g., 120/60) if your sales motion needs a longer runway to act on at-risk renewals. Keep the weight modest — it should nudge prioritization, not dominate the health score. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Contract value (MRR tier)account.Mrr

Revenue-at-risk band from the account's MRR.

Higher is betterweight 3
How we collect it
Banded from account Mrr: ≥$5k → 85, ≥$1k → 75, >$0 → 65, otherwise 55.
How to read it
Not a sliding scale — it snaps to one of four bands by MRR (85 / 75 / 65 / 55). Even a zero-MRR account never scores below 55, so this component lifts the floor for paying accounts rather than penalizing anyone. It encodes how much a churn would cost, not how likely it is.
How to analyze it
Not a churn-likelihood signal — a prioritization lens. Two equally at-risk accounts are not equal if one is 10× the revenue; concentrate scarce attention where the dollar impact is largest.
What to do
Use to triage: same risk, higher MRR → handle first.
Tune it
The bands ($5k / $1k) are fixed in the spec — edit them to match your own revenue distribution so 'high value' means high value for your business. Keep the weight low: it's a triage lens, not a churn signal, and over-weighting it makes big accounts look healthy regardless of behavior. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Customer tenureaccount.CreatedAt

How long the account has been a customer.

Higher is bettergood 365warn 30weight 3
How we collect it
Days since account creation — no events needed.
How to read it
Raw value is days since the account was created (higher = more established). ≥365 days → 100, ≤30 days → 30. New accounts deliberately start low here, reflecting that early-life churn risk is highest; the score climbs steadily as trust is earned.
How to analyze it
Churn risk is front-loaded — the first 30–90 days carry the highest cancellation rate, and tenure earns trust. A young account behaves nothing like a two-year one, so this contextualizes every other signal.
What to do
Treat new-account dips as fragile and lean in early; treat long-tenured dips as notable departures from a known baseline.
Tune it
Lower good (e.g., to 90–180 days) if your customers reach a stable, trusting relationship quickly. Keep the weight small — tenure is context for the other signals, not a health verdict on its own. Weight, thresholds, and the metric spec are all editable in Settings → Components.
NPS / CSAT (optional)nps_response / csat_response

Blended survey satisfaction, when you collect it.

Higher is betterweight 2
How we collect it
Average payload score from survey.nps (×10) and survey.csat (×20), blended 60/40 over 180 days. Skipped entirely if you send neither.
How to read it
Raw value is a 0–100 blend: NPS×10 and CSAT×20 are each clamped to 100, then mixed 60/40 (or whichever you collect). It is already on the score's scale, so it maps almost directly. Crucially it auto-skips when no survey events exist, so it never drags down accounts you haven't surveyed.
How to analyze it
Stated sentiment complements behavior, but it lags and is self-selected (detractors and fans answer most). Trust it as confirmation, not a primary alarm — behavior usually moves first.
What to do
A detractor score on an otherwise-healthy account is an early warning worth a direct conversation.
Tune it
Because it auto-skips with no surveys, you can leave it enabled at a low weight with no downside. Raise the weight only if you survey consistently and trust your response rate; otherwise let behavioral signals lead. Weight, thresholds, and the metric spec are all editable in Settings → Components.

Behavioral & UX friction

Friction the customer feels but rarely reports — captured automatically by the browser SDK with no extra instrumentation. Because they're auto-captured, a flat zero can simply mean the SDK isn't installed; confirm coverage before trusting a clean score here.

Evidence: UX research (Nielsen Norman Group) treats rage clicks, dead clicks, and form abandonment as markers of unmet intent; repeated friction erodes satisfaction and completion, and correlates with disengagement.

2 signalscombined weight 10≈8% of score
Frustration signals (30d)frustration_score

Rage clicks and abandoned forms from auto-capture.

Lower is bettergood 0warn 8weight 6
How we collect it
Count of SDK rage_click + form_abandoned events (→ frustration_score) over 30 days. No extra instrumentation beyond the browser SDK.
How to read it
Raw value is the count of frustration events in 30 days (inverse). 0 → 100, ≥8 → 30, ~4 sits mid-band. It depends on the browser SDK being installed; server-only integrations won't generate it, in which case it reads 0 by absence rather than by good UX.
How to analyze it
Unsolicited friction — users rarely file a ticket about a frustrating form, they just struggle or leave. Rage clicks cluster on broken or confusing UI; abandonment marks intent that never completed.
What to do
Recurring frustration on the same screen is a product/UX fix, not a CS touch — route it to the team that owns that surface.
Tune it
Raise warn if your auto-capture is noisy (some UIs trigger benign rage clicks) to cut false positives. Increase the weight for self-serve products where unreported friction is your main early-warning channel. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Client errors (30d)error_rate

JavaScript errors the account's users hit.

Lower is bettergood 0warn 5weight 4
How we collect it
Count of SDK js_error events (→ error_rate) over the last 30 days.
How to read it
Raw value is the count of captured JS errors in 30 days (inverse). 0 → 100, ≥5 → 30. The threshold is strict — set it against your real baseline, because a busy app may emit a few benign errors that don't reflect a degraded experience.
How to analyze it
Errors are objective friction — a user hitting repeated failures is being actively blocked from value. Even when they never complain, error exposure correlates with disengagement.
What to do
Cross-reference errored sessions with the account and escalate reproducible failures to engineering quickly.
Tune it
Set warn relative to your normal error baseline — 0/5 may be too strict for a large app. Lower the weight if you already monitor errors in a dedicated tool and don't want to double-count. Weight, thresholds, and the metric spec are all editable in Settings → Components.

Intelligence & trend

Trends across all signals at once — they catch decline even when every individual number still looks fine from the last active period. When these turn red while the rest stay green, trust the trend: it is usually seeing the change before the levels catch up.

Evidence: in customer-success practice, gradual disengagement — not a single event — precedes most cancellations, which is why usage-decline and silence are the canonical early-warning signals (Gainsight early-warning frameworks).

2 signalscombined weight 10≈8% of score
Account silenceall events

Days since any event of any kind for the account.

Lower is bettergood 7warn 21weight 5
How we collect it
Days since the most recent event across every metric. Skipped if the account has no events at all.
How to read it
Raw value is days since the most recent event of any type (inverse). ≤7 days → 100, ≥21 → 30. Unlike days-since-last-login it watches every signal at once, so it only goes quiet when the account truly goes dark — which makes a low score here especially meaningful.
How to analyze it
The broadest recency net — it catches accounts that went quiet across the board, even if the last active snapshot looked healthy. Silence frequently precedes a cancellation by weeks.
What to do
Lengthening silence is a high-priority re-engagement trigger; do not wait for renewal.
Tune it
Tighten good/warn for high-frequency products where three weeks of total silence is already very late. Keep the weight meaningful — it's the broadest safety net and catches accounts the per-metric signals miss. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Activity drop (WoW)all events

How much total activity fell this week versus last.

Lower is bettergood 20warn 60weight 5
How we collect it
100 − (this week's events ÷ last week's × 100), across all events. Skipped with no prior-week baseline.
How to read it
Raw value is the percentage drop week-over-week: 0 means flat or growing, 100 means total silence versus last week. ≤20% drop → 100, ≥60% → 30. It needs a prior-week baseline, so brand-new accounts skip it until there is something to compare against.
How to analyze it
Catches the rate of disengagement, not the absolute level — a steep week-over-week fall is an early tremor even when totals still look respectable. The trend leads the level.
What to do
A sharp drop is worth a quick look while it is fresh — find what changed (champion left, season, competitor).
Tune it
Loosen warn if week-over-week swings are normal for your usage pattern, so only real cliffs score low. Raise the weight if catching momentum changes early matters more to you than absolute activity levels. Weight, thresholds, and the metric spec are all editable in Settings → Components.

Growth & advocacy

The positive signals — an account deepening, not just surviving. Expansion and referrals are the leading edge of net revenue retention. These only ever lift a score, never sink it, so treat them as upside flags for expansion plays rather than risk alerts.

Evidence: net revenue retention is one of the strongest drivers of SaaS value (Bessemer / SaaS benchmarks), and active referral is a stronger proof of advocacy than any stated score, because the customer stakes their own reputation on it.

3 signalscombined weight 8≈7% of score
Seat expansionseat_expanded

Did the account add seats recently?

Higher is bettergood 1warn 0weight 3
How we collect it
Count of billing.seat.expanded events (often via a billing integration) over the last 30 days.
How to read it
Effectively a yes/no with a soft floor: ≥1 expansion event in 30 days → 100, 0 → 30. It is a bonus signal — it lifts healthy, growing accounts rather than punishing steady ones, since 'no expansion this month' is normal for most.
How to analyze it
Expansion is the strongest positive health signal — accounts add seats when they are getting value and rolling out wider. It is also the leading edge of net revenue retention.
What to do
Expanding accounts are advocacy and case-study candidates; nurture them, do not just renew them.
Tune it
Raise the weight if expansion is central to your growth model and you want healthy accounts rewarded for it. Lengthening the window in the spec captures slower expansion cycles. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Plan upgradesplan_upgraded

Did the account move to a higher plan?

Higher is bettergood 1warn 0weight 3
How we collect it
Count of billing.plan.upgraded events over the last 180 days.
How to read it
Yes/no with a soft floor over a long 180-day window: ≥1 upgrade → 100, 0 → 30. The wide window means a single upgrade keeps lifting the score for months, reflecting that a recent upgrade is a durable signal of commitment.
How to analyze it
A willingness-to-pay-more signal — upgrades reflect realized value and rising commitment, and they meaningfully lower near-term churn risk.
What to do
Recent upgraders are prime expansion and reference targets; reinforce the value that drove the upgrade.
Tune it
Widen or narrow the 180-day window to match your typical upgrade cadence. Raise the weight if upgrades are a strong loyalty signal in your data; keep it modest if upgrades are rare across your base. Weight, thresholds, and the metric spec are all editable in Settings → Components.
Referralsreferral

Did the account refer someone?

Higher is bettergood 1warn 0weight 2
How we collect it
Count of growth.referral events over the last 180 days.
How to read it
Yes/no with a soft floor over 180 days: ≥1 referral → 100, 0 → 30. It carries the lowest weight in the template on purpose — referrals are sparse, so it should reward advocates without dragging down the majority who simply haven't referred.
How to analyze it
Referring is the clearest behavioral proof of advocacy — stronger than any stated survey score, because the customer is staking their own reputation. Referrers are among your stickiest accounts.
What to do
Thank and activate referrers; they are your best growth and renewal anchors.
Tune it
Keep the weight low by default — referrals are sparse, so a high weight would make most accounts look worse simply for not referring. Lengthen the window if your referral cycle is long. Weight, thresholds, and the metric spec are all editable in Settings → Components.

Make them your own

Every threshold, weight, and metric spec above is editable in Settings → Components. Lower a weight you don't trust, disable a signal you don't collect, or build a brand-new component from the metric source picker. The score and every widget update together.