Churn
Get Customer Churn Score
Retrieve the churn risk score and contributing factors for a specific customer.
GET
/
v1
/
customers
/
{id}
/
churn
Churn risk score for a customer
curl --request GET \
--url https://billing.example.com/v1/customers/{id}/churn \
--header 'Authorization: Bearer <token>'import requests
url = "https://billing.example.com/v1/customers/{id}/churn"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://billing.example.com/v1/customers/{id}/churn', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://billing.example.com/v1/customers/{id}/churn",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://billing.example.com/v1/customers/{id}/churn"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://billing.example.com/v1/customers/{id}/churn")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.example.com/v1/customers/{id}/churn")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"score": 50,
"risk_level": "low",
"features": {
"days_since_signup": 123,
"total_invoices": 123,
"failed_invoices_90d": 123,
"payment_failure_rate": 123,
"avg_days_to_pay": 123,
"plan_downgrades": 123,
"months_active": 123,
"current_mrr": 123,
"usage_trend": 123
},
"model_version": "<string>"
}
}{
"error": {
"code": "BAD_REQUEST",
"message": "<string>"
}
}{
"error": {
"code": "BAD_REQUEST",
"message": "<string>"
}
}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The unique identifier of the customer (e.g. cust_a1b2c3d4-e5f6-7890-abcd-ef1234567890). |
Example Request
curl https://api.recurso.dev/v1/customers/cust_a1b2c3d4-e5f6-7890-abcd-ef1234567890/churn \
-H "Authorization: Bearer rsk_live_xxx"
Response
Returns the churn risk assessment for the customer.{
"customer_id": "cust_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"score": 0.73,
"risk_level": "high",
"risk_factors": [
"3 failed payment attempts in last 30 days",
"Support ticket volume increased 200% month-over-month",
"Usage dropped 65% compared to 90-day average",
"Downgraded plan from Enterprise to Pro 14 days ago"
],
"recommendations": [
"Reach out via customer success team within 48 hours",
"Offer a 20% retention discount for the next 3 billing cycles",
"Schedule a product feedback call to address usage concerns",
"Review and resolve open support tickets as priority"
],
"model_version": "v2.4.1",
"computed_at": "2026-06-23T10:15:30Z"
}
The
score ranges from 0.0 (no churn risk) to 1.0 (highest churn risk).
Customers with a score above 0.7 are classified as high risk, between 0.4
and 0.7 as medium, and below 0.4 as low.Churn scores require at least 30 days of customer activity data. New customers
without sufficient history will return a score of
null with risk_level
set to insufficient_data.⌘I
Churn risk score for a customer
curl --request GET \
--url https://billing.example.com/v1/customers/{id}/churn \
--header 'Authorization: Bearer <token>'import requests
url = "https://billing.example.com/v1/customers/{id}/churn"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://billing.example.com/v1/customers/{id}/churn', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://billing.example.com/v1/customers/{id}/churn",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://billing.example.com/v1/customers/{id}/churn"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://billing.example.com/v1/customers/{id}/churn")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.example.com/v1/customers/{id}/churn")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"score": 50,
"risk_level": "low",
"features": {
"days_since_signup": 123,
"total_invoices": 123,
"failed_invoices_90d": 123,
"payment_failure_rate": 123,
"avg_days_to_pay": 123,
"plan_downgrades": 123,
"months_active": 123,
"current_mrr": 123,
"usage_trend": 123
},
"model_version": "<string>"
}
}{
"error": {
"code": "BAD_REQUEST",
"message": "<string>"
}
}{
"error": {
"code": "BAD_REQUEST",
"message": "<string>"
}
}