Subscription lifecycle (state)
Trial, active, past due, canceled — and back.
Subscription billing looks simple until a payment fails. This state diagram captures the full lifecycle the way billing providers actually model it: trials that convert or expire, an active state that can fall into past-due when a charge bounces, a dunning loop that either recovers the payment or exhausts itself, and a canceled state that customers can return from. Every edge is a webhook you'll need to handle and an email you'll probably need to send.
Teams need this diagram because the lifecycle cuts across systems — the billing provider owns the transitions, your database mirrors them, and support, finance, and lifecycle marketing all act on them. A shared picture keeps everyone using the same vocabulary for "canceled" versus "past due."
When to use this template
- Billing integration design — enumerate every transition before writing
webhook handlers, so
past_due → canceleddoesn't surprise you in production. - Support and retention playbooks — each edge is a moment to act: dunning
emails on
Payment failed, win-back campaigns onUser cancels, reactivation offers from Canceled. - Metrics definitions — churn, recovery rate, and trial conversion are all transition counts; agreeing on the diagram means agreeing on the numbers.
How to adapt it
Match the states to what your billing provider reports, then layer in your product's rules:
- Add a Paused state between Active and Canceled if you offer subscription pausing — it measurably reduces hard churn.
- Insert an Incomplete state before Trialing or Active if the first payment can fail during checkout.
- Annotate transitions with grace periods and retry counts (for example "3 retries over 14 days") so the dunning policy is documented where everyone reads it.
You can make these changes by editing the diagram visually — the editor regenerates clean Mermaid code as you work, so the result drops straight into your billing docs.
Mermaid code
Copy it anywhere Mermaid is supported — GitHub, Notion, or your docs.
stateDiagram-v2
[*] --> Trialing
Trialing --> Active: Card charged
Trialing --> Canceled: Trial expired
Active --> PastDue: Payment failed
PastDue --> Active: Payment recovered
PastDue --> Canceled: Dunning exhausted
Active --> Canceled: User cancels
Canceled --> Active: Reactivated
Canceled --> [*]
Frequently asked questions
- What states should a subscription model have?
- Four cover most SaaS products: trialing, active, past due, and canceled. The transitions matter as much as the states — trials either convert or expire, failed payments drop into past due rather than straight to canceled, and canceled customers can reactivate. If you offer pausing or seat-based proration, add a paused state, but resist inventing states your billing provider can't report.
- How does this diagram map to Stripe subscription statuses?
- Almost directly: Stripe's `trialing`, `active`, `past_due`, and `canceled` statuses correspond to the four states here. Stripe also has `incomplete` (first charge never succeeded) and `unpaid` (dunning ended but you kept the subscription) — add those as extra states if your integration uses them. Modeling your app's states to mirror the provider's avoids painful webhook translation bugs.
- What is dunning and where does it appear in this diagram?
- Dunning is the automated retry-and-remind process after a payment fails: retry the card on a schedule, email the customer, and eventually give up. It lives in the PastDue state — `Payment recovered` returns the customer to Active, while `Dunning exhausted` transitions to Canceled. Annotating those two edges with your retry window makes the policy explicit for support and finance.
- How do I create a state diagram in Mermaid?
- Begin with `stateDiagram-v2`, use `[*]` for the start and end pseudo-states, and write transitions as `StateA --> StateB: trigger label`. States are created implicitly the first time you mention them, so a complete lifecycle takes under a dozen lines. Composite states and notes are available when a flat diagram stops being enough.