Versioning
Six things in Kontier carry versions: plans, products, promotions, tax rules, rate tables and price formulas.
Versioning exists so that editing something you already sell is safe. Change a price, publish, and customers who bought the old version keep it until you decide otherwise. An edit is never a surprise bill.
You mostly do not have to think about it. Edit, publish, done. This page is for when you do.
What is not versioned
Prices, meters and costs are not versioned, and neither are customers, subscriptions or invoices.
That is deliberate rather than missing. A price belongs to a product, and the product's version is what freezes it. An invoice is immutable once finalised, so a version would add nothing. A subscription does not need versions because it pins one.
The lifecycle
Four states, identical on all six entities.
| State | Meaning |
|---|---|
draft | Being edited. Not visible to customers |
scheduled | Published with a future effective_at, waiting |
published | Live. This is what customers get |
archived | No longer in play |
Archiving records why, which is the field to read when reconstructing what
happened: canceled, discarded, errored or superseded.
The parent row and the version row use different words
A plan's own status says active where its version row says
published. Same idea, two vocabularies, because the parent and the version
are different rows. Read the one you are actually querying.
Publishing is a PATCH
There is no /publish route. You publish by updating the entity and saying when
the change takes effect.
| What you send | What happens |
|---|---|
PATCH with no effective_at | Published immediately |
PATCH with effective_at in the future | Version becomes scheduled |
PATCH with save_as_draft | Stays a draft. Nothing goes live |
Price formulas are the exception: they have no scheduling at all, only draft
and published, so save_as_draft works but effective_at does not.
This is not the same machinery as scheduled changes
Scheduled changes and version publishing both use the words release, cancel and reconfirm, and they mean different things by them. A scheduled change alters one subscription later; a scheduled version alters what the catalog offers from a moment onwards. The only place they meet is migrating subscribers.
Before you publish
Three read-only calls answer "what will this do", and they are worth the ten seconds.
| Call | Answers |
|---|---|
GET …/versions/{version}/publish-impact | How many subscriptions would move, and how many are pinned |
GET …/versions/{version}/referrers | What else depends on this version |
GET …/versions/{version}/usage | How much this version is actually being used |
publish-impact comes in two shapes sharing one name. At version level it
answers about that version; at entity level, on plans and products only, it
answers about the whole thing. Check which one you called.
What pins a version
A consumer either follows the latest version or holds still.
- A subscription pins the plan version it was sold on. Publishing a new plan version does not move it.
- A plan membership chooses per product:
product_track_modeislatestorpinned. - A subscription item can carry a
version_pin.
The default is pinned, whatever the schema says
subscriptions.version_track_mode has a column default of latest, but no write
path ever reaches it. The effective default through the API is pinned. If
you need subscriptions to follow the catalog, set it explicitly.
What each entity can do
The six share a lifecycle, not a feature set.
| Plans | Products | Promotions | Tax rules | Rate tables | Formulas | |
|---|---|---|---|---|---|---|
| List versions, snapshot, usage, referrers, publish-impact | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Timeline | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Cancel, reconfirm | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Unschedule, release | ✅ | ✅ | ✅ | — | ✅ | — |
effective_at scheduling | ✅ | ✅ | ✅ | ✅ | ✅ | — |
save_as_draft | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Copy the entity | ✅ | ✅ | ✅ | ✅ | — | — |
| Create, delete, copy or archive a version | — | — | — | — | ✅ | — |
The gaps are deliberate, not unfinished:
- Tax rules cannot unschedule or release, because a tax rule row is its own version. There is no separate draft to release.
- Price formulas have no scheduling because
scheduledis never written for them; a formula version is draft or published. - Rate tables are the only entity where you manage versions directly, because a rate table is a body of data rather than a configuration.
Where versioning shows up
| Page | What it adds |
|---|---|
| Plans | The version you meet first, and moving subscribers |
| Products | Product versions and what a plan membership tracks |
| Promotions | Publishing an offer |
| Taxes | Dated rules, where valid_from and the version interact |
| Rate tables / Formulas | Versioned pricing inputs |
| Scheduling | The other mechanism, and why it is not this one |