Subscriptions
A subscription sells recurring products to a customer for a committed term (1 to 60 months), billed monthly, quarterly or yearly, in advance or in arrears. A second person approves its terms; the approval activates the term's revenue contract, and the billing run invoices each period against that contract, so what is billed goes to the contract liability and the period-end recognition run books the revenue month by month. The design is ADR-0036 section 4; this guide is slice 4 as built, with ending a term early and renewals (ADR-0047). Upsells, downsells and price changes of an active subscription are still to come. The controls are in the control matrix (section "Subscriptions", SB1 to SB5).
Everything is a REST operation first (/v1, tag "subscriptions" in the
generated reference); the MCP tool list_subscriptions reads the same
operations.
The subscription
The New subscription form says under each field what goes in it and offers the usual values with one click: a start of today or the 1st of next month, a term of 12, 24 or 36 months, a renewal notice of 30, 60 or 90 days and an uplift of 3 or 5 %.
POST /v1/subscriptions {entity, party, title, start_date, term_months, billing_frequency?, billing_timing?, auto_renew?, renewal_notice_days?, renewal_uplift_percent?, payment_terms_days?, auto_submit?, opportunity?, lines}
GET /v1/subscriptions ?entity&status&party_id
GET /v1/subscriptions/{subscription_id}
POST /v1/subscriptions/{subscription_id}/cancel {reason}
A subscription is created as a draft by anyone who may draft, an agent too, and numbered
SUB-000001 per entity. The term is a whole number of billing periods (a quarterly subscription runs 3,
6, 9 ... months) and ends the day before its months are up: 12 months from 16 January end on 15 January.
A subscription starts in an open period: one back-dated into a closed period is refused when it is
created and when its terms are approved. Each line names a product that is recurring, active and
not goods, and has a standalone selling price on the start day; quantities and prices have at most six
decimals, percentages at most four; the line's price per month or year is the product's list price on that day
unless the line gives its own unit_price, less its discount_percent. The description and revenue
account default to the product's. The subscription bills in a currency: currency as given, else
the won deal's currency, else the customer's, else the entity's functional currency
(ADR-0050). Its prices, invoices,
credit notes, renewals and revenue contracts are all in that currency, and each product needs a list
price (or the line its own unit_price) and an SSP in it on the start day: a price is never converted.
A product priced only in other currencies is refused with subscription_currency_unpriced, which names
the currencies it has prices in; a missing SSP with subscription_ssp_missing. A currency the ECB rates
do not cover is refused with fx_rate_missing. The approval policy compares the term's amount in the
functional currency (at the rate of the start day, or of the day it was drafted when it starts later).
Creating it
builds its first invoice without keeping it, so a customer's VAT treatment or an account that cannot
work is found now. A draft's terms are not edited: cancel it (a reason is kept) and create a new one.
What a term costs, term_amount, is each line's monthly amount (quantity times the price per month,
less the discount, kept exact) times the term's months, rounded once to the currency.
Approval and the term's revenue contract
POST /v1/subscriptions/{subscription_id}/activate
POST /v1/approvals/{approval_id}/approve {comment?}
activate asks for the terms to be approved. A second person always decides: never the person who
drafted the subscription or asked, never an agent (the policy action approve_subscription), and never
the requester alone even where an organization's policy allows the action; a policy that denies it
refuses the request. The approval is bound to the digest of the terms. The database holds the same
rule: it activates a subscription only from pending_approval, approved by a person who did not create
it, through an approved request someone else made, with the term's own contract; the lines and terms
of a subscription never change. Approving, in one transaction:
- creates the term's revenue contract (
RC-2026-000001): the term's dates, the transaction price the term's amount, and one over-time obligation per line, recognised by themonthlymethod, with the line's product and quantity, so its SSP is the product's over the term and the price is allocated by relative SSP. Billing in arrears makes the right to consideration unconditional (only time stands before each invoice), billing in advance conditional; - activates that contract, which writes its schedule;
- makes the subscription
active, with the contract and the first period to bill (next_bill).
A rejection returns the subscription to draft. When every line is priced at its product's SSP the
allocation equals the line prices and each month's revenue is what is billed for it. A line sold below
its SSP moves revenue between the obligations, as IFRS 15.74 requires: 10 seats at CHF 50.00 a month
less 10 % and an add-on at CHF 20.00 (SSP 25.00) bill CHF 450.00 and 20.00 a month, while the contract
allocates CHF 5,371.43 and 268.57 over the year. The billing cap is the contract's, so each invoice may
bill its list prices.
Billing
POST /v1/subscriptions/run {through, entity?, subscription?}
GET /v1/subscriptions/{subscription_id}/invoices
The run invoices every billing period of an active subscription whose invoice date is on or before
through (at most a day after the server's date): in advance on the period's first day, in arrears on
the day after its last. When that day is in a closed period, the invoice is dated the first day of the
next open period (its lines still name the service period), so closing a period never stops billing. Each invoice has a line per subscription line for the period's part
of the line's term amount (the difference of two cumulative amounts, so the periods of a term add up to
it exactly), shown as the line's quantity at the period's unit price, discount included (25 seats x 38.00;
1 x the amount when no unit price of six places multiplies to it exactly), naming its obligation: Dr receivables / Cr contract liability / Cr VAT. The product gives
the VAT treatment. Without auto_submit the invoice is a draft for a person to submit; with it, the
system submits it through the approval policy, the open period and the numbering. A run's draft bills
what was approved: its customer, currency, cash discount, products, quantities, prices, obligations,
accounts and what decides its VAT (supply type, rate type, tax category, VAT scope) cannot be
changed (its dates and notes can). To bill otherwise, void the draft: the next run makes that period
again. The invoices list shows what the run did (status) and each invoice's own status now
(document_status).
The run is idempotent: one invoice per subscription and period, enforced by the database, and a
subscription is locked while it is billed. A period that cannot be made (an inactive customer, the
contract's billing cap after an invoice billed the obligation by hand, terms that no longer match the
approved digest) is recorded as failed with its error and stops that subscription at that period,
never the others in the run; the next run tries it again. The run is a person's request: an agent is
refused, since with auto_submit it posts invoices. At most twelve periods of one subscription are billed in a run (more_due says to
run again). A fully billed subscription stays active until its term ends; the first run after that
renews it (see Renewals) or, when nothing renews it, marks it ended once every invoice it
made is posted: a draft may still be voided, and the next run then makes that period again. A period
whose invoice date is past through is left for a later run before anything else is looked up, so a
next period in a fiscal year not opened yet never stops a run; one a run does reach there is made as a
draft (posted once the year is opened) or, with auto_submit, fails that subscription only. A late
invoice goes to the next open regular period, never a one-day adjustment period.
The close check subscriptions_billed (a warning) names subscriptions with a period due through the
period's end and not billed, and invoices a run made through it that are not posted (a draft, waiting
for approval, rejected or voided). It does not block the close: a late period is invoiced in the next
open period. The period-end recognition run
recognises the term's revenue month by month and presents what was recognised and not billed (in
arrears) as an unbilled receivable; the run of the term's last month marks its contract completed
once every month is recognised. The subscription shows ended from its own billing run, without
waiting for the contract.
Ending a term early
POST /v1/subscriptions/{subscription_id}/amendments/preview {kind: "cancel", end_date, proration?, reason}
POST /v1/subscriptions/{subscription_id}/amendments {kind: "cancel", end_date, proration?, reason}
GET /v1/subscriptions/{subscription_id}/amendments
An active subscription is not cancelled: its current term is ended early by an amendment that a
second person approves, as its terms were (approve_subscription, never the person who asked, never an
agent). end_date is the last day of service, within the current term. proration says what happens
to what was already billed past that day: prorate (the default) credits its unused part, none keeps
it as a non-refundable prepayment. The preview and the request show the term's price before and after,
the part earned through the last day and a credit per billed period, worked out from what is posted now;
the approval is bound to those amounts, and approving refuses with terms_changed when they moved in
between (reject it and ask again). An amendment is refused while an invoice of the subscription is not
posted, when revenue is already recognised after the last day (an end is prospective), or while
another amendment waits.
Approving, in one transaction: billing stops after the last day (the period it falls in, if not billed
yet, is billed for its days: 10 seats at CHF 50.00 ended on 20 June bill round(500.00 x 20/30) =
333.33 for June); with prorate the system posts a credit note for each period billed past the last
day, its lines naming the obligations (Dr contract liability / Dr VAT / Cr receivables); and the
term's revenue contract is modified prospectively: its price becomes what the customer now pays for
the term, its end the last day, the schedule lines not yet recognised are superseded and what is left is
spread over the remaining days, recognised revenue stays. Billing and revenue count a month the same
way, from the subscription's start day: a subscription from 15 February has months from the 15th to the
14th, so its schedule books 14 of the 28 days from 15 February (250.00 of 500.00) for February and
14/28 + 17/31 of a month for March, and ended on 31 May it has earned 3 months and 17/31 of the fourth,
exactly what its schedule says and what is left billed after the credit. With none, the part of the prepayment for
service not given is recognised on the last day. The subscription shows ends_on, and the first run
after that day marks it ended. Ending on the term's last day stops a renewal without changing
anything else. On screen: End early in a subscription's dialog.
Changing the plan within a term
POST /v1/subscriptions/{subscription_id}/amendments/preview {kind: "change", effective_date, lines, reason}
POST /v1/subscriptions/{subscription_id}/amendments {kind: "change", effective_date, lines, reason}
An upgrade or a downgrade (more or fewer seats, another product, price or discount) is a change
amendment a second person approves, as every amendment (ADR-0059). lines are the subscription's
lines from effective_date on, the ones that stay named again (a price left out is the list price that
day); effective_date is after the first day of the lines in force and at the latest the term's last
day. The term, the billing frequency and the timing stay.
Approving, in one transaction, ends the current lines the day before exactly as an early end with
prorate does (what was billed past that day is credited, a credit note per invoice, and their revenue
contract is modified to end that day) and starts the new lines that day with a revenue contract of
their own for the rest of the term, activated in the approver's name. The first period of the new
lines is cut at their first day, so it charges only the rest of that period; when its invoice date has
come it is billed at once. Example: 10 seats at CHF 50.00 a month, billed in advance from January; on
16 March 12 seats from that day. March's invoice is credited 258.06 (16 of 31 days at 500.00), the
new lines invoice 309.68 for 16 to 31 March (600.00 x 16/31) and 600.00 a month from April. The old
contract ends on 15 March at 1,241.94, the new one runs from 16 March at 5,709.68, and March recognises
241.94 + 309.68. A change from the first day of the next period credits and charges nothing in between.
In arrears, or when an invoice of the current lines is not posted yet, the subscription keeps
changes_on (the new lines' first day) until the current lines are billed and posted through the day
before; the billing run then moves to the new lines. While changes_on is set no other amendment is
asked. A subscription in another currency changes in that currency (its contracts too). On screen:
Change plan in a subscription's dialog: from today, the next period or another day, the new lines,
and the preview of the credit, the first charge and the new lines' price to the term's end.
Renewals
POST /v1/subscriptions/{subscription_id}/amendments {kind: "renew", reason}
A renewal starts the next term the day after the current one ends: as long as the term, its months
counted from the subscription's start day (a one-month term from 31 January renews on 28 February, then
31 March, never drifting to the 28th), billed at the prices in force raised by renewal_uplift_percent
(each price rounded to the cent: 50.00 + 3 % = 51.50, then 53.05), with its own revenue contract: a
renewal is a new contract, never a modification. With an uplift, each line is closed on the term's last
day and a line at the new price opens the next day.
- With
auto_renew, the approved terms are the authority: the first billing run after the term's last day, once every period of the term is billed, starts the next term (its contract activated by the system) and bills its due periods. - Without, a person asks for the renewal (Renew in the subscription's dialog; the preview shows the
next term's days and price) and a second person approves it, as an amendment. Approved before the
term ends, the run starts the next term after its last day; approved after, it starts at once. The
contract is activated in the approver's name. Without a renewal the run ends the subscription; while a
renewal request waits, it does not. A renewal that was due is not lost when the run ended the term
first: a subscription ended with its term (not ended early) still shows its renewal
due, Renew stays in its dialog, and the approval makes itactiveagain and starts the next term at once, the day after the old one ended (its periods are billed by the next run).
renewal_status says what happens next: auto, approved (with renews_on), requested, due (from
renewal_notice_days before the term's last day, renewal_due_on, while nothing renews it; the list
marks it renewal due), upcoming, or ends for a term ended early. term_no counts the terms. The
database starts a term only with its subscription_renewal row: the next term counted from the start
day, of an active subscription not ended early, with an active contract for exactly its days, under
auto_renew or the applied renewal of the term before.
Renewal deals and recurring revenue
A subscription whose renewal is due gets a renewal deal in the pipeline, won or lost with the renewal, and its MRR counts in the recurring revenue report: see recurring revenue.