---
title: "How do you spot customers at risk of churning before renewal?"
blueprint: 014
slug: customer-health-score-churn-risk
category: Customer success
revised: 2026-09-10
platform: Coevera CRM (formerly Pipeliner CRM)
canonical: https://blueprints.coevera.com/blueprints/customer-health-score-churn-risk/
publisher: Coevera
customer_data: none
---

# How do you spot customers at risk of churning before renewal?

**Short answer.** Coevera has a **native health engine**, so the score is configuration rather
than a field somebody maintains. It is set up **per record sub-type**: a list of **indicator
rules** — each a field, an operator and a point score — resolves into a numeric score, one of four
**bands**, and a **trend**. A separate set of **critical indicators** scores `-1`, and because the
Critical band is also `-1`, one matched critical rule forces the worst band however well
everything else scored.

The health band is a legitimate process trigger — verified in production. But the design rule that
makes this work is: **let the score route attention, and let a person commit.** Health changes
notify the relationship owner; the owner records a verdict in a field of its own; and **the
verdict field** is what triggers the outreach.

The caveat: the score is **recalculated, not live**. Automation fires when the recalculation runs,
not when the underlying field moved.

## 01 · The business problem

A customer does not churn on renewal day. They churn over the preceding months, quietly, and the
renewal conversation is where you find out. By then the useful interventions — fixing the thing
that broke, training the team that never onboarded, reaching the sponsor who left — are all too
late to matter.

So the requirement is early warning that actually reaches somebody:

- a standing read on every customer, refreshed without anyone maintaining it;
- built from signals the customer generates, not opinions someone remembers to record;
- expressed so that **"why is this account amber?"** has an answer;
- that surfaces *to the person who owns the relationship*, not onto a dashboard;
- and that leads to a specific committed action, with a record that it happened.

The middle one is where most health scoring dies. A number nobody can explain gets argued with,
then ignored. The last one is where the rest dies: a signal that produces no obligation produces
no outcome.

## 02 · Why the obvious approach fails

### A health field somebody maintains

The first instinct is a dropdown — green, amber, red — that the account manager sets. It describes
the account manager's mood, decays the moment attention moves elsewhere, and is always most stale
on exactly the accounts nobody is watching. Which are the accounts at risk.

### A calculated or rollup field

The better instinct is to compute it: a formula or rollup field producing a number. This gets you
the arithmetic and none of the apparatus. There is no history, so you cannot see a decline; no
trend, so a 60 falling from 90 looks identical to a 60 climbing from 30; no attribution, so you
cannot say which input moved; and no bands, so every consumer of the number reinvents the
thresholds. You end up rebuilding the health engine badly, in a field.

### Wiring the intervention straight to the score

The most consequential mistake is architectural rather than mechanical: firing the customer
outreach directly off the score crossing a threshold. It is one process node away and it is wrong,
because **a score is a probabilistic signal and outreach is a commitment**. Three things follow
from wiring them together:

- **Data problems become customer contact.** An integration pauses, an indicator degrades
  fleet-wide, and the CRM emails half your customer base about a problem that does not exist.
- **Nobody owns the judgement.** When a score threshold sends the email, no human ever decided
  this account was at risk — so no human is accountable for what happens next.
- **The band oscillates.** A recalculation moves an account across a threshold and back, and each
  crossing is another trigger.

### A dashboard

A ranked at-risk list is genuinely useful and completely insufficient, because it relies on
somebody choosing to look. Health scoring earns its cost when it interrupts the right person; a
dashboard interrupts nobody.

## 03 · Data model

### One health configuration per record sub-type

Health is not a space-wide setting. A configuration record names an `entityType` *and* a `typeId`
— a specific record sub-type — so an account type that represents a paying customer is scored by
different rules and different bands than one representing a prospect. In one live space there are
**fifteen configurations on Account, one per account type, and exactly one is enabled.**

