xmetric / Use cases

Cohort retention from a Stripe payments export

Group customers by the month of their first paid charge, then count how many paid again in each later month. With Stripe's payments CSV that takes three columns: Created date (UTC), Customer ID and Status. Drop your export below and the table builds in your browser, or copy the Tableau formula, the SQL or the agent prompt.

Drop your Stripe export

Monthly cohort retention, from a sample export

Each row is a group of customers by the month of their first paid charge. Each cell is the share of that group who paid again that many months later. Built from a synthetic Stripe payments export (252 rows, 72 customers, the real export's 23 column names).

First paid monthCustomersMonth 0Month 1Month 2Month 3Month 4Month 5Month 6Month 7Month 8
2026-0112100%75%58%42%33%25%25%25%17%
2026-0212100%75%50%42%33%33%8%17%
2026-0312100%50%33%33%25%17%17%
2026-0412100%83%75%58%42%42%
2026-0512100%75%50%33%33%
2026-0612100%58%50%42%

Drop your export here

Use the payments CSV from your Stripe Dashboard. xmetric finds the Created date (UTC), Customer ID and Status columns, keeps rows whose status is Paid, and rebuilds this table from your rows in this browser. Nothing is uploaded.

No file loaded yet. You can also drop the CSV on this box.

Export the payments CSV from Stripe

  1. In the Stripe Dashboard, open the Transactions (Payments) page.
  2. Click Export.
  3. Pick the date range and time zone. The default columns include everything this page needs.
  4. Download the CSV and drop it in the box above.

Steps from Stripe's support page, checked 2026-10-17. If you chose custom columns, pick the date, customer and status columns in the box.

The calculation in Tableau

Three calculated fields. Put Cohort on Rows, Months since first payment on Columns and Retention on Text, with Retention computed along Months since first payment.

// Cohort
DATETRUNC('month', {FIXED [Customer ID] : MIN(IF [Status] = "Paid" THEN [Created date (UTC)] END)})

// Months since first payment
DATEDIFF('month',
  {FIXED [Customer ID] : MIN(IF [Status] = "Paid" THEN [Created date (UTC)] END)},
  [Created date (UTC)])

// Retention (table calculation)
COUNTD(IF [Status] = "Paid" THEN [Customer ID] END)
  / LOOKUP(COUNTD(IF [Status] = "Paid" THEN [Customer ID] END), FIRST())

Why the IF sits inside the FIXED: a normal filter on Status runs after FIXED, so a failed first charge would still set the cohort month. Putting the condition inside, or making Status a context filter, keeps failed charges out. See the filter order on your own data.

The same table in SQL

Runs in DuckDB on the raw export. Every number in the sample table above comes from this query.

WITH paid AS (
  SELECT "Customer ID" AS customer_id,
         CAST("Created date (UTC)" AS TIMESTAMP) AS created
  FROM read_csv_auto('unified_payments.csv')
  WHERE "Status" = 'Paid' AND "Customer ID" IS NOT NULL
), firsts AS (
  SELECT customer_id, date_trunc('month', MIN(created)) AS cohort
  FROM paid GROUP BY customer_id
), activity AS (
  SELECT DISTINCT p.customer_id, f.cohort,
         date_diff('month', f.cohort, date_trunc('month', p.created)) AS month_number
  FROM paid p JOIN firsts f USING (customer_id)
)
SELECT cohort, month_number, COUNT(*) AS customers,
       COUNT(*) / FIRST_VALUE(COUNT(*)) OVER (PARTITION BY cohort ORDER BY month_number) AS retention
FROM activity
GROUP BY cohort, month_number
ORDER BY cohort, month_number;

Ask an agent to do it

Paste this into Claude, ChatGPT or Codex with your export attached. It works with any agent that can run code.

I attached a Stripe payments export (unified_payments.csv).
Keep rows where Status is "Paid" and Customer ID is not empty.
For each Customer ID, the cohort is the month of their first paid charge.
For each cohort and each month since (0, 1, 2, ...), count distinct customers
with a paid charge in that month, and divide by the cohort's month-0 count.
Return a table: one row per cohort, one column per month, as percentages.
Show the SQL you ran.

The xmetric MCP server, in development, will build this view for an agent directly.

Reading the table

Month 0 is always 100%: it's the month each customer first paid. Read across a row to see one cohort fade; read down a column to compare cohorts at the same age. A later cohort holding a higher month-3 number than an earlier one means retention improved. Empty cells are months that haven't happened yet for that cohort.

Stripe is a trademark of Stripe, Inc. xmetric is not affiliated with Stripe. The sample file is synthetic: made-up customers using the column names of Stripe's default payments export as of 2026-10-17.

Questions people ask

Why count customers instead of revenue?

Customer retention shows who keeps paying. Revenue retention also counts upgrades and downgrades; it needs the Amount column and a sum instead of a distinct count.

Do refunds or failed charges count?

Only rows whose status is Paid count. A failed charge that was retried and paid counts once, in the month it was paid.

Is my Stripe data uploaded?

No. The CSV is read by code in this browser tab. Nothing is sent anywhere.

Drop your Stripe export above, or download the sample to see the format.