SubTracker

Which statuses does a subscription tracker need, and how should they affect totals?

A minimal status model — active, paused, trial, cancelled, expired — with one rule for spending totals and an event trail for every transition.

Answer

Five statuses cover almost every real case: active, paused, trial, cancelled and expired. Only active subscriptions should count towards spending totals, which keeps paused and trial subscriptions visible without inflating the number. Record every transition as an event with the old and new status, so a cancellation or reactivation leaves a trail instead of silently overwriting the row.

LM

Leutrim Miftaraj

Founder, SubTracker · Updated September 29, 2026

The short answer

Five statuses cover almost every real case: active, paused, trial, cancelled and expired. Only active subscriptions should count towards spending totals, which keeps paused and trial subscriptions visible without inflating the number. Record every transition as an event with the old and new status, so a cancellation or reactivation leaves a trail instead of silently overwriting the row.

The Five Statuses

Active — being paid for now. Paused — the user intends to resume; billing is stopped for a period. Trial — a third-party trial that will convert unless cancelled. Cancelled — the user has ended it. Expired — it ended on its own, for example a fixed-term plan that was not renewed.

SubTracker's schema started with active, paused, cancelled and expired, and added trial later as a value of the same enum rather than a separate flag.

One Rule for Totals

Every status question about money reduces to one rule: only `active` rows count towards spend. Paused subscriptions stay on the list, so the user remembers them, without inflating the monthly figure. Trials inherit the same exclusion, which is why adding the trial status required no change to the cost calculation.

A single rule is easier to trust than a set of special cases, and easier to test.

Paused Is Not Cancelled

Paused exists because gyms, meal kits and some streaming services genuinely pause. Treating a pause as a cancellation loses the resume date; treating it as active overstates spending. It also changes reminders: a paused subscription should not receive renewal reminders while paused.

Transitions Leave a Trail

Status changes are written to the events table as `status_changed`, `cancelled` or `reactivated` events with the old and new status. That trail answers questions the current row cannot: when was this cancelled, was it ever reactivated, how long was it paused. It also lets reports explain a drop in spending as cancellations rather than guessing.

Soft Deletion Is Separate

Deleting a subscription is not a status. A `deleted_at` timestamp hides the row without destroying its history, and indexes that filter on status also filter out deleted rows. Mixing deletion into the status enum would make "cancelled" ambiguous between "the user ended it" and "the user removed it from the list".

Frequently asked questions

Should paused subscriptions count towards monthly spend?+

No. They are not being charged. Keep them visible on the list but exclude them from totals by counting only active subscriptions.

What is the difference between cancelled and expired?+

Cancelled means the user ended the subscription. Expired means it ended on its own, such as a fixed-term plan that was not renewed.

Why record status changes as events?+

So the history survives. The current row only shows the present status; events show when it was cancelled, paused or reactivated.

Should deletion be a status?+

Better not. A separate deleted_at timestamp keeps "cancelled" meaning one thing — the user ended it — and preserves the full history of rows that were removed from the list.