REST API reference
A standard JSON API over HTTPS. Use it to wire ChurnWin's churn data into any AI agent, internal tool, or automation that can make HTTP requests. All endpoints are read-only in v1.
Authentication
Pass your API key as a Bearer token on every request. Keys are created at Settings → Agent Access.
curl https://api.churnwin.com/api/v1/agent/metrics \
-H "Authorization: Bearer cwk_live_YOUR_KEY_HERE"Base URL: https://api.churnwin.com/api/v1/agent
OpenAPI spec: https://api.churnwin.com/openapi-agent.jsonThis machine-readable contract contains only the read-only agent endpoints documented below; it excludes ChurnWin's internal admin, auth, billing, settings, and webhook surfaces.
All endpoints accept an optional business=<id> query parameter to scope results to one connected Stripe account. Use the numeric id returned by GET /businesses.
Endpoints
Example email addresses use JSON's Unicode escape for the at sign. JSON parsers decode this escape as the @ character; keeping the escape in the HTML prevents edge email protection from rewriting otherwise valid code samples.
/metricsTop-level churn metrics for a period: churn rate, MRR, retention, and subscriber counts.
Query parameters
periodstringOne of 30d | 90d | 6m | 12m (default: 30d)businessintegerBusiness id from GET /businesses to scope results (not the Stripe acct_ id)Example response
{
"data": {
"period": "30d",
"churn_rate": 4.2,
"churn_rate_change": -1.3,
"mrr": 45200.0,
"mrr_change": 3.1,
"retention_rate": 95.8,
"retention_rate_change": 1.3,
"active_customers": 1042,
"active_customers_change": 2.4,
"active_subscribers": 1042,
"at_risk_count": 37,
"churned_count": 44,
"payment_failures": 12,
"voluntary_cancellations": 32,
"recovery_attempted_count": 12,
"recovery_pending_count": 3,
"recovery_resolved_count": 9,
"recovery_lost_count": 0,
"recovered_count": 9,
"recovered_mrr": 441.0,
"recovery_rate": 75.0,
"mrr_lost": 2156.0,
"at_risk_mrr": 1813.0,
"avg_mrr_per_subscriber": 43.4,
"new_subscribers": 61,
"long_standing": 780,
"total_churned": 512,
"avg_lifetime_days": 418.0
}
}/churn-trendDaily churn volume over the requested window. Useful for plotting or spotting spikes.
Query parameters
periodstringOne of 30d | 90d | 6m | 12m (default: 30d)businessintegerBusiness id from GET /businesses to scope results (not the Stripe acct_ id)Example response
{
"data": {
"period": "30d",
"data_points": [
{ "date": "2026-05-23", "churn_rate": 0.0, "churned_count": 0, "recovered_count": 0 },
{ "date": "2026-05-24", "churn_rate": 0.1, "churned_count": 1, "recovered_count": 0 },
{ "date": "2026-05-25", "churn_rate": 0.3, "churned_count": 3, "recovered_count": 1 }
]
}
}/churn-reasonsAggregated cancellation reason breakdown with counts and percentages.
Query parameters
periodstringOne of 30d | 90d | 6m | 12m (default: 30d)businessintegerBusiness id from GET /businesses to scope results (not the Stripe acct_ id)Example response
{
"data": [
{
"reason": "too_expensive",
"count": 18,
"by_account": [{ "connection_id": 1, "count": 18 }]
},
{
"reason": "missing_features",
"count": 11,
"by_account": [{ "connection_id": 1, "count": 11 }]
}
]
}/at-riskSubscribers ranked by rule-based risk score with the factors that scored them. Highest risk first.
Query parameters
limitintegerMax customers to return, 1-100 (default: 20)min_riskintegerOnly customers at or above this risk score, 0-100 (default: 70)businessintegerBusiness id from GET /businesses to scope results (not the Stripe acct_ id)Example response
{
"data": {
"customers": [
{
"id": "4812",
"name": "Acme Corp",
"email": "founder\u0040example.com",
"risk_score": 90,
"risk_factors": ["payment_failures", "pending_cancellation"],
"mrr": 99.0,
"status": "at_risk",
"last_activity": null
}
],
"total": 1
}
}/recent-churnMost recently churned customers, newest first.
Query parameters
limitintegerMax customers to return, 1-100 (default: 20)businessintegerBusiness id from GET /businesses to scope results (not the Stripe acct_ id)Example response
{
"data": {
"cancellations": [
{
"id": "1841",
"customer_name": "Acme Corp",
"customer_email": "user\u0040acme.com",
"event_type": "cancellation",
"mrr_lost": 49.0,
"reason": "too_expensive",
"cancelled_at": "2026-06-20T09:14:00",
"plan_name": "Starter",
"connection_id": 1
}
],
"total": 128
}
}/cancellation-feedbackVerbatim survey responses collected from churned customers.
Query parameters
limitintegerMax responses to return, 1-100 (default: 20)businessintegerBusiness id from GET /businesses to scope results (not the Stripe acct_ id)Example response
{
"data": [
{
"id": "1841",
"customer_name": "Acme Corp",
"customer_email": "user\u0040acme.com",
"reason": "too_expensive",
"comment": "Love the product but our budget was cut.",
"mrr_lost": 49.0,
"cancelled_at": "2026-06-18T14:22:00",
"connection_id": 1
}
]
}/action-planChurnWin's AI-generated list of prioritised retention actions for your account.
Query parameters
businessintegerBusiness id from GET /businesses to scope results (not the Stripe acct_ id)Example response
{
"data": [
{
"id": 12,
"connection_id": null,
"title": "Add a pause-subscription option",
"description": "23% of churned users cited needing a break, not cancellation.",
"impact": "high",
"category": "product",
"position": 1,
"is_completed": false,
"completed_at": null,
"generated_at": "2026-06-21T06:00:00"
}
]
}/feedback-performanceFeedback survey send, open, and response rates across all feedback sent.
Query parameters
businessintegerBusiness id from GET /businesses to scope results (not the Stripe acct_ id)Example response
{
"data": {
"total_sent": 88,
"total_responded": 37,
"total_opened": 52,
"total_clicked": 21,
"total_bounced": 2,
"response_rate": 42.0,
"open_rate": 59.1,
"click_rate": 23.9,
"top_reasons": [{ "reason": "too_expensive", "count": 14 }],
"average_rating": 3.4
}
}/customersFull customer roster filterable by status, searchable by email or name.
Query parameters
pageintegerPage number (default: 1)per_pageintegerResults per page, 1-100 (default: 20)statusstringFilter: active | churned | at_risksearchstringEmail or name substring searchbusinessintegerBusiness id from GET /businesses to scope results (not the Stripe acct_ id)Example response
{
"data": {
"customers": [
{
"id": "4812",
"stripe_customer_id": "cus_ABC123",
"email": "founder\u0040example.com",
"name": "Acme Corp",
"status": "at_risk",
"risk_score": 70,
"total_mrr": 99.0,
"lifetime_value": 1188.0,
"stripe_created_at": "2025-04-02T10:00:00",
"subscription_count": 1,
"created_at": "2026-01-14T08:30:00"
}
],
"total": 1042,
"page": 1,
"per_page": 25,
"pages": 42
}
}/businessesAll Stripe accounts connected to your ChurnWin workspace.
Example response
{
"data": [
{
"id": 1,
"name": "My SaaS",
"website_url": "https://mysaas.example",
"sync_status": "completed",
"last_synced_at": "2026-06-21T05:12:00",
"customers_synced": 1042,
"subscriptions_synced": 1103
}
]
}Errors
401Missing or invalid API key403Key exists but lacks permission for this resource404Resource not found429Rate limit exceeded — back off and retry500Internal error — contact ChurnWin supportGet your API key
Sign up free, connect Stripe, then mint a key at Settings → Agent Access. You're ready to query in under 2 minutes.
Developer overview
The pitch, what data is available, who it's for
MCP setup guide
Connect Claude, Cursor, or Windsurf in 2 min