CoeveraBlueprints

Blueprint 014 · Customer success

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

A native health-scoring engine, configured per record sub-type — and the reason the score should route attention rather than trigger the intervention.

Revised 2026-09-10Verified against a live health configuration in productionMarkdown twin ↓

The 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.

01The 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.

02Why 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.

03Data 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

PropertyWhat it is
categoriesThe bands. Each has a label, an integer threshold and a colour. Four by default: Critical, Poor, Neutral, Good
calculationTypeManual — each rule carries points that sum — or Priority — rules are ordered and carry no points
healthIndicatorsThe scoring rules
criticalHealthIndicatorsA separate rule set for disqualifying conditions
isEnabled, lastRecalculation, calculationProgressWhether it runs, when it last ran, and how far a running recalculation has got

What each record gets

On the recordTypeNotes
healthStatusintegerThe score. Read-only — the engine owns it
healthCategoryidThe resolved band. Writable — which matters in two ways, see §05 and §06
healthobjectscore, 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.

04Field-level configuration

The anatomy of an indicator rule

PropertyPurpose
fieldThe field id being tested
operatorOne 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
valuesWhat it is tested against
scorePoints awarded when the rule matches
descriptionThe 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:

DimensionIndicatorOperatorPoints
Are they using it?Licence utilisation %More25
Is it still connected?Date of last telemetry syncRelativePeriod25
Are they doing work in it?Date the customer last created a recordRelativePeriod25
Is support struggling?Rollup of tickets open longer than two weeksIs20
Critical indicators — each scoring -1
Commercially intact?Account statusIsNot-1
Collapsed usageLicence utilisation % below a floorLess-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:

BandWeighted configPriority config
Critical-1-1
Poor3530
Neutral7580
Good100100

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

calculationTypeHow it resolvesUse 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.

05Automation & 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 noticesLayer 2 — a person commits
TriggerHealth category changesA verdict field changes
Written byThe health engineThe relationship owner, by hand
ActionNotify the owner. Nothing elseThe 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 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 changesrecalculation runsscore and band changeprocess 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.

06Limits & 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 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.

07Verification

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.
Published by Coevera · abstracted to the pattern, no client dataBlueprint 014 · rev 2026-09-10