How to Track Customer Health Score in DenchClaw
Learn how to build and track customer health scores in DenchClaw CRM using product usage, support signals, and engagement data to predict churn.
How to Track Customer Health Score in DenchClaw
Customer health scores are the closest thing SaaS companies have to a churn crystal ball. When you're tracking the right signals — product usage, support volume, NPS, engagement — you can see an account going sideways 60-90 days before they actually cancel. That's enough time to intervene.
DenchClaw lets you build a custom health scoring model in your local CRM, store scores in DuckDB alongside all your other customer data, and have your AI agent surface at-risk accounts before your team is even awake.
What Goes Into a Health Score#
There's no universal health score formula — it depends on your product and business model. But the common signal categories are:
- Product usage — login frequency, feature adoption, session depth
- Support signals — ticket volume, unresolved issues, escalations
- Relationship health — NPS/CSAT, QBR completion, executive sponsor engagement
- Financial signals — overdue invoices, downgrades, payment failures
- Engagement signals — email open rates, webinar attendance, community activity
In DenchClaw, each of these becomes fields on your accounts object — and the health score is a calculated number derived from them.
Step 1: Define Your Health Score Model#
Start by deciding what signals matter for your business and how to weight them. A simple model:
| Signal | Weight |
|---|---|
| Sessions last 30 days | 30% |
| Feature adoption score | 25% |
| Days since last support escalation | 20% |
| NPS score | 15% |
| Contract payment status | 10% |
Simpler is better when you're starting out. You can always refine the model later.
Step 2: Add Health Score Fields to Your Accounts Object#
Tell the agent:
"Add these fields to the company object: Health Score (number 0-100), Health Tier (enum: Green/Yellow/Red), Last Health Update (date), NPS Score (number), Support Tickets Open (number), Feature Adoption Score (number 0-100), Sessions Last 30d (number), Payment Status (enum: Current/Overdue/Failed)"
Your .object.yaml additions:
- name: Health Score
type: number
- name: Health Tier
type: enum
options: [Green, Yellow, Red]
- name: Last Health Update
type: date
- name: NPS Score
type: number
- name: Support Tickets Open
type: number
- name: Feature Adoption Score
type: number
- name: Sessions Last 30d
type: number
- name: Payment Status
type: enum
options: [Current, Overdue, Failed]Step 3: Build the Scoring Formula#
The health score calculation runs as a SQL query in DuckDB. Here's a working example for the 5-signal model above:
-- Calculate health score for all accounts
WITH signal_values AS (
SELECT
e.id as entry_id,
MAX(CASE WHEN f.name = 'Sessions Last 30d' THEN ef.value::int ELSE 0 END) as sessions,
MAX(CASE WHEN f.name = 'Feature Adoption Score' THEN ef.value::int ELSE 0 END) as adoption,
MAX(CASE WHEN f.name = 'Support Tickets Open' THEN ef.value::int ELSE 0 END) as tickets,
MAX(CASE WHEN f.name = 'NPS Score' THEN ef.value::int ELSE 0 END) as nps,
MAX(CASE WHEN f.name = 'Payment Status' THEN ef.value ELSE 'Current' END) as payment
FROM entries e
JOIN entry_fields ef ON e.id = ef.entry_id
JOIN fields f ON ef.field_id = f.id
WHERE e.object_id = (SELECT id FROM objects WHERE name = 'company')
GROUP BY e.id
),
scored AS (
SELECT
entry_id,
-- Sessions: 0 = 0pts, 1-5 = 15pts, 6-15 = 25pts, 16+ = 30pts
CASE
WHEN sessions = 0 THEN 0
WHEN sessions <= 5 THEN 15
WHEN sessions <= 15 THEN 25
ELSE 30
END +
-- Adoption score: 0-100 scaled to 0-25
LEAST(25, adoption * 25 / 100) +
-- Tickets: 0 = 20pts, 1-2 = 15pts, 3-5 = 8pts, 6+ = 0pts
CASE
WHEN tickets = 0 THEN 20
WHEN tickets <= 2 THEN 15
WHEN tickets <= 5 THEN 8
ELSE 0
END +
-- NPS: <0 = 0pts, 0-6 = 5pts, 7-8 = 10pts, 9-10 = 15pts
CASE
WHEN nps < 0 THEN 0
WHEN nps <= 6 THEN 5
WHEN nps <= 8 THEN 10
ELSE 15
END +
-- Payment: Current = 10pts, Overdue = 3pts, Failed = 0pts
CASE payment
WHEN 'Current' THEN 10
WHEN 'Overdue' THEN 3
ELSE 0
END as health_score
FROM signal_values
)
SELECT entry_id, health_score,
CASE
WHEN health_score >= 70 THEN 'Green'
WHEN health_score >= 40 THEN 'Yellow'
ELSE 'Red'
END as health_tier
FROM scored;Step 4: Automate Score Recalculation#
Tell the agent to run the scoring query automatically:
"Every morning at 7am, recalculate health scores for all accounts using the scoring formula, update the Health Score and Health Tier fields, update the Last Health Update field to today, and message me if any account moved from Green to Yellow or Yellow to Red"
The agent creates a daily cron job. You'll get a Telegram message like:
"⚠️ Health score changes overnight:
- Acme Corp: Green → Yellow (sessions dropped to 3 last 30d)
- Beta Inc: Yellow → Red (2 open tickets + payment overdue)"
Step 5: Build Health Score Views#
Create views organized by health tier:
Red Accounts (Intervention Needed)#
- name: Red Accounts
filters:
- field: Health Tier
operator: equals
value: Red
sort:
- field: Contract Value
direction: descRecently Declined Accounts#
Ask the agent: "Create a view showing accounts whose health score dropped by more than 15 points in the last 7 days"
For this, you'll need to store previous health scores — add a Previous Health Score field and a Score Delta field that the cron job updates each run.
Green VIPs (Expansion Targets)#
- name: Healthy High Value
filters:
- field: Health Tier
operator: equals
value: Green
- field: Contract Value
operator: greater_than
value: 25000
sort:
- field: Health Score
direction: descStep 6: Build the Health Score Dashboard#
Ask the agent to build a Dench App:
"Build a health score dashboard showing: accounts by health tier (donut chart), health score distribution (histogram), top 5 at-risk accounts by contract value, and health tier breakdown by industry segment"
// Health tier distribution
const tiers = await dench.db.query(`
SELECT ef.value as tier, COUNT(*) as count,
SUM(cv.value::decimal) as arr
FROM entry_fields ef
JOIN fields f ON ef.field_id = f.id AND f.name = 'Health Tier'
JOIN entry_fields cv ON ef.entry_id = cv.entry_id
JOIN fields fcv ON cv.field_id = fcv.id AND fcv.name = 'Contract Value'
GROUP BY ef.value
`);The dashboard gives your CS team an at-a-glance view of portfolio health without running queries.
Step 7: Health Score-Triggered Workflows#
Use health scores to trigger customer success playbooks:
"When an account drops to Red tier, create a task in my tasks object: 'CS intervention needed for [Account Name]' with a due date of 48 hours from now, and notify the account owner via Telegram"
"When an account in Red tier hasn't had an outreach logged in 7 days, send me a reminder with the account details and their last interaction date"
This closes the loop from data → alert → action.
Keeping Your Model Honest#
A common mistake: building a health score model and never validating it. Every quarter, look at accounts that churned and check what their health scores looked like 90 days prior. If your Red accounts are still churning but Yellow accounts are too, your thresholds need adjustment.
Ask the agent: "Show me the health scores of the 5 accounts that churned this quarter, as of 90 days before their churn date"
This kind of lookback analysis is trivial with DuckDB and a timestamped health history — another reason to track Last Health Update and consider keeping a health_history table over time.
Frequently Asked Questions#
What's a good starting health score threshold for Green/Yellow/Red?#
A common starting point: 70+ = Green, 40-69 = Yellow, under 40 = Red. But your business may be different. Run your model on historical data first, check which threshold would have flagged your actual churned accounts, and adjust accordingly.
How often should health scores be recalculated?#
Daily is sufficient for most teams. Real-time scoring adds complexity without proportional benefit — most customer health signals don't change hour by hour. Daily recalculation catches meaningful trend shifts quickly enough to act.
Can I track health score history over time?#
Yes. Create a health_history table in DuckDB and INSERT a new row each time scores are recalculated. This gives you trend lines — you can track whether an account's health is improving or deteriorating over weeks, not just its current state.
What if I don't have NPS data for all accounts?#
Default missing values to a neutral score (e.g., treat unknown NPS as 7 — middle of the range). Flag accounts with no NPS data as "survey needed." Don't penalize accounts for missing data, but do track which accounts need a survey.
Can DenchClaw calculate health scores without product usage data?#
Yes, though the score will be less predictive. Even with just support ticket volume and payment status, you can build a meaningful first version. Add usage signals when you can pipe them in.
Ready to try DenchClaw? Install in one command: npx denchclaw. Full setup guide →
