Plan changes
Reprice or reshape a plan by publishing a new commercial release; existing subscriptions keep their pins.
Change a plan by editing its fs.plan() declaration — or the pricing catalog it
binds — and pushing the managed repository. Do not mutate live plan, price, or
catalog rows through an API. Bespoke per-customer terms are
economic agreements, not repo edits.
Safe workflow
farthershore build
farthershore commercial-release diff <from-release.json> <to-release.json>
git push
farthershore apply-timeline inspect <business> <commit-sha> \
--env production \
--format json
Inspect the apply check and publish only after reviewing the current-head
result. For an active repository-managed business, create a GitHub Release for
that exact commit. farthershore business publish is only the first-activation
command for a business that is still DRAFT.
What the diff answers
farthershore commercial-release diff compares two compiled release manifests
and reports:
- compiled plans added, removed, or changed — a compiled plan changes when its route grants, structural limits or recurring fee change, not when you edit a catalog rate;
- rating-context versions added or removed — a catalog or modifier change mints a new rating context and leaves the compiled plan alone, so a price-only edit shows up here and only here;
- every changed manifest path and any compatibility-fence change;
unchanged: truewhen both manifests hash identically — republishing identical content reuses the same release id.
Who a change reaches
| Change | New subscriptions | Existing subscriptions |
|---|---|---|
Recurring price up or down | new price | keep their recurring-price pin |
Catalog rate change under a pricing.current() plan | new rate | new rate from the activation of the release, forward only — never rerated retroactively |
| Catalog rate change under a fixed-version agreement | n/a | untouched; the agreement is require_amendment by definition |
| New meter or measure | rated per catalog | agreements apply their NewMeterPolicy / NewMeasurePolicy; plain subscriptions rate it |
Funding bucket amount or onExhaustion | new policy | next issuance follows the new plan version they are moved to; current buckets are unchanged |
| Route grants and structural limits | applies | applies with the release they are served under |
The generalized "move every subscriber to the latest plan" operation is
deferred to post-launch: it is strictly a pricing rebind (repoint the recurring
pin, refresh non-current bindings, open a new rating segment), and it is not
part of prepare, publish, or activation.
Subscriber-initiated plan changes
Everything above is a BUILDER change — a new release of the catalog. A subscriber changes their own plan from the portal, and that path has its own timing rule:
| Move | Endpoint | When it takes effect |
|---|---|---|
| Free floor → a chargeable plan | POST /me/subscription/checkout-session | immediately, once checkout completes |
| Cheaper plan → dearer plan (upgrade) | POST /me/change-plan | immediately |
| Dearer plan → cheaper plan (downgrade) | POST /me/change-plan | at the end of the current period |
| Any paid plan → back to the free floor | POST /me/change-plan | at the end of the current period |
The rule is a single comparison of the recurring fee: a strictly dearer target is applied immediately, and everything else is scheduled for the period boundary. A downgrading subscriber therefore keeps the plan they paid for until it expires — there is no mid-period repricing and no refund to compute.
This means cancelling is not the only way down. The portal's plan list offers every plan except the one the subscriber is already on, including the free floor, so "I want to spend less" does not have to become "I want to leave". Cancelling ends the subscription; downgrading to the free floor keeps the account, its keys, and its history.
Moving a live subscription onto the free floor can only be scheduled, never
forced through immediately: asking for an immediate move is refused with
409 PAID_TO_PROVIDERLESS_REQUIRES_PERIOD_END.
Activation is per business
Publication writes an immutable release; activation appends an entry to the business's release log and promotes a per-business pointer. Business A's publish or rollback never touches business B. Requests already admitted under the previous release complete under it. See Commercial releases.
Recovery
Publishing is idempotent. Inspect workflow and Apply Timeline state before issuing another mutation.
A rollback appends a new release-log entry that points at an older immutable release; it never rewrites a release, never re-rates usage that was admitted under the rolled-back release, and does not erase invoices or ledger history.