What it does
Churn is the number everyone quotes and few compute the same way twice. This skill fixes the definition: paying customers and MRR on the first day of the month, against what was canceled by the last day, with failed payments kept apart from people who chose to leave.
The half that changes decisions is the second one. Stripe stores the reason and comment a customer gives in the customer portal on the subscription itself, and the agent groups them, so you can tell a pricing month from an onboarding month.
How it works
1Find last month's cancellations
Page through
GET /v1/subscriptions?status=canceledand keep subscriptions whoseended_atfalls inside last month. Drop trials that ended without a single paid invoice: they never paid, so they are not churn.2Rebuild the starting base
List subscriptions with
status=alland keep the ones that were paying on the 1st:start_datebefore it, out of trial by then, and not ended before it. Count distinct customers and their MRR, normalized to a month after discounts.3Read the reasons
From each canceled subscription take
cancellation_details.reason(cancellation_requested,payment_failed,payment_disputed),cancellation_details.feedback(too_expensive,missing_features,switched_service,unused,too_complex,low_quality,customer_service,other) and the free-textcomment.- 4
4Compute both rates
Customer churn is customers with no active subscription left at month end divided by paying customers on the 1st. Revenue churn is the MRR of canceled subscriptions divided by MRR on the 1st. Keep voluntary and
payment_failedapart. - 5
5Write the report
Both rates next to the month before, churned MRR, the reasons ranked by MRR lost, and up to three customer comments that say something specific.
Post it to the board
With
BRIEFKIN_TOKENset, the agent sends the result as one card that updates in place every run. Without it, you get the report in the chat.
What you get
- Revenue churn and customer churn for last month, with the month before.
- Churned MRR and the number of customers lost, voluntary and involuntary.
- Reasons ranked by MRR lost.
- Up to three comments worth reading, quoted as written.
- One line on the pattern, for example "Three of four too_expensive cancellations were Starter accounts in their second month."
Install and run it
Pick your agent, where the data comes from and how to schedule it; the commands below follow.
Install the skill
One command. It puts the SKILL.md in ~/.claude/skills and in ~/.agents/skills, which Codex, Cursor, Gemini CLI, Copilot CLI and OpenCode read too.
shell
curl -fsSL https://briefkin.dev/skills/stripe-monthly-churn-report/install | shRun it
Ask Claude Code:
prompt
Run the stripe-monthly-churn-report skillOr paste the prompt instead of installing:
prompt
Using my Stripe account (read only), compute last month's churn. Base: customers and MRR (monthly-normalized, after discounts, no trials) paying on the 1st of last month. Churned: subscriptions with status canceled whose ended_at falls in last month, leaving out trials that never paid. Give me customer churn (customers with nothing active left at month end divided by the base) and revenue churn (MRR of the canceled subscriptions divided by base MRR), split into voluntary and payment_failed. Group cancellations by cancellation_details.feedback, ranked by MRR lost, and quote up to three specific comments. Compare both rates with the month before.Put it on a schedule
1st of the month at 9:00, from cron on any machine where Claude Code is signed in:
crontab
0 9 1 * * claude -p "Run the stripe-monthly-churn-report skill"Add it with
crontab -e. A laptop asleep at that time runs it when it wakes if you use launchd or Task Scheduler instead; GitHub Actions works for the API route.Send it to your board
Connect Briefkin once with
curl -fsSL https://briefkin.dev/install | shand every run lands on your board as a stat tile. How setup works
View the SKILL.mdRaw file
SKILL.md---
name: stripe-monthly-churn-report
description: "On the 1st: last month's customer churn and revenue churn from Stripe, split by the reason customers gave when they canceled. Use when the user asks for this, or when it runs on a schedule."
---
# Stripe monthly churn report
On the 1st: last month's customer churn and revenue churn from Stripe, split by the reason customers gave when they canceled.
Churn is the number everyone quotes and few compute the same way twice. This skill fixes the definition: paying customers and MRR on the first day of the month, against what was canceled by the last day, with failed payments kept apart from people who chose to leave.
The half that changes decisions is the second one. Stripe stores the reason and comment a customer gives in the customer portal on the subscription itself, and the agent groups them, so you can tell a pricing month from an onboarding month.
## Two ways to get the data
Use the API or MCP route when you have an MCP server, a CLI or an API token for the app. Otherwise, if you can drive a browser the user is signed in to (Claude in Chrome, a Playwright or Chrome DevTools MCP server), use the browser route. Say which one you used.
## Route A: API or MCP server
### What you need
- A restricted Stripe key that reads Subscriptions, Customers and Prices, in `STRIPE_API_KEY`, or the Stripe MCP server.
- Cancellation reasons turned on in the Stripe customer portal settings, so there is feedback to group.
If something is missing, say what and stop; do not invent numbers.
### Steps
1. **Find last month's cancellations** (Stripe): Page through `GET /v1/subscriptions?status=canceled` and keep subscriptions whose `ended_at` falls inside last month. Drop trials that ended without a single paid invoice: they never paid, so they are not churn.
2. **Rebuild the starting base** (Stripe): List subscriptions with `status=all` and keep the ones that were paying on the 1st: `start_date` before it, out of trial by then, and not ended before it. Count distinct customers and their MRR, normalized to a month after discounts.
3. **Read the reasons** (Stripe): From each canceled subscription take `cancellation_details.reason` (`cancellation_requested`, `payment_failed`, `payment_disputed`), `cancellation_details.feedback` (`too_expensive`, `missing_features`, `switched_service`, `unused`, `too_complex`, `low_quality`, `customer_service`, `other`) and the free-text `comment`.
4. **Compute both rates**: Customer churn is customers with no active subscription left at month end divided by paying customers on the 1st. Revenue churn is the MRR of canceled subscriptions divided by MRR on the 1st. Keep voluntary and `payment_failed` apart.
5. **Write the report**: Both rates next to the month before, churned MRR, the reasons ranked by MRR lost, and up to three customer comments that say something specific.
## Route B: the browser
### What you need
- Signed in to the Stripe Dashboard with the Analyst role or higher, live mode on, and cancellation reasons turned on in the customer portal settings so there is feedback to read.
If something is missing, say what and stop; do not invent numbers.
### Steps
Start at https://dashboard.stripe.com/billing. The user is signed in already; never type passwords or codes, and ask if a sign-in page appears.
1. **Read churned MRR for last month**: Open `https://dashboard.stripe.com/billing` in live mode and go to the **Churn** page. Set the date range to last month. Click **Explore** on **Churned revenue**: the table separates churned MRR from contraction MRR, and clicking the churn cell lists the customers who canceled. Take the churned MRR and the customer count, leaving contraction out.
2. **Read the base on the 1st**: On the **Revenue** page set the range to end on the 1st of last month and read **MRR** for that day; on the **Subscribers** page read **Active subscribers** for the same day. Those are the base for both rates.
3. **List the cancellations with their reasons**: Open `https://dashboard.stripe.com/subscriptions`, filter **Status** to **Canceled** and narrow to subscriptions that ended last month (**Export** the list and filter the CSV by its canceled-at column when the filter does not go by end date). Open each subscription: the page shows the cancellation reason, and the feedback and comment the customer left in the customer portal when there is one. Skip trials that never paid.
4. **Compute both rates**: Customer churn is churned customers divided by active subscribers on the 1st; revenue churn is churned MRR divided by MRR on the 1st. Keep cancellations caused by failed payments apart from voluntary ones, and group the rest by feedback, ranked by MRR lost.
5. **Compare and write**: Shift the Churn page range to the month before for the comparison, then write both rates, churned MRR, the reasons table and up to three specific comments.
Note: Stripe's headline Subscriber churn rate is a trailing 30-day figure that adds new subscribers to the base, and its Churned revenue includes downgrades, so read the Explore split rather than the headline. Feedback only exists for cancellations made in the customer portal or passed by your app; dashboard cancellations come through blank. Past a hundred cancellations a month, the subscription-by-subscription reading is slow and the export is the way.
## The report
- **Revenue churn** and **customer churn** for last month, with the month before.
- **Churned MRR** and the number of customers lost, voluntary and involuntary.
- **Reasons** ranked by MRR lost.
- Up to three comments worth reading, quoted as written.
- One line on the pattern, for example "Three of four too_expensive cancellations were Starter accounts in their second month."
## Post it to Briefkin
When `BRIEFKIN_TOKEN` is set, or `~/.config/briefkin/env` exists (the installer from https://briefkin.dev/docs writes it), also send the result to the user's Briefkin board as one card. Keep the `item_key` the same on every run so the card updates in place, use a new `event_id` per run, and put today's real numbers in place of the example ones. If the request fails, say so once and carry on.
```bash
[ -n "${BRIEFKIN_TOKEN:-}" ] || . ~/.config/briefkin/env
curl -sS -X POST "${BRIEFKIN_URL:-https://briefkin.dev}/v1/events" \
-H "authorization: Bearer $BRIEFKIN_TOKEN" \
-H "content-type: application/json" \
-d @- <<'JSON'
{
"v": 1,
"event_id": "stripe-monthly-churn-report/<date of this run, e.g. 2026-10-16>",
"item_key": "stripe-monthly-churn-report",
"project": "Acme",
"source": "Stripe monthly churn report skill",
"kind": "metric",
"title": "September revenue churn: 3.1%",
"metric": {
"label": "Revenue churn",
"value": 3.1,
"unit": "%",
"better": "lower",
"period_start": "<start of the period, ISO 8601>",
"period_end": "<end of the period, ISO 8601>"
},
"glance": [
{
"type": "Grid",
"props": {
"columns": 3
},
"children": [
{
"type": "Stat",
"props": {
"label": "Customer churn",
"value": 2.4,
"unit": "%",
"better": "lower"
}
},
{
"type": "Stat",
"props": {
"label": "Churned MRR",
"value": 1402,
"prefix": "$",
"better": "lower"
}
},
{
"type": "Stat",
"props": {
"label": "Customers lost",
"value": 14,
"better": "lower"
}
}
]
}
],
"widget": [
{
"type": "Table",
"props": {
"columns": [
"Reason",
"Customers",
"MRR lost"
],
"align": [
"left",
"right",
"right"
],
"rows": [
[
"switched_service",
2,
"$460"
],
[
"too_expensive",
4,
"$318"
],
[
"unused",
3,
"$244"
],
[
"missing_features",
2,
"$210"
],
[
"payment_failed",
3,
"$170"
]
]
}
},
{
"type": "Text",
"props": {
"text": "Base on Sep 1: 580 paying customers, $45,200 MRR.",
"variant": "muted"
}
},
{
"type": "Callout",
"props": {
"text": "Both switched_service comments mention bulk export, and they were the two largest accounts lost.",
"tone": "info"
}
}
],
"size": "md"
}
JSON
```
Widgets come from the Briefkin catalog (https://briefkin.dev/v1/catalog). Without a token, just give the user the report.
Set `period_start` and `period_end` to the window the number covers: the tile names it from them ("Last 7 days", "Today", "This month"). For a count right now (open items, a balance), send `"period_label": ""` so no period shows; for another name, send it as `period_label` (at most 24 characters).
## Run it on a schedule
Suggested: 1st of the month at 9:00 (`0 9 1 * *`). From cron, with the agent signed in on that machine:
```bash
0 9 1 * * claude -p "Run the stripe-monthly-churn-report skill"
# Codex: codex exec, Gemini CLI: gemini -p, Copilot CLI: copilot -p, OpenCode: opencode run
```
Or use the agent's own scheduler (Claude Code routines, Codex automations, Claude or ChatGPT scheduled tasks): https://briefkin.dev/skills/stripe-monthly-churn-report#schedule
Source: https://briefkin.dev/skills/stripe-monthly-churn-report
What your agent needs
A restricted Stripe key that reads Subscriptions, Customers and Prices, in
STRIPE_API_KEY, or the Stripe MCP server.Cancellation reasons turned on in the Stripe customer portal settings, so there is feedback to group.
Connecting Stripe
- Stripe MCP server, Stripe's official remote server at
https://mcp.stripe.com. Add Stripe from Claude's connector directory, or runclaude mcp add --transport http stripe https://mcp.stripe.com/, and sign in with OAuth; clients without OAuth can send a restricted key as a Bearer token. - The Stripe CLI. Run
stripe loginonce, thenstripe subscriptions list --status active --limit 100orstripe invoices list --status open. The CLI defaults to test mode, so add--livefor real data. - API with a restricted key. Create a restricted key (
rk_live_...) in the Dashboard under Developers > API keys, store it inSTRIPE_API_KEY, and give it Read on the resources the report touches: Subscriptions, Invoices, Charges, Customers, PaymentIntents, Balance and Disputes.
Never hand an agent the secret key (sk_live_...); a restricted key with Read where needed and None everywhere else can't refund, cancel or charge anything.
Questions
Are downgrades included in revenue churn?
No, this is churn from cancellations only. Downgrades are contraction and belong in the weekly MRR report. For gross revenue churn including contraction, ask for it and the agent adds downgrades from the month's customer.subscription.updated events.
Why do so many cancellations have no reason?
Stripe records feedback only when the customer cancels in the customer portal with cancellation reasons turned on, or when your app passes cancellation_details on the cancel call. Cancellations made in the dashboard or caused by failed payments come through blank.
Do I need Briefkin to use the Stripe monthly churn report skill?
No. The skill runs with your own agent and gives you the report in the chat. With a Briefkin token set, each run also posts it to your board as a stat tile, so you can glance at it instead of asking again.
Which agents can run it?
Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot CLI, OpenCode, Windsurf and Amp all read SKILL.md files. The install command puts it in ~/.claude/skills and ~/.agents/skills, where they look. ChatGPT, the Claude app and Grok take the prompt instead. Pick your agent under "Install and run it" and the commands change to match.
Can it work without an API or MCP server, from my browser?
Yes. Switch the data source to "Your browser" and the steps change to the Stripe pages themselves: the agent opens dashboard.stripe.com/billing in a browser you are signed in to and reads the numbers off the page. Claude Code, Codex, Cursor, Claude app and ChatGPT can drive a browser out of the box; Gemini CLI, Copilot CLI and OpenCode need a Playwright or Chrome DevTools MCP server first. It is slower than the API and needs you signed in, so it suits a report you run by hand or from a scheduler on your own computer.