---
id: saas/health-score-risco-churn
industry: SaaS and technology
usage: widely used
effort: medium
grain: 1 row per active paying account per day, plus a history of severity changes
---

# Health score and churn risk queue by account

A daily queue of at-risk accounts, with the reason and MRR for each, built from signals that actually precede cancellation.

## Executive summary

**For:** SaaS CS and retention teams that today find out about churn when the customer has already canceled.

**What it solves**

- CS gets a short queue of accounts with a concrete reason and MRR at risk.
- The alert only uses signals proven to be tied to churn, with no false comfort.
- Every risk change is recorded, so you can measure whether the score is right.

**Why it matters:** Exit signals are scattered: an overdue invoice in billing, a cancellation request in support, a contract expiring in the CRM and a complaint in an email. A pretty score that marks most revenue as healthy is worse than none, because it pulls attention away from who is leaving. The value lies in getting right who will cancel, not in having a grade for everyone.

**Business questions it answers**

- Which accounts are at critical churn risk today and how much MRR do they add up to?
- Which contracts expire in the next 90 days with no renewal under negotiation?
- Who asked to cancel or downgrade this week?
- Of the accounts we flagged as at risk last quarter, how many canceled?
- Build customer X's profile for the retention meeting.

**What you need**

- Billing and CRM or support tool connected to Nekt
- The single customer across billing, CRM, product and support
- A history of churns to test each signal before using it

**What you get**

- A daily table with severity, reason and MRR at risk by account
- A history of severity changes to measure accuracy
- A CS action queue sorted by MRR at risk
- An AI agent that builds the account profile with the evidence

## Detailed

### The problem

The typical health score sums usage, logins and engagement into a 0 to 100 grade and paints almost the entire base green. In one real base, almost none of the customers who canceled had a low-usage signal, and most were active in the days before leaving. Meanwhile, the signals that actually warn (cancellation request, overdue invoice, contract with no renewal) sit in systems CS doesn't look at every day.

### Business rules

