Template - Customer health
ScoreAccount health
How healthy a customer account is, from written account notes.
A score decision that turns free-text account notes and call summaries into a consistent three-level health rating.
The decision
How healthy is this customer account?
Levels, lowest to highest
- 0
At risk: Declining usage, lost champion, payment problems, or stated dissatisfaction. - 1
Needs attention: Mixed signals, or too little information to tell. - 2
Healthy: Growing or stable usage, engaged champion, renewals on track.
Evidence checks (asked in the same request)
- Usage declining: Product usage is declining or low.
- Cancellation intent: The customer says they intend to cancel or not renew.
Expected output: A score from 0 to 2, a probability for each level, and a confidence value, plus one probability per evidence check.
Input
The Decisions API reads one input. Combine these fields into it as labeled text so the model sees the facts the decision depends on.
| Field | Type | Required | Description |
|---|---|---|---|
notes | string | Yes | Account notes, a QBR summary, or a call recap. |
usage_summary | string | No | A sentence of usage data, such as "weekly active users down 20% in 60 days". |
Worked examples
Growing usage and a signed renewal
QBR notes: usage is up 30% quarter over quarter, they added 15 seats in May, and their champion is now VP of Support. Renewal signed for two years.
A careful reviewer would say: Healthy. Growth, expansion, a promoted champion, and a signed renewal.
Run this exampleFalling usage and a lost champion
Account notes: usage has dropped since April, only a few logins last month, their champion left the company, and there's an overdue invoice.
A careful reviewer would say: At risk. Every signal is negative: usage, engagement, sponsorship, and payment.
Run this example
Expected answers are human judgments written for this template, not model output.
How it works
Health scores built from usage metrics miss what account managers write down after calls. This template scores that text, so qualitative signals can sit beside the quantitative ones.
The middle level explicitly includes "too little information to tell". Thin notes should land there, not get a confident rating.
Human review and thresholds
Recommended review policy
Use the score to prioritize attention, not to take action on an account. A change from Healthy to At risk should prompt a person to look.
Choosing thresholds
Because no automated action follows, thresholds here decide what appears on someone's list. Set them by how many accounts your team can meaningfully review each week.
- Starting automation threshold
- 80%
- Starting don’t-decide threshold
- 50%
Starting points for the playground, not recommendations. The right values depend on your costs and on how the model’s confidence tracks accuracy on your data. Calibrate them.
Limitations
- Notes reflect the author's optimism. An account manager who writes "great call!" about every account will produce healthy scores.
- This rates the text, not the account. Combine it with usage and billing data before acting.
- Health levels are not forecasts. Do not present them as renewal probabilities.
Use it in your code
The template definition is plain JSON: the question, the evidence checks, the input fields, and the starting policy. The SDK call below sends the same question and checks to the Decisions API from your server.
{
"$format": "thethinkloop.template/v1",
"slug": "account-health",
"title": "Account health",
"decides": "How healthy a customer account is, from written account notes.",
"question": {
"name": "decision",
"type": "score",
"instructions": "How healthy is this customer account?",
"levels": [
{
"label": "At risk",
"description": "Declining usage, lost champion, payment problems, or stated dissatisfaction."
},
{
"label": "Needs attention",
"description": "Mixed signals, or too little information to tell."
},
{
"label": "Healthy",
"description": "Growing or stable usage, engaged champion, renewals on track."
}
]
},
"evidenceChecks": [
{
"name": "factor_1",
"label": "Usage declining",
"type": "predicate",
"instructions": "Product usage is declining or low."
},
{
"name": "factor_2",
"label": "Cancellation intent",
"type": "predicate",
"instructions": "The customer says they intend to cancel or not renew."
}
],
"inputs": [
{
"name": "notes",
"type": "string",
"required": true,
"description": "Account notes, a QBR summary, or a call recap."
},
{
"name": "usage_summary",
"type": "string",
"required": false,
"description": "A sentence of usage data, such as \"weekly active users down 20% in 60 days\"."
}
],
"expectedOutput": "A score from 0 to 2, a probability for each level, and a confidence value, plus one probability per evidence check.",
"examples": [
{
"title": "Growing usage and a signed renewal",
"input": "QBR notes: usage is up 30% quarter over quarter, they added 15 seats in May, and their champion is now VP of Support. Renewal signed for two years.",
"expected": "Healthy"
},
{
"title": "Falling usage and a lost champion",
"input": "Account notes: usage has dropped since April, only a few logins last month, their champion left the company, and there's an overdue invoice.",
"expected": "At risk"
}
],
"startingPolicy": {
"automateAt": 0.8,
"abstainBelow": 0.5,
"note": "A starting point for exploration, not a recommendation. Calibrate against your own labeled examples."
}
}import OpenAI from "openai"; // openai >= 7.30.0
const client = new OpenAI(); // reads OPENAI_API_KEY on the server
const decision = await client.decisions.create({
model: "gpt-6-luna",
input: "QBR notes: usage is up 30% quarter over quarter, they added 15 seats in May, and their champion is now VP of Support. Renewal signed for two years.",
questions: [
{
type: "score",
name: "decision",
instructions: "How healthy is this customer account?",
levels: [
{
label: "At risk",
description: "Declining usage, lost champion, payment problems, or stated dissatisfaction."
},
{
label: "Needs attention",
description: "Mixed signals, or too little information to tell."
},
{
label: "Healthy",
description: "Growing or stable usage, engaged champion, renewals on track."
}
]
},
{
type: "predicate",
name: "factor_1",
instructions: "Product usage is declining or low."
},
{
type: "predicate",
name: "factor_2",
instructions: "The customer says they intend to cancel or not renew."
}
],
});
// Answers come back in question order. A question can also be refused.
const [main, ...checks] = decision.answers;
if (main.type === "refusal") throw new Error("The model declined to answer.");
if (main.type === "score") {
console.log(main.score, main.confidence, main.probabilities);
}
Download it as account-health.json, or browse all developer resources.
Need this decision tuned to your policy?
Related
- Score template
Churn risk
How likely a customer is to leave, based on what they wrote.
- Score template
Customer priority
What priority a request should get, combining urgency with account context.
- Choice template
Customer message routing
Which team should own a customer message: sales, customer success, support, or billing.
- Guide
Predicate vs choice vs score
Most decisions can be phrased three ways, and each returns different information. Which type fits which decision, with worked examples and common mistakes.
- Guide
Human-in-the-loop AI
When should AI decide and when should it ask? Review patterns, what reviewers need to see, sizing the queue, and keeping oversight real.
- Guide
Calibration metrics
Three ways to measure calibration, what each one hides, and which to use for which question, with code that computes them from labeled decisions.