> **This is the fact that surprises people.** Enabling health is a per-sub-type decision, and the
> sub-types you are not scoring still carry a full configuration. Scoring a customer differently
> from a prospect is the point — but so is the maintenance consequence in §06.

### What a configuration holds

| Property | What it is |
|---|---|
| `categories` | The bands. Each has a label, an integer threshold and a colour. Four by default: Critical, Poor, Neutral, Good |
| `calculationType` | `Manual` — each rule carries points that sum — or `Priority` — rules are ordered and carry no points |
| `healthIndicators` | The scoring rules |
| `criticalHealthIndicators` | A **separate** rule set for disqualifying conditions |
| `isEnabled`, `lastRecalculation`, `calculationProgress` | Whether it runs, when it last ran, and how far a running recalculation has got |

### What each record gets

| On the record | Type | Notes |
|---|---|---|
| `healthStatus` | integer | The score. **Read-only** — the engine owns it |
| `healthCategory` | id | The resolved band. **Writable** — which matters in two ways, see §05 and §06 |
| `health` | object | `score`, `trend`, per-indicator results, `lastCalculation` |

The trend is a real enum — `Increasing`, `Decreasing`, `NoChange`, `Critical` — not something you
derive. And each indicator reports `Ok`, `Error` or `Critical` individually, with **the date it
last changed**, so "this account has been failing this check since March" is answerable from the
record.

### The history is the part worth knowing about

Health keeps a day-by-day history. Each entry carries the date, the score, the trend, and a list
of **changes** — and each change names a `scoreDifference`, the `ruleId` and the `fieldId` that
moved.

That is per-day attribution. It is the difference between "this account is at 55" and *"this
account dropped 25 points on the fourteenth when the licence-utilisation rule stopped matching"*.
It is also the answer to §01's hardest requirement, and it is the single strongest reason to use
the engine rather than compute a number in a field.

### The alternative that was rejected

