Terms, Commitments & Billing Behavior
What You'll Learn
- How the subscription lifecycle flows from activation to termination
- How
OPEN_ENDEDandTERMEDsubscriptions differ in practice - How
TerminationReasonflags translate into real invoice lines - The four most common configurations with ready-to-use JSON
- What actually happens at each branch of the expiry decision tree
- SUSPEND vs TERMINATE — when to use which
Subscription Lifecycle
Every subscription follows the same path. What varies is what happens at term expiry.
[Offer subscribed]
|
v
ACTIVE (billing runs every cycle)
|
v
[Term expires?]
|
+--[OPEN_ENDED]-----------> No expiry — runs forever until cancelled
|
+--[TERMED, autoRenew=true, renewalTerm defined]--> [Renews for renewalTerm]
| |
| ACTIVE (next term)
|
+--[TERMED, autoRenew=true, no renewalTerm]--------> OPEN_ENDED (minimum met)
|
+--[TERMED, autoRenew=false, onExpiry=SUSPEND]-----> SUSPENDED
| (billing halts, reactivatable)
|
+--[TERMED, autoRenew=false, onExpiry=TERMINATE]---> RESILIATED
(permanent, terminationReason applied)
OPEN_ENDED: No Commitment
The simplest possible configuration. No expiry date. Billing runs until someone cancels.
{
"termsAndConditions": {
"type": "OPEN_ENDED"
}
}
What happens in billing
- Charges fire every billing cycle (e.g., monthly) with no end in sight
- To stop: call
PUT /api/v1/subscriptions/{code}/terminatewith a reason - No penalties, no prorating by default — depends on the termination reason used
When to use
- Month-to-month SaaS
- Pay-as-you-go usage plans
- Internal services with no contract obligation
TERMED: Fixed Commitment
The subscriber commits for a defined period. The offer defines what happens when that period ends.
initialTerm Modes
Pick exactly one mode per term (initial and renewal terms are independent):
| Mode | JSON | When to use |
|---|---|---|
| Duration | { "duration": 12, "unit": "MONTH" } | 90% of cases |
| Fixed date | { "endDate": "2027-12-31" } | Fiscal year contracts |
| Calendar | { "calendar": "CAL_FISCAL_YEAR" } | Calendar-boundary alignment |
Note: renewalTerm does not support endDate — use duration or calendar for renewals.
What happens at term expiry
Term expires
|
+-- autoRenew=true + renewalTerm --> New term starts (ACTIVE)
|
+-- autoRenew=true + no renewalTerm --> Subscription becomes OPEN_ENDED
|
+-- autoRenew=false + onExpiry=SUSPEND --> Status: SUSPENDED
| Billing halts
| No charges, no reimbursements
| Can reactivate with PUT .../reactivate
|
+-- autoRenew=false + onExpiry=TERMINATE --> TerminationReason applied
Status: RESILIATED
Final invoice if generateImmediateInvoice=true
TerminationReason: Billing Flags Explained
When a subscription terminates — whether naturally at end of term or through an API call — the TerminationReason code controls exactly which billing actions fire. Think of it as a billing behavior preset.
The six flags
applyRecurringChargesUpToCommitmentEndDate → Charge remaining months (positive)
applyTerminationPenaltyCharges → Charge penalty fee (positive)
prorata → How to handle partial periods
reimbursePrepaidRecurringCharges → Refund unused recurring charges (negative)
reimburseOneShotSetupFees → Refund setup fees (negative)
generateImmediateInvoice → Invoice now, don't wait for next cycle
How invoice lines are created
CHARGE ADJUSTMENTS (positive lines)
applyRecurringCharges* → Invoice lines for remaining commitment period
applyTerminationPenalty* → One-shot penalty charge lines
prorata controls → how much of the period is charged
ACCOUNT OPERATIONS (negative lines)
reimbursePrepaidRecurring* → Credit for unused invoiced period
reimburseOneShotSetupFees* → Credit for setup fees paid
Concrete examples
Subscriber terminates after 3 months of a 12-month contract:
With USER_CANCELLATION (applyRecurringChargesUpToCommitmentEndDate=true, applyTerminationPenaltyCharges=true):
- Invoice line: 9 months of recurring charges (months 4–12)
- Invoice line: penalty fee (configured on the service charge)
- Result: subscriber owes the remaining commitment
With RETRACTATION (reimbursePrepaidRecurringCharges=true, reimburseOneShotSetupFees=true, prorata=PRORATA, generateImmediateInvoice=true):
- Already paid month 1 and 2: only month 3 partial usage was consumed
- Credit line: unused days in month 3
- Credit line: setup fee refund
- Immediate invoice generated
- Result: subscriber gets money back (legal cooling-off, within 14 days)
Normal end of 12-month contract:
With END_OF_CONTRACT (all flags false):
- No additional charges
- No reimbursements
- Subscription simply terminates cleanly
- Regular billing cycle handles the final period
Seed data: which reason to use when
| Scenario | Reason to use |
|---|---|
| Contract runs its full term | END_OF_CONTRACT |
| Legal 14-day cooling-off withdrawal | RETRACTATION |
| Subscriber cancels early (after cooling-off) | USER_CANCELLATION |
| Admin corrects a billing error | ERROR_CORRECTION |
| Trial period ended without payment | TRIAL_EXPIRED |
| Migrating subscriber to new plan | MIGRATION |
Common Configurations
Trial then Paid (30-day trial → monthly subscription)
{
"type": "TERMED",
"initialTerm": { "duration": 30, "unit": "DAY" },
"autoRenew": true,
"renewalTerm": { "duration": 1, "unit": "MONTH" },
"onExpiry": "TERMINATE",
"terminationReason": "TRIAL_EXPIRED"
}
Flow:
- Subscriber activates. Trial starts. Day counter begins.
- Day 30:
autoRenew=truefires. Monthly term starts. - If auto-renewal fails for any reason:
TRIAL_EXPIREDterminates with no charges (all flags false). - From month 2 onwards: standard monthly billing.
Pro tip: Set trial charge pricing to 0 via a FLAT pricing version — the trial period produces zero-value invoice lines, confirming activation without charging.
Annual Contract with Penalties
{
"type": "TERMED",
"initialTerm": { "duration": 12, "unit": "MONTH" },
"autoRenew": false,
"onExpiry": "TERMINATE",
"terminationReason": "END_OF_CONTRACT",
"notifyDaysBefore": 60
}
For early exit by the subscriber, the runtime termination call passes USER_CANCELLATION instead:
PUT /api/v1/subscriptions/{code}/terminate
{ "reason": "USER_CANCELLATION" }
This charges the remaining months + penalty, regardless of what terminationReason is on the offer.
Month-to-Month (no commitment, clean billing)
{
"type": "OPEN_ENDED"
}
At cancellation time: PUT .../terminate { "reason": "USER_CANCELLATION" }. No remaining months to charge since there was no commitment. The applyRecurringChargesUpToCommitmentEndDate=true flag has no effect when there is no commitment end date.
12-Month Minimum, Then Open-Ended
The most popular commercial configuration for professional services:
{
"type": "TERMED",
"initialTerm": { "duration": 12, "unit": "MONTH" },
"autoRenew": true,
"onExpiry": "SUSPEND",
"notifyDaysBefore": 30
}
No renewalTerm means: after month 12, the subscription continues without an expiry date. The subscriber has served their minimum commitment and can now cancel anytime without penalties (assuming USER_CANCELLATION has applyRecurringChargesUpToCommitmentEndDate=false — which it does not by default, so configure a custom reason if needed).
SUSPEND vs TERMINATE: Deep Dive
Choosing between SUSPEND and TERMINATE for onExpiry is the most impactful decision in terms configuration.
SUSPEND
Status: SUSPENDED
Billing: halted (no charges until reactivated)
Reactivation: PUT /api/v1/subscriptions/{code}/reactivate
Effect on data: none — subscription and its history are preserved
Use SUSPEND when:
- You want a grace period before permanent termination
- The subscriber might renew after reviewing their contract
- You run an annual renewal workflow and send invoices before reactivation
- You want to avoid accidental permanent terminations due to system issues
After SUSPEND, billing history: The subscription was ACTIVE from activation to suspension date. No charges accumulate during SUSPENDED period. On reactivation, billing resumes from the reactivation date.
TERMINATE
Status: RESILIATED (French: résilié)
Billing: final billing actions per terminationReason
Reactivation: not possible — must create new subscription
Effect on data: subscription is archived
Use TERMINATE when:
- The contract definitively ends with no expectation of renewal
- You need to apply final charges (remaining commitment, penalties)
- Legal requirements demand a clean termination record
After TERMINATE, billing history: The terminationReason's flags determine the final invoice lines. If generateImmediateInvoice=true, an invoice is produced immediately. Otherwise, charges appear on the next billing run.
Hybrid pattern: SUSPEND then TERMINATE
Many platforms use SUSPEND as a transitional state before permanent termination:
- Term expires →
onExpiry=SUSPEND→ SUSPENDED - Send renewal invoice or notice
- If subscriber pays →
PUT .../reactivate→ ACTIVE - If subscriber does not pay within X days →
PUT .../terminate { "reason": "USER_CANCELLATION" }→ RESILIATED
This is implemented via a dunning workflow or a scheduled job — not by the offer configuration itself.
FAQ: What Happens If...
Q: The subscriber cancels on day 1 of month 2 of a 12-month contract.
With USER_CANCELLATION: 10 months of recurring charges + penalty fee billed immediately (if generateImmediateInvoice=true on the reason, otherwise on next cycle).
With RETRACTATION (within 14-day cooling-off): full refund of any charges paid, no further billing.
Q: autoRenew=true but the payment method is expired.
The renewal timer fires and attempts to extend the term. If the extension succeeds at the subscription level (it always does — payment is a separate step), billing continues. The payment failure is handled by the dunning workflow, not by terms configuration.
Q: What if I set onExpiry=TERMINATE but also autoRenew=true?
autoRenew=true takes priority. The onExpiry action only fires when renewal does NOT happen (i.e., autoRenew=false, or autoRenew=true but no renewal term defined and the subscription was already OPEN_ENDED). If both are set with a renewalTerm, onExpiry is a safety fallback if the auto-renewal system fails.
Q: Can I change termsAndConditions on an ACTIVE offer?
No. Terms are locked once an offer is ACTIVE. To change terms, you must: close the existing offer, create a new offer with updated terms, and migrate subscribers via the subscription change API. This is by design — changing terms on an active offer would affect existing subscriber commitments.
Q: Does SUSPEND stop ALL charges?
Yes. A SUSPENDED subscription generates zero charges. Usage events (CDRs) are still ingested and stored, but they are not rated until the subscription is reactivated. On reactivation, depending on configuration, historical EDRs may or may not be re-rated.
Q: What is alignEndDate?
When alignEndDate=true, the commercial agreement's end date is aligned to the subscription's subscribedTillDate. This affects reporting and agreement-level billing summaries. Default is false — use true when the offer is tied to a master commercial agreement.