Estimate contract renewal risk category from customer health
This contract renewal risk estimator turns two readily available customer-success signals into a consistent low, medium, or high risk category.
Run — free
Enter a customer health score between 0 and 100 and the whole number of days remaining before renewal. The result follows a transparent threshold rule, making it suitable for triage, reporting, workflow routing, and repeatable portfolio reviews. It does not claim to predict an exact renewal probability; instead, it provides a simple operational category that teams can understand, audit, and apply consistently.
Understand the classification rule
The estimator evaluates customer health and renewal timing together. A result is high risk when the health score is below 50 or when the contract renews in 30 days or fewer. A result is medium risk when the high-risk rule does not apply, but the health score is below 75 or renewal is 90 days or fewer away. Only a customer with a health score of at least 75 and more than 90 days remaining receives a low-risk category. The more urgent signal always governs the result. For example, excellent health does not erase the operational urgency of a renewal next week, while a long runway does not hide a seriously weak health score. These thresholds deliberately form a small, explainable rule rather than a statistical model. Every result can be reproduced from the two inputs without hidden weights, historical training data, or subjective interpretation. That transparency makes the category especially useful as a shared first-pass signal across customer success, sales, finance, and leadership teams.
Prepare reliable inputs
Use a health score that already follows a stable 0-to-100 definition across the customers being compared. If one business unit treats product activity as the entire score while another heavily weights support sentiment, their resulting categories may look comparable even though the source measurements are not. Document the health-score method and refresh it on a predictable schedule. For renewal timing, provide the whole number of calendar days until the contractual renewal date. Zero means the renewal date is today. Negative values are rejected because they describe a renewal date that has already passed and therefore require a different workflow, such as overdue renewal handling or churn review. The estimator also rejects missing values, nonnumeric values, non-finite numbers, health scores outside the permitted range, and fractional day counts. These checks prevent ambiguous classifications and make automation failures visible. Before processing a portfolio, verify that the source system uses the correct renewal date, especially for amended contracts, early renewals, extensions, or agreements containing notice dates that differ from the actual renewal date.
Use the category as an operational signal
Treat the output as a prioritization aid, not as a guaranteed forecast or a substitute for account knowledge. High-risk customers may need an immediate review, a confirmed renewal plan, executive sponsorship, or remediation of unresolved adoption and support issues. Medium-risk customers often benefit from scheduled outreach, success-plan checks, and validation that commercial stakeholders are engaged before the renewal window becomes urgent. Low-risk customers still require normal renewal management; the category only indicates that neither threshold currently signals elevated risk. Because the rule is deterministic, teams can run it repeatedly and track category changes as health scores and renewal dates evolve. A move from low to medium may result simply from time passing, while a move from high to medium can reflect improved health after an intervention. Keep the two returned inputs alongside the category in reports so reviewers can understand why the rule produced its result. If your organization later develops a calibrated predictive model, compare its outcomes with this baseline instead of interpreting these categories as measured probabilities.
What you can do with it
Prioritize a renewal portfolio
Classify accounts consistently before a weekly customer-success review so the most urgent combinations of weak health and short renewal timing appear first.
Route workflow actions
Send high-risk results to immediate intervention, medium-risk results to planned outreach, and low-risk results to the standard renewal cadence.
Create an explainable baseline
Compare a future predictive model with a transparent threshold baseline whose result can always be reconstructed from two source values.
FAQ
Does this return an exact renewal probability?
No. It returns an operational risk category based on explicit thresholds, not a statistically calibrated probability percentage.
What creates a high-risk result?
A health score below 50 or 30 days or fewer until renewal creates a high-risk result. Either condition is sufficient.
What creates a medium-risk result?
When the high-risk rule does not apply, a health score below 75 or 90 days or fewer until renewal creates a medium-risk result.
Why are negative days rejected?
Negative days indicate that the renewal date has passed. That situation belongs in an overdue-renewal or churn workflow rather than a pre-renewal risk classification.
Can I use a fractional health score?
Yes. Any finite health score from 0 through 100 is accepted, including decimals. Days until renewal must be a whole number.
What does an API request cost?
Each API request costs $0.002. The same deterministic calculation can also run in the browser.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/biz/renewal-probability-estimate \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"health_score":82,"days_until_renewal":120}'const res = await fetch("https://api.kit.forhosting.com/biz/renewal-probability-estimate", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"health_score": 82,
"days_until_renewal": 120
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/biz/renewal-probability-estimate",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"health_score": 82,
"days_until_renewal": 120
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/biz/renewal-probability-estimate", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"health_score":82,"days_until_renewal":120}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"health_score":82,"days_until_renewal":120}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/biz/renewal-probability-estimate", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"health_score": 82,
"days_until_renewal": 120
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "biz.renewal_probability_estimate",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |