Methodology · 13 min read
Subscription LTV Cohort Analysis: 30/60/90/180-Day DTC Subscriber Math
Subscription DTC brands (skincare clubs, supplements, pet food, coffee) cannot rely on first-order AOV to evaluate ad campaigns — a $40 first-order subscriber with 8 refills is worth 8× more than a $200 one-time buyer. This playbook lays out the 30/60/90/180-day subscription cohort LTV math Admaxxer uses by default, plus how to read the cohort heatmap to spot churn cliffs in the second month.
Why subscription LTV is different from product LTV
Product LTV is a statistical estimate. It is repeat-purchase rate multiplied by average order value across some chosen window, with a probability distribution attached. You can never know the LTV of a one-time purchaser until they either come back or stay away long enough that you decide they will not.
Subscription LTV is deterministic up to churn. A $40 monthly subscription generates exactly $40 every month until cancellation. There are only two unknowns: when the subscriber cancels, and whether they pause or upgrade in between. Both of those signals are recorded by the subscription platform in real time as state transitions on the contract record. You do not estimate them, you read them.
That distinction changes the whole cohort vocabulary. For product DTC you build a back-of-envelope payback curve and accept that the right side of the curve is noise. For subscription DTC you build a survival curve on the contract table and you can read the LTV today for any subscriber who signed up six months ago. The math is exact at every point.
Practically this means subscription brands should never make ad-mix decisions on first-order AOV alone. A subscriber whose first order is $40 may be worth $240 over six months, or $80 if they cancel after two refills. Two campaigns with identical first-order economics can have a 3x gap in 180-day revenue. The campaign-mix question is invisible without cohort math.
The 30, 60, 90, and 180-day windows — why these buckets
Admaxxer ships four cohort windows by default: 30 days, 60 days, 90 days, and 180 days. Each one is chosen for the kind of subscriber behavior it captures, not because the numbers look round.
Day 30 is first-renewal hesitation. The first auto-renewal lands roughly at day 30 for a monthly billing interval. This is when subscribers who never intended a recurring commitment cancel. It is a noise-bucket: heavy on impulse signups, accidental signups, and gift-as-subscription confusion. A 5% drop in the first 30 days is normal; a 25% drop is a product-onboarding problem.
Day 60 is the second renewal. Subscribers who hesitated at day 30 but stayed have now paid twice. Day 60 retention is the cleanest early signal that the product is actually being consumed. If day 60 retention is flat against day 30, you have an active early-life cohort and the day-180 LTV will be high. If day 60 dropped 15% from day 30, you have a product-fit problem masked by the first month.
Day 90 is the post-honeymoon mark. The novelty has worn off, the cabinet is full, and the credit-card statement is showing a recurring charge that the subscriber notices. Day-90 retention is the canonical 'how good is this DTC subscription' number. DTC-wide median is 55–65% at day 90; top-decile brands hold 80%+. Below 50% at day 90 is a hard signal to either fix product, pricing, or shipping cadence.
Day 180 is price-sensitivity. After six months the subscriber has had ample chance to compare your prices against alternatives, has likely been targeted by a competitor with a 20%-off offer, and has made an explicit cost/benefit judgement. Day 180 retention separates 'they like the product' from 'they value the product enough to keep paying.' For high-AOV subscriptions, day 180 is where the LTV curve flattens — subscribers past this mark tend to stay for years.
Churn cliffs — what they look like, how to read them
A churn cliff is a column on the cohort heatmap where retention drops sharply between two adjacent windows. On Admaxxer's /cohorts surface this renders as a heatmap with months on the y-axis and elapsed days on the x-axis. Healthy cohorts show a smooth gradient from green to amber across the row. Unhealthy cohorts show a green block followed by a red block — a cliff.
There are three canonical cliff patterns. First, the day-30 cliff: retention drops 10+ points between day 0 and day 30. This is bad onboarding. The product arrived, the subscriber tried it, and decided not to continue. Investigate first-shipment quality, confusion about the next bill, and whether the signup flow over-promised.
Second, the day-60-to-90 cliff: retention is flat through day 60 then drops 15+ points by day 90. This is the post-honeymoon cancellation pattern. Subscribers who tolerated the first two months now notice the third charge and act. Investigate price perception, inventory management on the subscriber's end (are they accumulating product faster than they consume it), and whether the second renewal email was tone-deaf about the recurring commitment.
Third, the day-180 cliff: retention holds through day 90 then drops 10+ points by day 180. This is competitive churn — a subscriber has been offered a better price elsewhere and switched. Investigate competitor activity, your win-back offers, and whether subscribers approaching the 6-month mark see a loyalty offer or get treated like every other charge.
Reading the heatmap row-by-row also reveals seasonal vs structural cliffs. A cliff that shows up in November cohorts only is BFCM-related: those subscribers signed up at peak promotion and were lower-intent. A cliff that shows up in every cohort uniformly is a structural product/pricing problem that needs a fix, not a campaign tweak.
Campaign-level subscriber LTV — the breakdown that exposes 3x LTV gaps
Channel-blended subscription LTV is useful for board-deck headlines and useless for campaign decisions. The honest number is per-campaign and per-creative, and it surfaces 2–3x LTV deltas that ROAS-on-first-order reporting hides.
Admaxxer attributes the first subscription order to its acquisition campaign using whatever attribution model your workspace has chosen (last-non-direct by default; data-driven and Markov also available). The subscriber's lifetime is then tagged with that single acquisition campaign for the full 180-day window — refills are attributed to the original campaign, not re-attributed to whichever ad the subscriber clicked last week.
The breakdown drills source → medium → campaign → ad set → creative. The most common operator-actionable insight at the creative level: video creatives almost always beat static creatives on first-order match rate (lower CAC, more clicks), and static creatives almost always beat video on day-180 retention (higher LTV per acquired subscriber). The mechanism is selection — video creatives convert impulse buyers; static creatives over-represent considered buyers who churn less.
The decision rule that falls out: optimize Meta video creatives for cold prospecting (CAC efficiency at scale), optimize static creatives for retargeting and lookalikes (LTV per converted subscriber), and never compare them on first-order ROAS alone. Admaxxer's /attribution drill-down ships this view as the default — 180-day LTV by source × medium × campaign × creative, with the day-30 / day-60 / day-90 / day-180 columns shown side by side.
The same rule generalizes across platforms. Google Performance Max often beats Meta on first-order ROAS but loses on day-180 LTV because PMax over-indexes on warm in-market searches; Meta's prospecting reaches a colder audience that converts at lower ROAS but stays longer. Without a cohort view you cannot see this; with one, your monthly budget mix recommendation is unambiguous.
Pulling data from Stripe Billing, Recharge, Bold, Skio, and Loop
Admaxxer's revenue connector reads subscription contracts and renewal events directly from your subscription platform. The supported set covers the major DTC subscription rails: Stripe Billing, Recharge, Bold Subscriptions, Skio, and Loop. The shape of what gets read is identical across providers — they all expose subscription contracts, renewal events, cancellation events, and per-renewal line items — so the downstream cohort math is the same regardless of which platform you run.
For each subscriber Admaxxer stores the signup date, the signup source from the first-touch pixel attribution (we resolve this at signup time, not at renewal time), the subscription product and billing interval, every renewal event with its timestamp and amount, and the cancellation event when it fires. Pause and skip events are captured separately so they do not pollute the churn signal.
The most common data hygiene issue is double-counting. A Shopify store running Recharge has two ingest paths: the Shopify order webhook fires when a subscription order ships, and the Recharge subscription event fires for the contract state transition. Without deduplication the same renewal would land twice. Admaxxer reconciles by transaction ID — the Recharge charge has a Shopify order link, and we use that link as the dedup key so the order counts once and the subscription metadata is enriched onto it.
Stripe Billing customers have the cleanest data path. Stripe is the canonical source for the subscription contract, the invoice, and the refund event. Admaxxer's Stripe connector subscribes to invoice.payment_succeeded, customer.subscription.created/updated/deleted, and charge.refunded — every state transition we need ships as a webhook. Recharge, Bold, Skio, and Loop expose equivalent webhook sets; the connector handles the per-platform schema differences.
Reconciling subscription orders with Shopify (no double-counting)
A subscriber who buys a one-off product at signup and then continues their subscription should appear once in the order ledger with two line items, not as two separate orders. The Shopify order webhook does this correctly when the storefront uses Shopify's native subscription support, but the mixed Recharge + Bold + Skio model needs an explicit reconciliation step.
Admaxxer's reconciliation runs on every order ingest. The Shopify order arrives first (the storefront fires it at checkout); the subscription platform webhook arrives a few seconds later with the subscription contract metadata. We match on the Shopify order ID, which every subscription platform stamps onto its charge record. The two records merge into a single canonical order with two line items: a one-time SKU and a subscription SKU.
If the order ID match fails (rare, but it happens during platform outages or when a subscription is created without an immediate charge), Admaxxer falls back to a secondary match on customer email + order time, with a 30-minute tolerance window. The match is logged so /admin/data-health can surface any orders that did not reconcile cleanly.
The reconciled order then enters the cohort table once. The acquisition source on the cohort row comes from the first-touch pixel attribution at signup, not from any subsequent re-attribution event. This is critical: if a subscriber clicks a re-engagement ad three months in, the renewal revenue belongs to the original acquisition campaign, not the re-engagement ad. The re-engagement ad gets credit in the retention / win-back reports instead.
Channel and creative breakdown by 30 / 60 / 90 / 180 windows
The default Admaxxer cohort view shows acquired-subscriber count, day-30 / 60 / 90 / 180 retention, and cumulative MRR through each window, broken out by source and medium. The actionable view is one level deeper: the same metrics at the campaign and creative level.
Meta video creatives versus Meta static creatives is the single highest-signal split for DTC subscription brands. Video over-indexes on impulse signups (lower CAC, higher day-0 conversion, lower day-180 retention); static over-indexes on considered signups (higher CAC, lower day-0 conversion, higher day-180 retention). The two creative formats answer different acquisition strategies, and budget allocation between them is best made on day-180 LTV, not day-0 ROAS.
Google PMax versus Google Search is the next-most-signal split. PMax fishes in a broader audience pool and converts at lower last-click ROAS but with subscriber profiles that resemble Meta video — impulse-leaning, lower 180-day retention. Search captures already-intent traffic that converts at higher last-click ROAS and retains better. Subscription brands that over-allocate to PMax based on last-click numbers tend to discover an LTV problem six months later.
TikTok and Pinterest carry their own profiles. TikTok over-represents younger demographics that churn harder on price-sensitivity at day 180. Pinterest over-represents save-for-later behavior that converts later (longer attribution windows needed) but retains exceptionally well. The cohort breakdown surfaces these network-level patterns automatically.
Admaxxer's recommended workflow: every Monday, open /cohorts and compare last week's cohort against the trailing 13-week median on the same source × medium × campaign tuple. Any 10+ point retention drop is a flag. Drill into the creative breakdown to find which ad changed. This catches creative fatigue and audience drift before they show up in monthly revenue.
Healthy retention benchmarks for DTC subscriptions
Subscription retention benchmarks vary by vertical, AOV, and shipping cadence. The DTC-wide median from the cohort data Admaxxer aggregates across its customer base lands at roughly 55–65% day-90 retention. Top-decile brands hold 80%+ at day 90 and stay above 70% through day 180. The bottom-decile drops below 40% at day 90 and is unlikely to be profitable at any reasonable CAC.
Vertical-specific patterns we see in the data: supplements retain better than apparel-as-subscription (cabinet effect is weaker, consumption is daily, refill cadence aligns with actual usage); coffee and grocery retain at high day-30 but degrade at day-180 when subscribers realize they prefer variety; skincare shows a bimodal pattern — high day-90 retention for the subset who incorporate it into a routine, sharp churn for the subset who treated it as a one-off trial; pet food retains the longest of any vertical (the pet eats every day, the human does not have to remember to use it).
The benchmark to internalise: below 50% at day 90 is a churn-cliff signal. Admaxxer flags it in /alerts and suggests running a holdout test at /incrementality to attribute the cause to one of the three canonical drivers (onboarding, price, competitive). Above 70% at day 90 is healthy and means the acquisition mix can be pushed harder without worrying about LTV degradation.
Day-180 retention follows day-90 with a typical 8–15 point further drop. Healthy: above 55% at day 180. Top-decile: above 65%. Below 40% at day 180: the LTV math will not support double-digit CAC growth and the brand should fix retention before scaling spend.
Cohort table schema and JSON shape
The canonical cohort table powers everything: the /cohorts dashboard, the /attribution drill-down, the /alerts retention-cliff detector, and the /api/v1/cohorts/subscription JSON endpoint. The schema below mirrors what gets returned to BI tools.
-- Admaxxer's canonical subscription cohort table
-- (schema mirrors what /api/v1/cohorts/subscription returns as JSON).
CREATE TABLE subscription_cohorts (
subscriber_id UUID NOT NULL,
workspace_id UUID NOT NULL,
signup_date DATE NOT NULL,
signup_source TEXT NOT NULL, -- e.g. 'facebook | cpc | acme_ss_q2'
signup_campaign_id TEXT, -- platform-side campaign id
signup_creative_id TEXT, -- platform-side ad-level id
subscription_plan TEXT NOT NULL,
billing_interval TEXT NOT NULL, -- 'month' | 'quarter' | 'year'
first_order_value NUMERIC(10,2) NOT NULL,
mrr_30d NUMERIC(10,2) NOT NULL, -- realised MRR through day 30
mrr_60d NUMERIC(10,2) NOT NULL,
mrr_90d NUMERIC(10,2) NOT NULL,
mrr_180d NUMERIC(10,2) NOT NULL,
refills_180d INT NOT NULL,
churned_at TIMESTAMPTZ, -- NULL if still active
churn_reason TEXT, -- voluntary | payment_failed | paused
presentment_currency TEXT NOT NULL,
usd_at_transaction NUMERIC(20,10) NOT NULL,
PRIMARY KEY (subscriber_id),
INDEX idx_signup (workspace_id, signup_date),
INDEX idx_source (workspace_id, signup_source)
);
-- Day-90 retention by acquisition source/medium/campaign.
SELECT
signup_source,
signup_campaign_id,
COUNT(*) AS subscribers,
SUM(CASE WHEN churned_at IS NULL
OR churned_at > signup_date + INTERVAL '90 days'
THEN 1 ELSE 0 END)::float / COUNT(*) AS day_90_retention,
AVG(mrr_90d) AS avg_mrr_90d,
AVG(mrr_180d) AS avg_mrr_180d
FROM subscription_cohorts
WHERE workspace_id = $1
AND signup_date BETWEEN $2 AND $3
GROUP BY signup_source, signup_campaign_id
ORDER BY avg_mrr_180d DESC;
The MRR columns are realised, not contracted. mrr_30d is the dollars that actually billed and cleared in the first 30 days, not the contracted monthly amount times the elapsed share. Failed payments are excluded; refunded charges are netted out. This makes the column directly comparable to revenue.
refills_180d counts successful renewal charges, not contracted intervals. A subscriber on a monthly plan who paused for 30 days will have refills_180d = 4, not 5. churned_at is the cancellation event timestamp; pause events do not set this field.
The /api/v1/cohorts/subscription endpoint returns a normalized JSON view of the same data, grouped by signup month with per-channel breakdowns:
{
"workspace_id": "ws_acme_skincare_us",
"window_days": 180,
"as_of": "2026-05-18T00:00:00Z",
"cohorts": [
{
"signup_month": "2025-11",
"acquired_subscribers": 312,
"retention": {
"day_30": 0.94,
"day_60": 0.81,
"day_90": 0.68,
"day_180": 0.54
},
"avg_mrr_usd": {
"day_30": 37.40,
"day_60": 64.20,
"day_90": 91.10,
"day_180": 148.90
},
"channels": [
{
"source": "facebook",
"medium": "cpc",
"campaign": "acme_ss_holiday",
"subscribers": 142,
"avg_mrr_180d_usd": 188.20,
"day_90_retention": 0.72,
"blended_cac_usd": 51.40
}
]
}
]
}
Exporting cohort data to BigQuery, ClickHouse, or your BI tool
There are four canonical export paths for subscription cohort data on Admaxxer. Each one trades on freshness, format, and tier availability.
- /api/v1/cohorts/subscription — JSON endpoint, refreshed every 15 minutes from the source-of-truth ClickHouse table. Available on every paid tier. Use this for ad-hoc pulls, custom dashboards, and BI tools that prefer JSON.
- BigQuery destination — Shopify app pixel pushes subscriber events to a BigQuery dataset you own. Available on the AD_PLATFORM tier ($999/mo). Refreshes within a few minutes of each event. Use this when your reporting stack already lives in BigQuery and you want Admaxxer cohort data alongside your data warehouse tables.
- CSV export at /export — point-in-time download of any cohort table for a chosen date range. Available on every paid tier. Use this for finance reviews, board decks, and one-off investigations.
- ClickHouse readonly credentials — a scoped account with SELECT-only access to your workspace's data in our self-hosted ClickHouse. Available on the AD_PLATFORM tier. Use this when you want to run SQL directly without going through the API rate-limit ceiling.
All four paths return the same numbers because they read from the same source table. The differences are in latency, schema preservation, and the kind of analyst who is going to use the output.
Worked example — a $40/mo skincare subscription, 35% day-90 churn
Walk through the numbers for a representative DTC subscription brand. The product is $40/month, the first-shipment includes a small one-time onboarding kit at $15, day-90 retention is 65% (slightly below the DTC median), and the brand is choosing between two Meta campaigns.
Campaign A is a Meta video creative — bright, impulse-friendly, costing $50 CAC. Campaign B is a Meta static carousel that explains the product clinically — higher consideration, costing $80 CAC. On first-order ROAS Campaign A wins easily ($55 first-order revenue / $50 CAC = 1.10x first-order ROAS vs Campaign B's $55 / $80 = 0.69x). A monthly performance report optimizing on first-order ROAS would scale Campaign A and pause Campaign B.
Now factor in the cohort retention curves attributable to each acquisition source:
-- $40/mo skincare subscription, 35% churn at day 90.
-- Numbers are illustrative (rounded to round-figure paths).
Campaign A — Meta video creative
CAC: $50
First-order value: $40
Day-30 retention: 92%
Day-90 retention: 68%
Day-180 retention: 54%
Cumulative revenue (180d): ($40 × 1) + ($40 × 0.92 × 1)
+ ($40 × 0.78 × 2) + ($40 × 0.61 × 3)
= $40 + $36.80 + $62.40 + $73.20
= $212.40 per acquired subscriber
ROAS @ 180d: $212.40 / $50 = 4.25x
Campaign B — Meta static creative
CAC: $80
First-order value: $40
Day-30 retention: 97%
Day-90 retention: 81%
Day-180 retention: 71%
Cumulative revenue (180d): ($40 × 1) + ($40 × 0.97 × 1)
+ ($40 × 0.89 × 2) + ($40 × 0.76 × 3)
= $40 + $38.80 + $71.20 + $91.20
= $241.20 per acquired subscriber
ROAS @ 180d: $241.20 / $80 = 3.01x
Without LTV, you'd kill Campaign B (CAC too high).
With 180d LTV, you'd kill Campaign A — Campaign B builds a
better book of business even with a 60% higher CAC.
The decision flips. Campaign A returns 4.25x at day 180 but the absolute revenue per acquired subscriber is $212. Campaign B returns 3.01x at day 180 but the absolute revenue is $241. If both campaigns are scaled to the same target subscriber count, Campaign B generates 14% more cumulative revenue at day 180 — and the gap widens as the cohort ages beyond 180 days because static-acquired subscribers churn less per month going forward.
The right mix is usually 'both campaigns running, budgets balanced on a blended LTV target' — not 'kill the high-CAC campaign.' The cohort table makes this visible; the first-order ROAS report buries it.
Operator checklist — running this in your own workspace
- Install Admaxxer's first-party pixel on your storefront (5 min on Shopify; the app handles theme injection).
- Connect your subscription platform at /settings/connections (Stripe Billing, Recharge, Bold, Skio, or Loop — paste-token, no app review).
- Connect your ad platforms (Meta + Google + TikTok + Pinterest + Amazon) on the same screen.
- Wait 24–48 hours for the initial Shopify + subscription history backfill to complete; /admin/data-health shows progress.
- Open /cohorts and verify your last 3 months of acquired subscribers appear with retention curves.
- Drill into /attribution and verify campaign × creative breakdowns at the 30 / 60 / 90 / 180 windows.
- Set up the cohort-cliff alert at /alerts (default threshold: 10-point day-90 retention drop versus trailing-90 baseline).
- Schedule a weekly review on the same source × medium × campaign tuple; flag any drift early.
The whole setup is under an hour of operator time. The cohort signal is immediately useful for week-old cohorts and becomes increasingly authoritative as the back-catalog rolls in.
Frequently asked questions
- Why is subscription LTV different from product LTV?
- Product LTV (one-time purchases) is a function of repeat-purchase rate × AOV across an arbitrary window. Subscription LTV is deterministic up to churn: a $40/mo subscription generates exactly $40/mo until cancellation. The two key signals are (1) churn cliffs — days where cancellation spikes (typically day 30 = first renewal hesitation, day 90 = post-honeymoon, day 180 = price-sensitivity check) and (2) campaign-level subscriber LTV at 30/60/90/180 days, which is the only honest way to compare a $50 CAC subscription brand campaign vs a $80 CAC one — the $80 campaign might have 3× the 180-day LTV.
- How does Admaxxer pull subscription data from Recharge / Bold / Skio?
- Admaxxer's revenue connector reads subscription contracts + renewal events directly from your subscription platform (Stripe Billing, Recharge, Bold Subscriptions, Skio, Loop). For each subscriber it stores: signup date, signup source (from the first-touch pixel attribution), subscription product, billing interval, renewal events, cancellation event, and per-renewal revenue. This is reconciled with Shopify orders so a one-time + subscription combined order doesn't double-count.
- What does a healthy 90-day subscription cohort retention look like?
- DTC-wide median is 55-65% at day 90 (so 35-45% churn by 3 months); top-decile is 80%+. Admaxxer's /cohorts page shows your retention curve overlaid on category benchmarks — supplements typically curve flatter than apparel-as-subscription (apparel churns harder at day 60). Below 50% at day 90 is a churn-cliff signal; Admaxxer flags this in /alerts and suggests a retention-focused holdout test in /incrementality.
- Can I see subscription LTV at the campaign × creative level?
- Yes. Admaxxer's /attribution drill-down breaks subscription LTV by source → medium → campaign → ad set → creative for the 30/60/90/180 windows. A common insight: Meta video creatives outperform static creatives at day 30 (first-order matching) but reverse by day 180 (subscribers from static creatives churn less). This is hidden in MER-only reporting and visible in subscription LTV reporting.
- How do I export subscription cohort data to my BI tool?
- Three ways. (1) /api/v1/cohorts/subscription endpoint returns JSON for any window. (2) Admaxxer's Shopify-app pixel pushes subscriber events to a BigQuery destination on the AD_PLATFORM plan ($999/mo). (3) The /export page lets you CSV-export any cohort table. For ad-platform tier customers, ClickHouse readonly credentials are also available — query the source data directly.
Run this playbook in your own dashboard
Admaxxer ships the pixel + Meta CAPI + Google Enhanced Conversions + Maxxer AI agent + cohort analytics out of the box. The playbook above becomes a live surface in your account after a 5-minute setup.