1. **A declared signal is worth more than an inferred one.** By default, the verdict uses what the customer or billing states: a cancellation or downgrade ticket or case, a subscription set not to renew (cancel_at_period_end in Stripe, non_renewing in Chargebee), an invoice overdue for more than N days and a contract expiring with no renewal negotiation open. Usage, logins and engagement are inferences and need proof. (Avoid: A usage and engagement score as the basis for risk.)
2. **Test each signal against real churns before using it.** For each signal, measure in how many of the last months' churns it was present before cancellation and in how many accounts that didn't cancel it also appeared. In one real base, the usage recency signal was recorded in under 1% of churns, response rate and activation in none, and those who had the signal were active in the 5 days before canceling. The usage axis was removed from the verdict. (Avoid: Choosing signals by intuition and never measuring accuracy.)
3. **No alert is not the same as healthy.** Call the no-problem state something like billing up to date, no exit request. Never report the share of the base in that state as the share that is healthy. A rule whose only possible outputs were healthy or great stamped most MRR as good with nothing that could contradict it. (Avoid: Saying 80% of the base is healthy because it has no alert.)
4. **Usage counts by trajectory, not by value.** If usage goes in, compare it with the account itself: a baseline of 6 full months before the current month (month -7 to month -2). Require a drop in both percentage and absolute terms at the same time (for example, -15% and at least 3 units), so small accounts don't alert on noise. Without a stored daily snapshot there is no trend: the product database often deletes old data. (Avoid: Alerting every account with adoption below 50%.)
5. **No source, no alert.** A null component due to missing data doesn't mean no risk, and zero engagement without a validated source doesn't mean risk. Mark the account as missing data for that component and don't raise an alert on it. (Avoid: Treating the absence of events as an idle customer.)
6. **In email, only the new text counts.** In the CRM, each email reply carries the entire quoted conversation. Before sending it to AI or counting signals, cut the history at the quote markers: "On ... wrote:", the Portuguese "Em ... escreveu:", the Portuguese Outlook header "De: ... Enviada em:", lines starting with ">" and nested quotes. Evidence only counts if it is in the new text; an exact duplicate in the same thread is not reanalyzed. (Avoid: Sending the full body of every email to the AI.)
7. **Universe: active paying accounts only.** The score covers accounts with MRR greater than zero, including those that will leave at the end of the cycle. Internal, test, demo and trial accounts are excluded. Delinquency is not inactivity: a delinquent customer who still has access stays in the base and comes in with the billing signal. (Avoid: Computing a score for trial, demo and internal accounts.)
8. **One row per account, with reason and next action.** The queue has the account, the severity, a readable reason (which signal fired and when), the MRR at risk, the CS owner and the next action. Sort by severity and MRR. A criterion that fills the queue on its own (for example, just going 30 days without a call) should come out of the OR and become a component. (Avoid: A 0 to 100 grade without saying why.)
9. **Versioned formula and severity history.** Record the formula version on each row and, when it changes, keep the original and the recalculated score. Maintain a transitions history (append only, 1 row when an account's severity changes) to count accounts and MRR at risk on any day and measure accuracy. (Avoid: Overwriting the score every day without keeping the previous one.)
10. **Early churn is a different queue.** A customer who cancels shortly after the sale (for example, within 45 days, or 90 days for large accounts) is a sales or implementation problem, not a CS one. Separate that reason so it doesn't mix with portfolio risk. (Avoid: Counting a first-month cancellation as a CS failure.)

### Risk signals and how much to trust them

Declarations and billing go in directly. Inferences go in only after being tested against real churns.

- **Cancellation or downgrade ticket or case** → Declaration: goes in directly
- **Subscription set not to renew** → Declaration: goes in directly (`cancel_at_period_end, non_renewing`)
- **Invoice overdue for more than N days** → Billing: goes in directly (`past_due, unpaid`)
- **Contract expires within 90 days with no renewal open** → Contract: goes in directly
- **Usage drop against the account's own baseline** → Inference: only with proof
- **Disconnected integration or invalid credential** → Inference: only with proof
- **Talk of leaving in an email or call** → Inference: only in the new text
- **Days without CS contact** → Component, never a standalone criterion

### Definitions

| Metric | Definition |
|---|---|
| Account in the universe | paying account with MRR > 0 on the day, including non-renewal, with no internal, test, demo or trial accounts |
| Severity | the account's risk level on the day (for example: no alert, attention, at risk, critical), computed by the current version's formula |
| MRR at risk | sum of current MRR for accounts with severity at risk or critical on the day |
| MRR in non-renewal | sum of MRR for subscriptions set to end at the end of the cycle |
| Overdue invoice | open invoice with a due date before the reference date by more than N days (for example, attention at 30 and at risk at 60) |
| Expiring contract | contract end date already past or within 90 days with no open renewal deal |
| Alert precision | accounts that entered at-risk in the period and canceled within 90 days / accounts that entered at-risk in the period |
| Alert coverage | churns in the period that were at risk before cancellation / churns in the period |

### Pitfalls

- **Usage gave false comfort.** In one real base, almost no churn had a recorded usage signal, and those that did were active days before leaving. The usage rule marked most MRR as healthy with no way to contradict it. How to avoid: Measure each signal against real churns and remove from the verdict anything that doesn't precede cancellation.
- **Quoted email inflated the signals.** More than 80% of email text was repeated history. The AI found the same statements again with every reply and churn signals came out nearly three times higher than reality. How to avoid: Send only the new text of each email and only accept evidence that is in it.
- **Incomplete quote stripping.** Even after stripping, about 7% of replies still carried history, because of the Portuguese Outlook header and nested quotes. How to avoid: Test the markers on a sample and include the Portuguese and nested formats.
- **One criterion filled the queue.** Going more than 30 days without a CS call, on its own, put accounts that were doing fine into the queue. How to avoid: Use contact inactivity as a component, never as a standalone criterion.
- **Null read as safe.** Accounts with no usage data showed no risk because the component came through null. How to avoid: Mark the component as missing data and show it on the account profile.
- **The score changed and so did the past.** The formula was adjusted and the risk history started counting something else, with no way to compare. How to avoid: Record the formula version and the severity transitions history.

Validated on real data: In the emails logged in Nekt's own HubSpot, 61% of the text is quoted history from earlier messages. In emails received from customers, 9 in 10 carry the previous conversation and 89% of the text is repeated. Without stripping the quotes, the same sentence from a customer would be counted with every reply in the thread.

### Acceptance test

- [ ] Every active paying account has exactly one row per day, and no internal, test or trial account appears.
- [ ] Every account with severity at risk or critical has at least one reason with a date and source.
- [ ] Every subscription set not to renew and every open cancellation ticket appears in the queue the next day.
- [ ] For the last 3 closed months, alert coverage (churns that were at risk before canceling) is measured and published alongside precision.
- [ ] No email evidence used by the score comes from quoted text of a previous message.

### Context document

```text
Health score and churn risk
- The universe is paying accounts with MRR > 0, including those that won't renew. Internal, test, demo and trial accounts are excluded.
- Risk comes first from declarations and billing: cancellation or downgrade ticket, subscription set not to renew, invoice overdue for more than <N> days, contract expiring within 90 days with no renewal open.
- Usage signals only count if they are in the current formula. Don't use usage to say an account is healthy.
- The no-alert state means billing up to date and no exit request. Never report it as a percentage of healthy customers.
- A component with no data is not an absence of risk. Say the data is missing.
- In emails, use only the new text of each message. Text quoted from earlier messages is not evidence.
- Always state the reason, the signal date and the MRR at risk for each account.
- Early churn (cancellation within <N> days of the sale) is analyzed separately, as a sales or implementation problem.
- For questions about the past, use the severity history, not today's table.
Per-client exceptions:
- <client>: annual contract with <N> days' notice; the notice counts as an exit request.
- <client>: account in implementation until <date>, outside the score.
```

### Variations

- **Points-based score:** Components summed into fixed bands (for example, 0 to 12 points in five bands), useful when CS wants to see each signal's weight.
- **AI over conversations:** Calls and emails classified by AI into risk, dissatisfaction and opportunity, using only new text and quoted evidence.
- **NRR forecast:** Notice and overdue invoice rules combined with a model, auditing the forecast rate against the actual every month.
