Skip to main content

Terms, Commitments & Billing Behavior

What You'll Learn

  • How the subscription lifecycle flows from activation to termination
  • How OPEN_ENDED and TERMED subscriptions differ in practice
  • How TerminationReason flags 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}/terminate with 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):

ModeJSONWhen 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

ScenarioReason to use
Contract runs its full termEND_OF_CONTRACT
Legal 14-day cooling-off withdrawalRETRACTATION
Subscriber cancels early (after cooling-off)USER_CANCELLATION
Admin corrects a billing errorERROR_CORRECTION
Trial period ended without paymentTRIAL_EXPIRED
Migrating subscriber to new planMIGRATION

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:

  1. Subscriber activates. Trial starts. Day counter begins.
  2. Day 30: autoRenew=true fires. Monthly term starts.
  3. If auto-renewal fails for any reason: TRIAL_EXPIRED terminates with no charges (all flags false).
  4. 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:

  1. Term expires → onExpiry=SUSPEND → SUSPENDED
  2. Send renewal invoice or notice
  3. If subscriber pays → PUT .../reactivate → ACTIVE
  4. 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.