Donutwork Docs
Entities

Subscription Entity

Managing recurring plans, seat-based addons, and lifecycle transitions without breaking legacy flows.

Subscription Entity

The Subscription object represents a recurring commercial agreement attached to a Customer. It combines plan pricing, addon quantities, billing cadence, and lifecycle state.

Usage

Use the Subscription entity to:

  • Attach a pricing plan to a customer.
  • Manage seat-based pricing through addon quantity.
  • Apply global and addon-level discounts.
  • Track metered usage when enabled.
  • Execute lifecycle transitions (pause, resume, change, cancel-at-period-end).
  • Keep amendment history for audits.

Seat-Based Pricing Model

In Donutwork, seat-based pricing is handled through addons.

  • Configure the per-seat value in the addon price.
  • Set the number of seats in addons[].quantity.
  • Renewal totals automatically include seat addons.

Example:

  • Addon workspace_seat price: 12.00
  • Quantity: 25
  • Seat subtotal contribution: 300.00 (before taxes/discounts)

Expected Subscription Flow

Plan Association

Attach a plan with POST /customers/{id}/subscriptions.json.

Runtime Configuration

Adjust addon quantities (including seats), discounts, and usage inputs as needed.

Lifecycle Transitions

Optionally apply lifecycle operations (pause, resume, change-plan, change-addons, cancel-at-period-end).

Renewal Boundary Processing

At next_renew, scheduled changes are applied and recurring billing is executed.


Lifecycle States and Transitions

Lifecycle StateMeaningTypical Entry
activeSubscription is billable and renewableAttach, resume, successful change
pausedBilling is temporarily suspendedPause action
cancel_pendingMarked to stop at period boundaryCancel-at-period-end
cancelledNo longer activePost-boundary stop/legacy cancellation
trialing / past_dueOptional advanced states for integrationsExternal lifecycle orchestration

State Definitions with Practical Examples

  • active: customer is billed normally at each renewal date. Example: a monthly plan renews on May 1, 2026 and remains active.
  • paused: billing is temporarily halted. Example: customer asks to freeze service for 30 days; no renewal charge is attempted while paused.
  • cancel_pending: subscription will stop at period boundary. Example: customer cancels on April 15, 2026; access remains until the next renewal boundary.
  • cancel_pending can be reversed before boundary. Example: customer cancels, then changes mind after 3 days; use undo-cancel-at-period-end to return to active.
  • cancelled: subscription is fully closed. Example: boundary reached and cancellation finalized.
  • trialing: customer is in free trial before first paid cycle. Example: plan includes 14-day trial after initial attach.
  • past_due: payment issue detected and recovery flow is required. Example: renewal attempt failed and account is awaiting payment method update.

Pause and Resume Billing Semantics

When a subscription is paused, Donutwork stops renewal execution and clears next_renew while storing the previous renewal date in pause_state.previous_next_renew.

When resumed:

  • If resume_at is provided and is in the future, that date becomes the new next_renew.
  • If resume_at is missing, Donutwork uses the stored previous_next_renew.
  • If the stored date is already in the past, Donutwork sets next_renew to the resume day (today), not retroactively.

Practical timeline example:

  • Subscription paused on May 1, 2026.
  • Resumed on July 1, 2026 (after 2 months).
  • Result: no automatic back-billing for May/June; next_renew is re-anchored to July 1, 2026 (or a future resume_at if provided).

Lifecycle Graph

Proration Rules

  • Positive delta (upgrade): immediate debit charge.
  • Negative delta (downgrade): credit stored in carryover_credit.
  • Carryover credit is consumed in future renewals before charging new amount.

Scheduled Changes

For change-plan and change-addons, you can set when: period_end. The request is stored in scheduled_change and applied automatically at renewal boundary.

Amendment History

Every lifecycle change is written to an immutable amendment stream. This supports support/debug/audit use cases with before/after snapshots and proration metadata.


Data Schema

FieldTypeDescription
ideidUnique customer-subscription identifier.
subscription_idstringPlan identifier currently applied.
statusstringLegacy status (active, dismiss, dismissed, ...).
next_renewdate | nullNext billing boundary.
addonsarrayAddon entries with price and quantity (seat count included).
global_discountobjectPlan-level discount descriptor.
lifecycleobjectAdvanced lifecycle state metadata.
pricing_snapshotobjectCaptured pricing context for lifecycle operations.
scheduled_changeobject | nullDeferred mutation to apply at renewal.
pause_stateobject | nullPause/resume metadata.
carryover_creditnumberCredit wallet generated by negative prorations.
cancel_at_period_endbooleanExplicit cancellation intent at renewal boundary.

Backward Compatibility

No legacy route or payload shape is removed.

Legacy vs New lifecycle behavior

AreaLegacy BehaviorNew Lifecycle Behavior
Attach/DetachExisting routes remain validUnchanged, plus additive lifecycle metadata
Addons updateExisting addon mutation worksOptional lifecycle change-addons with proration/scheduling
Status endpointLegacy status continuesAdds lifecycle visibility without removing legacy keys
Seat pricingAddons with quantitySame model, now explicitly documented as official seat model
Cancellationdismiss based flowcancel_at_period_end maps to compatible legacy status

Relationship Call Graph

On this page