Agreements
An agreement is one negotiated deal on one subscription. It records the term that was agreed, the negotiated rates that hold for it, what happens when it runs out, and how an early exit is treated.
Before agreements existed, that answer was scattered across the individual negotiated rates, each with its own window and nothing tying them together. An agreement is the deal itself, in one row, with one end date.
It is deliberately not called a commitment. Everywhere else in this industry that word means a minimum spend, and spend commitments are a separate object we have reserved the word for. The time lock-in is a term.
An agreement is not a subscription phase, and the two
are easy to confuse because both look like time. A phase says what the
subscription is doing right now, and the billing engine reads it: a trial
phase does not invoice, a paused phase does not meter. An agreement says what
was promised. It never opens, closes or skips a phase, and a phase never
shortens a term, so a paused subscription can sit under an unbroken two-year
agreement.
Agreements are created by signing a quote that carries an agreed term. There is no second authoring path, which is deliberate: a deal nobody offered is exactly the thing this feature exists to make impossible.
The term
The term is the clock. It is a unit and a count, week, month or year times
at least one, and it starts when the subscription starts. The end date is
derived from those two and never stated on its own, so the term and its end
can never disagree.
The agreement's status says where that clock has got to. pending and active
are the live ones; superseded, ended, terminated_early and cancelled are
terminal. At most one agreement is live over any instant of a subscription, and
the database enforces that rather than the application hoping for it.
Each negotiated rate the agreement governs carries its own window, which can
be narrower than the term but never longer. A rate agreed for the second year of
a two-year deal is an ordinary shape, and it is how a
ramp is stored: several rates on one line, tiling the
term. When a rate's window closes, subscription.negotiated_price_expired fires
for that line and it bills the catalog price again.
When the term runs out
Every agreement states one end action, and the choice is total: something always happens on the last day, and the agreement says what.
Renew
Mints a successor agreement starting exactly where this one ends, with the same term, the same notice period and the same early-termination shape, and carries the negotiated rates forward at the same amounts. Use it for a deal that rolls on until somebody stops it: the customer keeps their price, sees no gap, and the successor renews in turn.
Renew once
The same successor, exactly once. The successor's end action is CANCEL, so the
deal runs one more term and then the subscription ends. Use it where a renewal
was negotiated but an open-ended commitment was not.
Continue without term
Nothing happens to the subscription. The negotiated rates lapse on their own windows and the line bills the catalog price from the next period. This is what a signed quote records today, and it is the honest default: the offer agreed a term and said nothing about what follows, so the customer is neither silently re-committed nor silently cancelled.
Switch plan
Moves the subscription to a named successor plan on the day the term ends. The successor plan is required for this action and forbidden for every other, so "switch to nothing" and "a successor nobody will switch to" are both unrepresentable. Use it where the deal was written as an introductory package that becomes a standard one.
Cancel
Ends the subscription when the term ends, with cancellation_reason
AGREEMENT_TERM_ENDED. That reason matters for reporting: a fixed-term deal
running exactly to plan is not churn, and counting it as churn is how a
successful contract looks like a lost customer.
Renegotiate
Changes nothing automatically and puts a person in front of the choice instead.
At notice time it drafts a renewal quote, pre-filled with the rates the customer
actually holds rather than with list price, and starting where the current term
ends. The draft is never sent on its own, because auto-sending would be the
silent renewal this action exists to avoid. If nobody acts on it, the rates lapse
exactly as CONTINUE_WITHOUT_TERM, so the customer is not re-committed by
inaction and you are not still giving away a rate nobody re-approved. It needs a
notice period, because without one there is no moment at which the draft would be
produced.
Only one of the six is reachable from the public API today. A signed quote agrees the term and nothing else, so the agreement it records continues without a term, warns rather than blocks on an early exit, and governs cancellation only. The other five endings, the stricter gates, notice periods and termination charges are real on the agreement and rendered by the dashboard, but the surface that writes them is not public yet. The note at the foot of this page is where to ask about them.
Leaving before the end
Three subscription operations can be governed by a live agreement: cancelling it, decreasing a quantity, and removing an item. An agreement names which of them it covers, and by default it covers cancellation only.
What happens when somebody tries one inside the term is the early-termination gate, and it has three values.
None
The agreement expresses no opinion. The operation proceeds and nothing is recorded, because writing an audit row for every uncontested cancellation would bury the ones that were actually contested.
Warn
The operation proceeds and the attempt is recorded on the agreement as
gate_warned, so somebody can find out afterwards that a committed subscription
was cut short. Use it where the term is a commercial fact rather than a rule the
system should enforce. This is the default.
Block
The operation is refused with 409 CONFLICT until a reason is supplied. Retrying
with an override_reason proceeds and records gate_overridden against the
agreement, together with who did it.
The override is part of the model rather than an escape hatch. A block with no auditable way through is one that gets worked around by editing rows in production, after which nobody can tell that it happened. The reason is required precisely so the audit row is worth having.
The refusal carries the facts needed to decide, as top-level members of the
problem document: agreement_id, committed_until, operation, gate, and,
where the agreement charges one, an early-termination fee with its currency and
a sentence explaining how it was arrived at. "You may not do this" and "you may
not do this without paying 12,000" are different answers, and only the second one
lets somebody decide. The full shape is on Errors.
Cancelling inside a term ends the deal
Whatever the gate says, a cancellation that goes through inside the term ends the
agreement as terminated_early rather than ended. The distinction is the
point: ended means the deal ran its course and its end action applied,
terminated_early means somebody left. Collapsing them would make "how many of
our commitments were actually honoured" unanswerable.
Notice periods
A notice period defers a cancellation rather than refusing it or charging for it. An agreement that asks for three months' notice turns "cancel this today" into a cancellation booked for three months from today, recorded as a scheduled change that is visible before it happens, cancellable while it is pending, and attributable to whoever asked.
Two details decide whether that behaves fairly. The deferred date is clamped to the term's end, so a customer giving ninety days' notice two months before a three-year deal expires is not held for an extra month past the deal they signed. And notice applies to cancellation only: three months' notice is a promise about when the engagement ends, not about when a seat count may change.
The early-termination charge, if the agreement carries one, is raised when the notice runs out rather than when it is given. Until then the customer has not left, and a request withdrawn in the meantime costs nothing.
Co-termination groups
Enterprise deals are often several subscriptions on one contract, and the thing that makes them one contract is that they end on the same day. Agreements in a co-termination group share an end date, and the database refuses two live members with different ones, so members cannot drift apart. Renewals stay in the group, because every member renews on the same date from the same term.
The group changes what a refusal has to say. Cancelling one subscription out of a
five-subscription deal is a different act from cancelling a standalone one, so
the gate reports cotermination_group_size and states both halves of the truth:
this is one of five committed together, and the other four are not affected by
this request. Omit the first half and somebody cancels believing four others
follow; imply the second and somebody abandons a cancellation they were entitled
to make.
See it in the dashboard
-
Open the subscription
In the sidebar, under Sales, click Subscriptions, or press G then S. Open the subscription you want.
-
Read the rail
A subscription under a live agreement carries a Committed term group in the right-hand rail: Committed until the end date, At term end and the end action in plain words, and Ending early with what the gate does. A subscription with no live agreement shows nothing there, which is nearly all of them.
-
Open the Agreements tab
The Agreements tab lists every agreement the subscription has ever been under, newest term first, with its Term, Committed period, Status, At term end and Ending early. A subscription with none says so rather than hiding the tab.
-
Expand one
The caret at the end of a row opens Rates it governs, each with the window it holds for, and History, the agreement's audit trail from created through to term ran out.
-
Try to leave early
Cancelling, reducing a quantity or removing an item on a blocked agreement opens This subscription is committed, which states the date, the fee if there is one, and how many subscriptions are committed together. It asks Why is this happening early? and stores the answer with your name. Override and continue proceeds.
Use it over the API
An agreement is authored by agreeing a term on a quote. What you do with it afterwards is cancel, reduce or remove against the subscription, with a reason when the gate asks for one.
Send the same override_reason in the body of
PATCH /v1/subscriptions/{id}/items/{itemId} when reducing a quantity, and as a
query parameter on DELETE /v1/subscriptions/{id}/items/{itemId} when removing a
line. It is ignored where no agreement governs the operation, so a client can
always send one.
Two events carry the term's own lifecycle. agreement.term_ending_soon fires
once, the agreed number of days before the end, carrying how many days are left
and what is about to happen. agreement.term_ended fires once the end action has
been applied, and says what it did and whether the subscription changed. See
Webhooks and events.
Dashboard and API names
| In the dashboard | In the API |
|---|---|
| Committed term rail group | the live agreement on the subscription |
| Term | term_unit and term_count |
| Committed period | starts_at and ends_at, the second derived from the term |
| At term end | action_at_term_end |
| Moves to the successor plan | action_at_term_end: "SWITCH_PLAN" plus successor_plan_id |
| Ending early: Allowed | early_termination_gate: "NONE" |
| Ending early: Allowed with a reason | early_termination_gate: "WARN" |
| Ending early: Needs an override reason | early_termination_gate: "BLOCK" |
| Why is this happening early? | override_reason on cancel, item update and item removal |
| Status: In force / Ran its term / Ended early | status: active / ended / terminated_early |
| Rates it governs | the negotiated rates carrying this agreement_id |
| History | the agreement's events, from created to term_ended |
Where agreements go next
- Write the term on a quote. It is the only way an agreement comes into being, and the term is what a ramp needs to exist.
- Subscribe to
agreement.term_ending_soonso the renewal conversation happens before the price changes rather than after. - Read
cancellation_reasonbefore counting churn.AGREEMENT_TERM_ENDEDis a deal that ran to plan, and subscriptions treats it as its own reason for that purpose. - Handle the
409in whatever you have built on cancellation, because a committed subscription answers differently from an uncommitted one.
Next step
Reference
| Topic | When you need it |
|---|---|
| Quotes | Agreeing a term, and ramping a rate inside it |
| Subscriptions | What a term changes about cancelling |
| Errors | The gate's 409 and every member it carries |
| Webhooks and events | agreement.term_ending_soon and agreement.term_ended |
| Scheduling | Where a deferred cancellation waits |
| Glossary | Term, ramp, gate, notice period, co-termination |
Not everything is public yet
The endpoints that read an agreement back are internal while the vocabulary proves itself against real deals, so the dashboard shows a deal the public API does not yet return, and writing one means agreeing a term on a quote. If you need either from your own system, email engineering@kontier.eu and we will talk you through the options.