stripe-pulse — Stripe SaaS Metrics CLI
Get MRR, ARR, churn, LTV, NRR, quick ratio, MRR movements, reactivations — directly from Stripe subscriptions.
Install: npm install -g stripe-pulse
Quick check: stripe-pulse mrr --json
Everything at once: stripe-pulse dashboard --json
When to Use
- User asks about MRR, revenue, churn, customers, or any SaaS metric
- User asks about returning customers, reactivations, or win-backs
- Updating pulse.md or state files that track MRR
- Running freshness guards that check Stripe data
- User wants to export metrics (CSV, markdown, JSON)
- User says "check stripe", "what's our MRR", "how's churn looking", "any reactivations"
- Comparing metrics across profiles/accounts
Quick Start
# First time: authenticate
stripe-pulse login
# Check MRR
stripe-pulse mrr --json
# → { "mrr": 392, "arr": 4704, "currency": "usd", "activeSubscriptions": 18, "breakdown": [...] }
# Full dashboard (most efficient — one call, all metrics)
stripe-pulse dashboard --json
# → { "mrr": ..., "churn": ..., "reactivatedCustomers": ..., "reactivations": [...], ... }
Authentication
Three-tier auth chain (highest priority wins):
--api-key <key> flag on any command
STRIPE_API_KEY environment variable
- Credentials file at
~/.config/stripe-pulse/credentials.json
Multi-account: stripe-pulse mrr --profile activecalculator --json
Supports full keys (sk_live_*, sk_test_*) and restricted keys (rk_live_*, rk_test_*).
Commands
Single Metrics
| Command |
Returns |
Key Fields |
mrr --json |
MRR + breakdown |
mrr, arr, activeSubscriptions, breakdown[] |
arr --json |
Annual run rate |
arr, mrr |
customers --json |
Count by status |
activeSubscribers, trialingCustomers, pastDueCustomers |
arpu --json |
Avg revenue/user |
arpu |
ltv --json |
Lifetime value |
ltv, avgLifespanMonths, monthlyChurnRate |
plans --json |
Revenue by plan |
[{ productName, mrr, subscriptionCount, interval }] |
trials --json |
Trial conversion |
conversionRate, trialsStarted, trialsConverted |
Period Metrics (default: last 30 days)
| Command |
Returns |
Key Fields |
churn --json |
Customer churn % |
customerChurnRate, customersLost, reactivatedCustomers |
revenue-churn --json |
Revenue churn % |
revenueChurnRate, mrrLost |
nrr --json |
Net revenue retention |
nrr, expansionMrr, churnedMrr |
quick-ratio --json |
Growth efficiency |
quickRatio, reactivationMrr (>4 excellent, >1 healthy) |
movements --json |
MRR waterfall |
newMrr, expansionMrr, contractionMrr, churnedMrr, reactivationMrr, netNewMrr, reactivations[] |
Period flags: --from 2026-01-01 --to 2026-01-31
Customer Lists
| Command |
Returns |
Key Fields |
new-customers --json |
New in period |
count, reactivatedCount, reactivations[], customers[].email, .mrr |
churned --json |
Churned in period |
count, customers[].email, .canceledAt |
active --json |
All active (by MRR desc) |
count, customers[].email, .mrr |
Dashboard (All-in-One)
stripe-pulse dashboard --json
Returns every metric in one call: mrr, arr, activeSubscribers, arpu, customerChurnRate, revenueChurnRate, ltv, nrr, quickRatio, reactivatedCustomers, reactivations[], trialConversionRate, mrrByPlan[], currency, dataAsOf.
This is the most efficient call. Use it when you need multiple metrics — one API batch instead of many.
Auth & Diagnostics
| Command |
Purpose |
login |
Save Stripe API key (interactive) |
login --key sk_xxx --profile name |
Non-interactive login |
logout |
Remove credentials |
switch <profile> |
Switch active profile |
whoami --json |
Show profile, masked key, mode |
doctor --json |
Diagnostic checks (version, node, API key, connection) |
Global Flags
| Flag |
Effect |
--json |
Force JSON output |
--profile <name> |
Use specific Stripe account |
--api-key <key> |
Override API key for this request |
--from <date> |
Period start (YYYY-MM-DD) |
--to <date> |
Period end (YYYY-MM-DD) |
--format csv |
CSV output (for export) |
--format markdown |
Markdown table (for docs/updates) |
--verbose |
Extended output (reactivation details, extra context) |
--chart |
ASCII chart (MRR trend, plan bars) |
--quiet |
Suppress stderr, implies --json |
Auto-JSON: When stdout is piped (non-TTY), JSON is automatic. No --json needed.
Reactivation Tracking
When a customer cancels and later resubscribes, Stripe creates a new subscription ID. stripe-pulse detects this by matching customer IDs across canceled and new subscriptions.
Classification rules:
- Matches at the customer ID level (industry standard — same as Baremetrics, ChartMogul, ProfitWell)
- Only paid reactivations count — free-tier returns are excluded (
mrrCents > 0)
- 24h minimum gap — cancel/resub within 24 hours is treated as a plan switch, not reactivation
- One per customer — if a customer creates multiple new subs, only the most recent counts
- Churn still counts — reactivation is a separate positive MRR movement, not a reversal
Quick Ratio formula: (New + Expansion + Reactivation) / (Churn + Contraction)
Where reactivation data appears:
| Command |
Fields |
dashboard --json |
reactivatedCustomers, reactivations[] |
movements --json |
reactivationMrr, reactivations[] |
churn --json |
reactivatedCustomers (netted from customersLost for same-period) |
new-customers --json |
reactivatedCount, reactivations[] (filtered from customer list) |
quick-ratio --json |
reactivationMrr (in numerator) |
Reactivation detail object:
{
"customerId": "cus_abc",
"previousSubscriptionId": "sub_old",
"newSubscriptionId": "sub_new",
"canceledAt": "2026-01-15",
"reactivatedAt": "2026-03-01",
"mrrCents": 2900
}
Output Formats
# JSON (for parsing)
stripe-pulse mrr --json
# CSV (for spreadsheets)
stripe-pulse active --format csv > customers.csv
# Markdown (for investor updates)
stripe-pulse dashboard --format markdown
# Chart (MRR trend + movements waterfall)
stripe-pulse mrr --chart
Exit Codes
| Code |
Meaning |
0 |
Success |
1 |
API error (Stripe call failed) |
2 |
Auth error (no key, invalid key) |
3 |
Validation error (bad date, bad option) |
Gotchas
- Always use
--json when parsing output. Human output has ANSI colors that break parsing. Piped stdout auto-switches to JSON.
dashboard --json is more efficient than individual commands. One parallel batch vs many sequential calls. Use it when you need 2+ metrics.
customers counts unique customers, mrr counts subscriptions. A customer with 2 subscriptions = 1 customer but 2 in activeSubscriptions.
- Historical MRR (
--chart) is approximate. Reconstructed from subscription timestamps using current pricing. Doesn't reflect past price changes.
- Period defaults to last 30 days. Always pass
--from/--to for specific periods.
- Restricted keys work but show less info. Product names may show as price IDs if product read permission is missing.
- Benchmark strings only appear in human output. JSON has raw numbers only — no "⚠ High" or "✓ Good" strings.
- MRR breakdown is coupon-aware. Forever-duration discounts are distributed proportionally across plan items.
new-customers totalMrr vs movements newMrr can differ. new-customers counts all subs created in period (including already-canceled). movements only counts currently active ones.
- Designed for early-stage SaaS (up to ~10,000 subscriptions). Under 500 subs: 2-3 seconds. 2,000+ subs: 15-30 seconds. No caching between commands.
Common Patterns
Get a single number
stripe-pulse mrr --json | jq .mrr
# → 392
Check if churn is concerning
stripe-pulse churn --json | jq '.customerChurnRate > 10'
# → true/false
Get churned customer emails
stripe-pulse churned --json | jq -r '.customers[].email'
Check for reactivations
stripe-pulse movements --json | jq '.reactivations'
# → [{ customerId, previousSubscriptionId, newSubscriptionId, canceledAt, reactivatedAt, mrrCents }]
stripe-pulse dashboard --json | jq '.reactivatedCustomers'
# → 2
Compare two accounts
stripe-pulse mrr --profile activecalculator --json | jq .mrr
stripe-pulse mrr --profile teamai --json | jq .mrr
Diagnose connection issues
stripe-pulse doctor --json | jq '.checks[] | select(.status == "fail")'
Source: progrmoiz/stripe-pulse — distributed by TomeVault.
1---2name: stripe-pulse3description: Check Stripe SaaS metrics from the terminal. Use when you need MRR, churn, LTV, NRR, ARPU, customer counts, MRR movements, reactivations, or a full dashboard. Triggers on 'check MRR', 'what's our churn', 'stripe metrics', 'how many customers', 'revenue breakdown', 'returning customers', 'reactivations', 'export metrics', or any Stripe analytics question. Runs `stripe-pulse` CLI commands and parses JSON output. Use when this capability is needed.4---56# stripe-pulse — Stripe SaaS Metrics CLI78Get MRR, ARR, churn, LTV, NRR, quick ratio, MRR movements, reactivations — directly from Stripe subscriptions.910**Install:** `npm install -g stripe-pulse`11**Quick check:** `stripe-pulse mrr --json`12**Everything at once:** `stripe-pulse dashboard --json`1314## When to Use1516- User asks about MRR, revenue, churn, customers, or any SaaS metric17- User asks about returning customers, reactivations, or win-backs18- Updating pulse.md or state files that track MRR19- Running freshness guards that check Stripe data20- User wants to export metrics (CSV, markdown, JSON)21- User says "check stripe", "what's our MRR", "how's churn looking", "any reactivations"22- Comparing metrics across profiles/accounts2324## Quick Start2526```bash27# First time: authenticate28stripe-pulse login2930# Check MRR31stripe-pulse mrr --json32# → { "mrr": 392, "arr": 4704, "currency": "usd", "activeSubscriptions": 18, "breakdown": [...] }3334# Full dashboard (most efficient — one call, all metrics)35stripe-pulse dashboard --json36# → { "mrr": ..., "churn": ..., "reactivatedCustomers": ..., "reactivations": [...], ... }37```3839## Authentication4041Three-tier auth chain (highest priority wins):42431. `--api-key <key>` flag on any command442. `STRIPE_API_KEY` environment variable453. Credentials file at `~/.config/stripe-pulse/credentials.json`4647Multi-account: `stripe-pulse mrr --profile activecalculator --json`4849Supports full keys (`sk_live_*`, `sk_test_*`) and restricted keys (`rk_live_*`, `rk_test_*`).5051## Commands5253### Single Metrics54| Command | Returns | Key Fields |55|---------|---------|------------|56| `mrr --json` | MRR + breakdown | `mrr`, `arr`, `activeSubscriptions`, `breakdown[]` |57| `arr --json` | Annual run rate | `arr`, `mrr` |58| `customers --json` | Count by status | `activeSubscribers`, `trialingCustomers`, `pastDueCustomers` |59| `arpu --json` | Avg revenue/user | `arpu` |60| `ltv --json` | Lifetime value | `ltv`, `avgLifespanMonths`, `monthlyChurnRate` |61| `plans --json` | Revenue by plan | `[{ productName, mrr, subscriptionCount, interval }]` |62| `trials --json` | Trial conversion | `conversionRate`, `trialsStarted`, `trialsConverted` |6364### Period Metrics (default: last 30 days)65| Command | Returns | Key Fields |66|---------|---------|------------|67| `churn --json` | Customer churn % | `customerChurnRate`, `customersLost`, `reactivatedCustomers` |68| `revenue-churn --json` | Revenue churn % | `revenueChurnRate`, `mrrLost` |69| `nrr --json` | Net revenue retention | `nrr`, `expansionMrr`, `churnedMrr` |70| `quick-ratio --json` | Growth efficiency | `quickRatio`, `reactivationMrr` (>4 excellent, >1 healthy) |71| `movements --json` | MRR waterfall | `newMrr`, `expansionMrr`, `contractionMrr`, `churnedMrr`, `reactivationMrr`, `netNewMrr`, `reactivations[]` |7273Period flags: `--from 2026-01-01 --to 2026-01-31`7475### Customer Lists76| Command | Returns | Key Fields |77|---------|---------|------------|78| `new-customers --json` | New in period | `count`, `reactivatedCount`, `reactivations[]`, `customers[].email`, `.mrr` |79| `churned --json` | Churned in period | `count`, `customers[].email`, `.canceledAt` |80| `active --json` | All active (by MRR desc) | `count`, `customers[].email`, `.mrr` |8182### Dashboard (All-in-One)83```bash84stripe-pulse dashboard --json85```86Returns every metric in one call: `mrr`, `arr`, `activeSubscribers`, `arpu`, `customerChurnRate`, `revenueChurnRate`, `ltv`, `nrr`, `quickRatio`, `reactivatedCustomers`, `reactivations[]`, `trialConversionRate`, `mrrByPlan[]`, `currency`, `dataAsOf`.8788**This is the most efficient call.** Use it when you need multiple metrics — one API batch instead of many.8990### Auth & Diagnostics91| Command | Purpose |92|---------|---------|93| `login` | Save Stripe API key (interactive) |94| `login --key sk_xxx --profile name` | Non-interactive login |95| `logout` | Remove credentials |96| `switch <profile>` | Switch active profile |97| `whoami --json` | Show profile, masked key, mode |98| `doctor --json` | Diagnostic checks (version, node, API key, connection) |99100## Global Flags101102| Flag | Effect |103|------|--------|104| `--json` | Force JSON output |105| `--profile <name>` | Use specific Stripe account |106| `--api-key <key>` | Override API key for this request |107| `--from <date>` | Period start (YYYY-MM-DD) |108| `--to <date>` | Period end (YYYY-MM-DD) |109| `--format csv` | CSV output (for export) |110| `--format markdown` | Markdown table (for docs/updates) |111| `--verbose` | Extended output (reactivation details, extra context) |112| `--chart` | ASCII chart (MRR trend, plan bars) |113| `--quiet` | Suppress stderr, implies --json |114115**Auto-JSON:** When stdout is piped (non-TTY), JSON is automatic. No `--json` needed.116117## Reactivation Tracking118119When a customer cancels and later resubscribes, Stripe creates a new subscription ID. stripe-pulse detects this by matching customer IDs across canceled and new subscriptions.120121**Classification rules:**122- Matches at the **customer ID level** (industry standard — same as Baremetrics, ChartMogul, ProfitWell)123- Only **paid** reactivations count — free-tier returns are excluded (`mrrCents > 0`)124- **24h minimum gap** — cancel/resub within 24 hours is treated as a plan switch, not reactivation125- **One per customer** — if a customer creates multiple new subs, only the most recent counts126- Churn still counts — reactivation is a separate positive MRR movement, not a reversal127128**Quick Ratio formula:** `(New + Expansion + Reactivation) / (Churn + Contraction)`129130**Where reactivation data appears:**131| Command | Fields |132|---------|--------|133| `dashboard --json` | `reactivatedCustomers`, `reactivations[]` |134| `movements --json` | `reactivationMrr`, `reactivations[]` |135| `churn --json` | `reactivatedCustomers` (netted from `customersLost` for same-period) |136| `new-customers --json` | `reactivatedCount`, `reactivations[]` (filtered from customer list) |137| `quick-ratio --json` | `reactivationMrr` (in numerator) |138139**Reactivation detail object:**140```json141{142 "customerId": "cus_abc",143 "previousSubscriptionId": "sub_old",144 "newSubscriptionId": "sub_new",145 "canceledAt": "2026-01-15",146 "reactivatedAt": "2026-03-01",147 "mrrCents": 2900148}149```150151## Output Formats152153```bash154# JSON (for parsing)155stripe-pulse mrr --json156157# CSV (for spreadsheets)158stripe-pulse active --format csv > customers.csv159160# Markdown (for investor updates)161stripe-pulse dashboard --format markdown162163# Chart (MRR trend + movements waterfall)164stripe-pulse mrr --chart165```166167## Exit Codes168169| Code | Meaning |170|------|---------|171| `0` | Success |172| `1` | API error (Stripe call failed) |173| `2` | Auth error (no key, invalid key) |174| `3` | Validation error (bad date, bad option) |175176## Gotchas177178- **Always use `--json` when parsing output.** Human output has ANSI colors that break parsing. Piped stdout auto-switches to JSON.179- **`dashboard --json` is more efficient than individual commands.** One parallel batch vs many sequential calls. Use it when you need 2+ metrics.180- **`customers` counts unique customers, `mrr` counts subscriptions.** A customer with 2 subscriptions = 1 customer but 2 in `activeSubscriptions`.181- **Historical MRR (`--chart`) is approximate.** Reconstructed from subscription timestamps using current pricing. Doesn't reflect past price changes.182- **Period defaults to last 30 days.** Always pass `--from`/`--to` for specific periods.183- **Restricted keys work but show less info.** Product names may show as price IDs if product read permission is missing.184- **Benchmark strings only appear in human output.** JSON has raw numbers only — no "⚠ High" or "✓ Good" strings.185- **MRR breakdown is coupon-aware.** Forever-duration discounts are distributed proportionally across plan items.186- **`new-customers` totalMrr vs `movements` newMrr can differ.** `new-customers` counts all subs created in period (including already-canceled). `movements` only counts currently active ones.187- **Designed for early-stage SaaS (up to ~10,000 subscriptions).** Under 500 subs: 2-3 seconds. 2,000+ subs: 15-30 seconds. No caching between commands.188189## Common Patterns190191### Get a single number192```bash193stripe-pulse mrr --json | jq .mrr194# → 392195```196197### Check if churn is concerning198```bash199stripe-pulse churn --json | jq '.customerChurnRate > 10'200# → true/false201```202203### Get churned customer emails204```bash205stripe-pulse churned --json | jq -r '.customers[].email'206```207208### Check for reactivations209```bash210stripe-pulse movements --json | jq '.reactivations'211# → [{ customerId, previousSubscriptionId, newSubscriptionId, canceledAt, reactivatedAt, mrrCents }]212213stripe-pulse dashboard --json | jq '.reactivatedCustomers'214# → 2215```216217### Compare two accounts218```bash219stripe-pulse mrr --profile activecalculator --json | jq .mrr220stripe-pulse mrr --profile teamai --json | jq .mrr221```222223### Diagnose connection issues224```bash225stripe-pulse doctor --json | jq '.checks[] | select(.status == "fail")'226```227228---229> Source: [progrmoiz/stripe-pulse](https://github.com/progrmoiz/stripe-pulse) — distributed by [TomeVault](https://tomevault.io).230<!-- tomevault:4.0:skill_md:2026-06-24 -->