Subscription Lifecycle
States
draft → confirmed → trialing → active ↔ past_due → canceled
↕
paused
| State | Description |
|---|---|
| draft | Draft. Can be freely edited before confirmation |
| confirmed | Confirmed, awaiting activation |
| trialing | In a free trial period |
| active | Active, generating recurring charges |
| past_due | Has overdue payments |
| paused | Temporarily paused (no charges) |
| canceled | Canceled (terminal state) |
Creating a Subscription
To create a subscription, provide:
| Field | Required | Description |
|---|---|---|
| Billing account | Yes | The customer account to be charged |
| Plan | No | Pre-configured plan (or individual items) |
| Billing cycle | Yes | monthly, quarterly, semiannual, or annual |
| Collection method | Yes | charge_automatically, manual_charge, or manual_invoice |
| Trial period | No | 0 to 90 days of free trial |
| Discount code | No | Discount to be applied |
| Items | No | Products, prices, and quantities (if not using a plan) |
| Company | No | Issuing company (for multi-company setups) |
Creation Flow
- The subscription is created with
draftstatus - Items are created from the provided items or from the plan items
- The discount code usage is incremented (if provided)
- A
subscription.db.createdevent is emitted
Confirmation and Activation
Confirm (draft → confirmed)
Confirms the subscription, optionally creating the first invoice.
Activate (draft/confirmed → trialing/active)
- If it has a trial period: status changes to
trialing, withtrialStartandtrialEndset - If there is no trial: status changes directly to
active - Sets the billing period dates (
currentPeriodStart,currentPeriodEnd)
Activation Preview
Before activating, you can preview:
- Start date, whether there will be a trial, trial duration
- Billing period dates
- First invoice date
- Setup and recurring totals
Renewal
Renewal is automatic via a daily worker (00:00 UTC):
- Finds active subscriptions with
currentPeriodEnd ≤ now - If
cancelAtPeriodEnd = true: cancels the subscription - Otherwise: advances to the next billing period
- Uses optimistic locking to prevent duplicate renewals
Period Calculation
| Cycle | Duration |
|---|---|
monthly | +1 month |
quarterly | +3 months |
semiannual | +6 months |
annual | +1 year |
Cancellation
Cancel at End of Period (cancelAtPeriodEnd: true)
- Sets the
cancelAtPeriodEndflag - The subscription remains active until the end of the current period
- On renewal, the status changes to
canceled - Can be reversed before the cancellation date
Cancel Immediately (cancelAtPeriodEnd: false)
- Status changes to
canceledimmediately - The cancellation date is recorded
- The cancellation reason is stored
Revert to Confirmed
Active subscriptions can be reverted to confirmed to allow editing before reactivating. Useful for correcting errors before the next renewal.
Deletion
Only subscriptions in draft, confirmed, or canceled status can be deleted, and only if there are no invoices with payments.
MRR Calculation
MRR (Monthly Recurring Revenue) is calculated by normalizing the total value to a monthly basis:
| Cycle | Formula |
|---|---|
monthly | Total of items |
quarterly | Total / 3 |
semiannual | Total / 6 |
annual | Total / 12 |
Emitted Events
| Event | When |
|---|---|
subscription.db.created | Subscription created |
subscription.confirmed | Confirmed |
subscription.activated | Activated |
subscription.renewed | Renewed |
subscription.paused | Paused |
subscription.resumed | Resumed |
subscription.canceled | Canceled |
subscription.pending_cancellation | Marked for cancellation at end of period |
subscription.plan_changed | Plan changed |
subscription_status_changed | Any status change |