Plans
A plan is what a customer actually subscribes to. It bundles priced products into one package: a POS subscription fee, a per-transaction charge, and an email allowance sold together as Retail Suite.
Products and prices describe what things cost. A plan decides what is sold together, how much of each is included in the base price, and how much a buyer may take. A product can appear in as many plans as you like, at different allocations in each.
Plans are versioned
Versioning exists so that changing a plan is safe. You edit a working draft, and nothing you change there reaches checkout or a subscription until you publish it. Each publish produces a new version: v1, v2, v3. New subscriptions are drawn from the version that is live.
The working draft
Two kinds of edit behave differently:
| Edit | Where it lands |
|---|---|
Display fields on PATCH /v1/plans/{id}: name, description, public_description, price_cadence_unit, metadata, translations | Live on the plan immediately |
The composition: attaching, changing or removing products (POST /v1/plans/{id}/products, PATCH / DELETE /v1/plans/{id}/products/{productId}) and the plan defaults default_timing, default_version_change, default_early_stop, billing_alignment | Staged on the working draft; goes live with the next publish |
GET /v1/plans/{id}/draft shows what is staged, and DELETE /v1/plans/{id}/draft
throws it away. PATCH never publishes: sending "status": "active" is refused
with PUBLISH_IS_A_CHANGE, and there is no effective_at, save_as_draft or
publish_now field. The only status PATCH can set is archived, which retires
the plan for good.
GET /v1/plans/{id}/draft shows the gap between the draft and the live version.
In the screenshot below, a product that is attached but not yet published is
badged Not offered.
So the one habit worth forming: after editing a plan, publish it. If something you added is not appearing in checkout, that is almost always why.
Publishing
Publishing is a change: POST /v1/changes with the operation
catalog.publish_version. It takes the draft live now, or at a wall-clock
time in a named time zone.
Publish the draft now
Publish it at midnight Vienna time on 1 January
at is a wall clock, not an instant: send it with a Z suffix and name the
zone separately in zone (an IANA name). The example fires at 00:00 in Vienna,
not at 00:00 UTC. A when: "now" publish answers with status: "applied": the
new version is live when the response arrives (unless you add
"gate": "approval", which holds it until a second person approves it; that
decision is made in the dashboard, not through the API).
A scheduled publish freezes the draft into a pending version. It shows on the
plan under scheduled_versions, each entry with its change_id, provisional
version number and scheduled_for. The working draft is free again for the
next edit while it waits.
| To | Call |
|---|---|
| Move a scheduled publish to another time | PATCH /v1/changes/{id} with a new when (and at + zone) |
| Publish it right away instead | PATCH /v1/changes/{id} with "when": "now" |
| Cancel it | DELETE /v1/changes/{id} |
Cancelling cancels the release, not the edit: the content stays staged on the
working draft, ready to publish later or to discard with
DELETE /v1/plans/{id}/draft. A change that has already run or been retired
answers 409 CHANGE_NOT_EDITABLE.
Before you publish
| Call | Answers |
|---|---|
GET /v1/plans/{id}/publish-preflight | Would a publish right now succeed? ok, or the refusal code and message |
GET /v1/plans/{id}/publish-impact | How many active subscriptions would move, how many hold a pinned line, and when the movers move (by version change) |
POST /v1/changes/preview | The same body as POST /v1/changes, answered with who moves, who does not and why, and the money delta, without writing anything |
GET /v1/plans/{id}/versions/{version}/followers | What else would publish because this one does |
History
GET /v1/plans/{id}/history lists the published and scheduled versions, newest
first, each with its publisher, release name, when it took or takes effect and a
readable diff against the version it replaced. GET /v1/plans/{id}/timeline
lists the versions in the order they fired and will fire. Name or annotate a
version afterwards with PATCH /v1/plans/{id}/versions/{version} (release_name,
release_note); that never creates a version.
What publishing does to existing subscriptions
There is no "migrate subscribers" call. Whether a subscription moves to a new
plan version is decided per subscription line, by its track attribute:
follow: the line moves to the newest version of its plan when one is published.pinned: the line keeps the version it was sold on. Publishing never touches it.
A line that states no track inherits one, in this order: the value stated on
the subscription's revision (on this phase or carried from an earlier one); the
plan version's own rule for that product (its product_track_mode: latest
reads as follow, pinned as pinned); and finally the subscription's default
track, which is pinned unless you change it with the subscription.set_default
change. A line drawn from a bare product price (origin product) has no plan rule,
so it falls straight to the subscription default. A subscription's live.effective_track (include=live) reports the result across
the lines in force: follow, pinned or mixed.
When a following line moves is its version_change attribute:
version_change | The line moves |
|---|---|
immediate_prorate | At the instant the version takes effect, pro-rated onto the current period |
next_period | At the start of the next billing period |
at_phase_change | At the subscription's next phase change |
A line without one takes the plan's default_version_change, else the
subscription's default, else next_period.
Publishing can bill following subscribers
If lines follow the plan and resolve to immediate_prorate, the publish itself
prorates the difference onto the current period. That is a charge (or credit)
the customer did not ask for. Check publish-impact first. Its strategies
block counts the movers per version change.
To pin or release a line on a live subscription, write its track value on a
new revision (see Subscriptions), or schedule it with a
change (subscription.transform with kind set_track; the
subscription's defaults with subscription.set_default).
What each product adds
Every product on a plan carries its own allocation settings. These four columns are where most of the modelling happens.
Included quantity
How much of this product is bundled into the base price before per-unit billing
starts. A plan that includes 10,000 emails sets included_quantity to 10000;
the customer is billed only for what they use beyond it.
Leave it empty and every unit is billed.
Default quantity
What a new subscription starts with when the caller does not say. For a seat product this is the number of seats a fresh subscription is created with.
It is a starting point, not a limit: Min and Max quantity bound what a buyer may actually pick.
Rollover
Whether unused included allowance carries into the next period instead of expiring. Off by default, which is what most plans want. When on, you can cap the accumulated balance and expire it after a set number of periods.
Version
Whether this membership follows the product's latest version (latest) or
holds a pinned one (pinned). With latest, publishing the product republishes
the plan with it. Pin it when a price change on the product must not reach this
plan on its own. Omitted, it takes the workspace default, which is latest.
The same value is what a following subscription line inherits as its track
(see above).
Create it in the dashboard
Over the API, this is POST /v1/plans; see
Create it with the API.
-
Open the plans list
In the sidebar under Billing, click Plans.
-
Create the plan
Click Create Plan. Give it an internal Name, and a Public Description if buyers will see it in checkout or the portal.
A plan starts empty, as a draft -
Attach your products
On the plan's Products tab, click Add Product for each product the plan sells, and set its allocation settings as you go.
-
Publish it
Use Publish, now or at a date. This is the step that puts your changes on sale; everything before it is a draft.
Create it with the API
Three calls: create the plan, attach each product with its allocation, then publish (shown above).
A plan's status is draft until its first publish, then active; archived
once you retire it. A pending release never changes it: look at
scheduled_versions for that.
Dashboard and API names
| In the dashboard | In the API |
|---|---|
| Name | name, operator-facing |
| Public Description | public_description, shown in checkout and the portal |
| Included Quantity | included_quantity, a string |
| Default Quantity | default_quantity, an integer |
| Min / Max quantity | min_quantity / max_quantity |
| Rollover | rollover_enabled, with rollover_max and rollover_expiry_periods |
| Version column | product_track_mode: "latest" or "pinned" |
| Not offered badge | the product is on the working draft but not in the published version |
| Publish | POST /v1/changes with catalog.publish_version |
| A scheduled version | an entry in scheduled_versions, cancelled with DELETE /v1/changes/{id} |
Where plans go next
- Subscribe a customer. Each subscription line names the
plan version it was drawn from in its
origin. - Check the blast radius before you republish.
publish-impactsplits active subscriptions into those that would move and those that hold a pinned line. - Schedule the release when the new version should go live on a date rather than now.
Next step
Continue to Customers, then Subscriptions, to put this plan to work.
Reference
| Topic | When you need it |
|---|---|
| Versioning | The draft, publish and history model all catalog entities share |
| Scheduling | Scheduled publishes and other changes: list, reschedule, cancel |
| Plan clauses | Contract text attached to a plan (/plans/{id}/clauses, in the Quotes reference) |
| Dunning | How failed payments are retried |
| Plans API | Every field, plus publish-impact, publish-preflight, copy and history |