Modelling health as custom fields — a score field, a band dropdown, a date-last-scored — is the
reflex. It reproduces the score and nothing else: no history, no trend, no per-indicator state, no
attribution, and no recalculation. Where a genuinely different structure *is* warranted is when
each health assessment must be a reviewable record with its own owner and lifecycle — a documented
periodic review rather than a standing signal. That is a related child record, and the framework
for making that call is [Blueprint
003](https://blueprints.coevera.com/blueprints/where-should-this-data-live/).

## 04 · Field-level configuration

### The anatomy of an indicator rule

| Property | Purpose |
|---|---|
| `field` | The field id being tested |
| `operator` | One of 23: `Is`, `IsNot`, `IsEmpty`, `IsNotEmpty`, `Less`, `LessOrEqual`, `More`, `MoreOrEqual`, `Between`, `BetweenNot`, `Contains`, `ContainsNot`, `Has`, `HasNot`, `RelativePeriod`, `RelativePeriodNot`, `In`, `InNot`, `StartsWith`, `EndsWith`, `IsActive`, `IsNotActive`, `Nop` |
| `values` | What it is tested against |
| `score` | Points awarded when the rule matches |
| `description` | **The rule in words.** Optional, and §06 is about what happens when it is left empty |

`RelativePeriod` is the one to reach for most often: it tests a date against a rolling window,
which is how "has synced in the last N days" or "created a record this quarter" is expressed
without a scheduled job maintaining a flag.

### Choosing indicators: signals, not opinions

The rule of thumb that survives contact with reality is that an indicator should be something
**the customer does**, not something a colleague records. A live configuration covers four
dimensions with six rules:

| Dimension | Indicator | Operator | Points |
|---|---|---|---|
| Are they using it? | Licence utilisation % | `More` | 25 |
| Is it still connected? | Date of last telemetry sync | `RelativePeriod` | 25 |
| Are they doing work in it? | Date the customer last created a record | `RelativePeriod` | 25 |
| Is support struggling? | Rollup of tickets open longer than two weeks | `Is` | 20 |
| **Critical indicators — each scoring `-1`** |  |  |  |
| Commercially intact? | Account status | `IsNot` | `-1` |
| Collapsed usage | Licence utilisation % below a floor | `Less` | `-1` |
| Paying? | Sent-to-collections flag | — | `-1` |

Note the shape rather than the specifics. Every weighted indicator is a fact about product usage
or service quality; every critical indicator is a fact about the commercial relationship. Note too
that **one field appears in both sets with different operators** — licence utilisation above a
target earns 25 points, and below a floor it disqualifies. That is the intended way to express a
metric with both a healthy range and a fatal one.

### The bands, and how critical short-circuits them

Categories carry an integer threshold, read as the ceiling of the band:

| Band | Weighted config | Priority config |
|---|---|---|
| Critical | `-1` | `-1` |
| Poor | 35 | 30 |
| Neutral | 75 | 80 |
| Good | 100 | 100 |

The elegant part is the Critical band sitting at `-1`. A critical indicator does not deduct points
— **it sets the score to `-1`**, which is below every other band's ceiling, so a single matched
critical rule lands the account in Critical no matter how well the weighted rules scored. An
account can be using every licence, syncing daily and raising no tickets, and still be Critical
because it went to collections. That is correct, and you get it without a single line of
automation.

Note also that those two live configurations band at **different thresholds** — 35/75 against
30/80. The same score is Poor under one sub-type and Neutral under another, which is a feature of
per-sub-type configuration and a trap for anyone comparing scores across types.

### Weighted or priority

| `calculationType` | How it resolves | Use when |
|---|---|---|
| `Manual` | Each rule carries points; matched points sum to the score | Several partial signals should compound — the usual case for a customer |
| `Priority` | Rules are ordered and carry `0` points; position decides the outcome | One dominant signal should settle it, with the rest as fallbacks |

In live use the weighted configurations carry explicit points on every rule and the priority ones
carry zero on all of them — which is how to tell at a glance which mode a configuration is really
using.

## 05 · Automation & logic

### Health is a trigger surface

`healthCategory` is an ordinary field as far as automation is concerned, so a change-triggered
process can watch it. Verified in production: a process triggers on Account update with **exactly
one watched field — the health category** — filters on renewal proximity, and emails the
relationship owner.

That process does one thing: it tells a human. It sends no customer-facing message and creates no
task on the customer's behalf. Which is the whole design.

### The two layers

|  | Layer 1 — the machine notices | Layer 2 — a person commits |
|---|---|---|
| Trigger | Health category changes | A **verdict field** changes |
| Written by | The health engine | The relationship owner, by hand |
| Action | Notify the owner. Nothing else | The full outreach chain |
| Meaning | "Worth a look" | "I have judged this account at risk" |

The verdict field is an ordinary custom radio — a small set of explicit outcomes such as *likely
to renew*, *at risk*, *expected loss*. It is what the account owner fills in after actually
looking. A production process watches that single field and, when it changes, emails the owner and
the relationship manager and then hands off to **three sub-processes** that create the follow-up
work.

That handoff pattern is not decoration — a process carries one filter whose branches are
first-match-wins, so independent consequences of one verdict have to be separate processes.
[Blueprint
011](https://blueprints.coevera.com/blueprints/keeping-hundreds-of-automations-maintainable/)
covers the constraint and the naming discipline that keeps such a chain readable.

> **Why the indirection is worth it.** The verdict field is a record that a named person assessed
> this account on this date and concluded something. That is auditable, reportable, and reviewable
> when the account churns anyway — none of which is true of a threshold crossing. It also means a
> paused integration degrades the *score* without generating a single customer-facing action.

### The safety net

Layer 1 only fires when the band *changes*. An account that has been Poor for four months never
triggers it again, which is exactly the account you most need to look at. So the pattern needs a
third piece: a **scheduled sweep** that finds accounts approaching renewal whose band is anything
but Good, or whose verdict field has not been touched in ninety days, and creates a review task.
Change-triggered automation catches deterioration; scheduled automation catches neglect. You need
both, and they fail differently.

### Recalculation, and what it means for timing

The score is not live. A configuration records `lastRecalculation` and a `calculationProgress`
percentage, and recalculation can be invoked for one record or for every record. So the causal
chain is:

`field changes` → `recalculation runs` → `score and band change` → `process fires`

Every timing assumption should be built on the second arrow, not the first. In the live space the
enabled configuration had recalculated the same morning it was read.

## 06 · Limits & trade-offs

> **The score cannot explain itself unless you make it.** Every indicator rule has a `description`
> field, and in the configuration examined **every description on every rule across all fifteen
> configurations was empty**. The apparatus for answering "why is this account Poor?" ships in the
> product and stays blank by default, leaving the answer only reachable by opening the
> configuration and resolving field identifiers by hand. Fill it in as you write each rule;
> nothing will ever prompt you.

- **The score is recalculated, not live.** Automation fires on recalculation, so "immediately when
  utilisation drops" is not available, and a same-day interpretation of a band change is only as
  good as the recalculation cadence.
- **An indicator on a replicated field turns an outage into a churn signal.** Where an indicator
  reads a field another system owns — a telemetry sync date, a subscription status — a stalled
  integration degrades that indicator across every account at once, and the engine cannot
  distinguish "the customer stopped using it" from "the pipe stopped delivering". This is a
  reasoned consequence of two verified facts, not an observed incident: live indicators do read
  replicated date fields, and [Blueprint
  009](https://blueprints.coevera.com/blueprints/automating-on-integration-owned-fields/)
  documents what a multi-day replication outage did in production. The mitigation is the same one
  that blueprint prescribes — a sync-freshness gate independent of the synced data — plus the §05
  rule that a score never triggers customer contact on its own.
- **The health category is writable.** Useful, because a person can override a band the engine got
  wrong — and hazardous, because a manually set band is indistinguishable from a computed one, and
  the next recalculation may overwrite it. If overrides matter, record them in a field of their
  own.
- **Configuration multiplies by sub-type.** Fifteen account types meant fifteen configurations,
  each with its own bands and rules, all but one switched off. Adding an indicator to "the
  customer health model" means editing the one that is enabled and remembering that the others
  have diverged — in the live space the weighted and priority configurations already band at
  different thresholds. There is no inheritance.
- **A rule can exist with no operator.** Observed on critical indicators in more than one
  configuration: a rule with a field, a score of `-1` and a null operator. What it evaluates to
  was not tested. Treat a null operator as a configuration defect and set it explicitly.
- **Scores need not reach the top of the band.** In the live weighted configuration the four
  indicators total 95 against a Good ceiling of 100, so a perfect account scores 95. That is
  harmless — the band is a ceiling, not a target — but it makes the raw score a poor thing to show
  anyone, and a worse thing to compare across sub-types.
- **Indicators from related records are unproven.** The configuration admits indicator rules drawn
  from a related entity across a lookup, and in the space examined that capability was unused —
  every live rule read the account's own fields. Schema-verified, not behaviour-verified. Where
  you need "no activity in 90 days", a rollup field on the account is the path that is known to
  work.
- **Only Account was observed carrying health.** The entity-type property admits others; every
  live configuration was on Account. Not tested elsewhere.

### The trade-off worth stating plainly

The engine buys history, trend, per-indicator state, per-day attribution and a band that
automation can watch — none of which you would build correctly by hand, and all of which arrive
from configuration alone. What it does not buy is a prediction. There is no model, no weighting
suggested for you, no learning from accounts that actually churned; the rules and the points are
your hypothesis about why customers leave, and the engine only applies them consistently. That is
a fair deal, provided the hypothesis gets revisited when it is wrong — which is what the history
is for, and the reason §05 insists a person's verdict, not the score, is what enters the record as
a decision.

## 07 · Verification

Read on 2026-09-10 from a live production space: the full health configuration set, the resolved
indicator fields, and the processes that consume the result.

- **Fifteen health configurations on Account**, one per account type, with exactly one enabled and
  carrying a recalculation timestamp from the morning of the read. The other fourteen were
  configured and switched off.
- **Both calculation modes were found in live use** — weighted configurations with explicit points
  on every rule, priority configurations with zero on all of them — and they band at different
  thresholds, 35/75 against 30/80.
- **The critical mechanism was confirmed from the data**: every critical rule in every
  configuration carries `score: -1`, and every configuration defines a Critical band at `-1`. That
  is what makes one critical indicator dominate the score.
- **Every indicator field id was resolved to its field**, which is where the four dimensions in
  §04 come from — utilisation, telemetry recency, customer-generated records, ageing support
  tickets, plus account status and a collections flag as critical rules. One field appears in both
  sets with opposite operators.
- **Health as a trigger was verified, not assumed.** A live enabled process triggers on Account
  update with a single watched field, and that field id resolves to the native health category. A
  second live process watches a single custom radio field — the verdict — and hands off to three
  sub-processes.
- **The 23 operators and the four trend values** were read from the schema, as were the per-record
  score, band and per-indicator state, and the history structure with its score-difference, rule
  and field attribution.
- **Every rule description was empty** across all fifteen configurations.

**Not observed, and stated as such:**

- A recalculation in progress, or the cadence on which it runs. Only that it had run, and that a
  progress percentage exists.
- What a rule with a null operator evaluates to.
- Indicators drawn from a related entity across a lookup — schema-verified, not
  behaviour-verified.
- Health on any entity other than Account.
- Whether a manually written health category survives the next recalculation.
- The precise resolution order in priority mode, beyond that rule order governs and points are
  zero.

### What would signal a regression

- **Many accounts changing band on the same day.** Genuine deterioration is not synchronised. A
  fleet-wide move means an indicator's input broke — most often a replicated field — and it will
  look like a churn wave.
- **A recalculation timestamp that stops advancing.** The score simply freezes; nothing fails, and
  every band on every record silently becomes an assertion about the past.
- **Band changes firing while verdict fields go untouched.** Layer 1 is working and layer 2 is
  not: people are being notified and nobody is deciding. This is the failure mode that looks
  healthiest from the automation side.
- **Accounts sitting in Poor for months without a review task.** The scheduled sweep has stopped,
  and change-triggered automation will never catch them, because their band is not changing.

## Related blueprints

- [Blueprint 009 — How do you automate on CRM fields that another system
  owns?](https://blueprints.coevera.com/blueprints/automating-on-integration-owned-fields/) —
  essential reading if any indicator reads a replicated field.
- [Blueprint 008 — How do you forecast renewals that don't exist as records
  yet?](https://blueprints.coevera.com/blueprints/forecasting-renewals-before-they-exist/) — the
  revenue side of the same renewal; health is the risk side.
- [Blueprint 011 — How do you keep hundreds of CRM automations
  maintainable?](https://blueprints.coevera.com/blueprints/keeping-hundreds-of-automations-maintainable/)
  — why one verdict fans out into several processes.
- [Blueprint 012 — How do you run a customer survey from your CRM and get the answers back onto
  the record?](https://blueprints.coevera.com/blueprints/customer-survey-answers-onto-the-record/)
  — how to get an answered question onto the record a health rule could read.
- [Blueprint 003 — How do you model something your CRM has no object
  for?](https://blueprints.coevera.com/blueprints/where-should-this-data-live/) — deciding between
  a standing signal and a reviewable assessment record.

---

Published by Coevera. Abstracted to the reusable pattern — no customer names, no client
data, no personal data.
