Once a subscription is active, its priced item set lives inside a version. To change what the customer pays, you produce a new version. There are two ways to do this.Documentation Index
Fetch the complete documentation index at: https://alguna.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
Path A: Changes API
High-level and delta-based. Describe what you want to change (add, remove, adjust) and the API computes a new version. Best for most use cases.
Path B: Version lifecycle
Low-level and snapshot-based. Supply a complete item set and iterate on it as a draft before publishing. Best for full control or staged multi-step edits.
Metadata-only updates (name, subscription-level discount, spending thresholds, price escalation, billing flags) use
update-subscription (PATCH /subscriptions/{id}) and apply immediately without creating a new version. The two paths below are specifically for changing the priced item set.Preliminaries
Every version carries:status:draftorpublished. Only one published version is current at any moment.effective_at: when the version takes over from the previous one. Can be a specific date, or the result of a timing keyword likeimmediateorend_of_term.
get-current-subscription-version.
id is the source that any change will be computed against.
Path A: Changes API
create-subscription-change (POST /subscriptions/{id}/changes) is the primary endpoint for applying deltas. You supply add, remove, and update arrays plus an effective timing, and the API:
- Reads the source version (default: the current active one, override with
source_version_id). - Applies your delta on top.
- Creates a new version with the result.
- Publishes it, unless you pass
draft: true.
preview-subscription-change to compute the same result without writing a new version.
Supported effective values
| Value | Meaning |
|---|---|
immediate | Takes effect now. Default. |
end_of_term | At the end of the current contract term. |
billing_cycle_start | At the next billing cycle boundary. |
end_of_contract | At the contract end date. |
next_renewal | At the next renewal date. |
YYYY-MM-DD | A specific date, for example 2026-07-01. |
A.1 Adjust a single product’s scalar price field
The most common mutation. Useupdate with a partial adjust. Every field is optional and merged onto the existing price. Here we bump the platform fee from 600/mo effective end of term.
price_per_unit was specified, so only that field is replaced. Billing interval, billing direction, tier structure, and every other field are inherited from the prior version.
A.2 Replace a price entirely
When the new price is structurally different, for example switching fromunit to graduated_tiered, use new_price instead of adjust. See the pricing models reference for the full list of supported types.
A.3 Replace tiers on a tiered price
Tiered pricing adjustments behave differently from other fields. Thetiers array inside a tiered pricing adjustment is not merged onto the existing tiers. The array you send becomes the entire tier set.
The example below restructures the API usage product’s tiers without changing the billing interval, metric bindings, or any other field on the price.
A.4 Add a new product
A.5 Remove a product
A.6 Swap to a different plan
Provideplan_id on the change request. The API swaps the item set to the plan’s item set while preserving the original contract and billing config. You can combine this with update, add, and remove for per-customer overrides on top of the new plan.
A.7 Bundle operations
Bundles use the sameadd, remove, and update shapes as standalone items, but reference bundle_id instead of product_id. Children of a bundle must be addressed through their parent bundle.
Adjust one child price inside a bundle. Use update with bundle_id and nested items.
add_items on a bundle update.
remove_items.
remove.
A.8 Preview a change before writing it
Every change body is also accepted bypreview-subscription-change (POST /subscriptions/{id}/changes/preview), which returns the computed delta without writing a new version. Use this to drive approval flows or show customers what is about to change.
A.9 Draft a change for later
Passdraft: true to produce an unpublished draft version. Useful for staging changes that need review before going live.
id (a plain 8-char string, for example vavGpNlc). Publish it later via publish-subscription-version.
Path B: Raw version lifecycle
When Path A’s delta semantics are not enough (typically large multi-item changes, staged edits that need to be saved and reopened, or programmatic migrations), drop down to the version endpoints directly. The lifecycle is:create-subscription-version:POST /subscriptions/{id}/versions. Create a new version from a full item set. Returns a draft by default.replace-subscription-version:PUT /subscriptions/{id}/versions/{vid}. Replace a draft’s contents in place.publish-subscription-version:POST /subscriptions/{id}/versions/{vid}/publish. Publish the draft. It becomes active ateffective_at.delete-subscription-version:DELETE /subscriptions/{id}/versions/{vid}. Discard an unwanted draft.
B.1 Create a draft with standalone items
id (for example vavGpNlc). The draft is not yet applied. The current active version is still in effect.
B.2 Create a draft with bundles
Bundles go directly intoitems with bundle_id and a nested items array of priced children. Every child needs a full price definition. You can mix standalone and bundle items in the same items array.
B.3 Replace a draft’s contents
While a version is still a draft, edit it in place withreplace-subscription-version. This is a full replacement. Send the entire item set.
draft status returns 422.
B.4 Publish a draft
Once the draft is correct, publish it withpublish-subscription-version.
- The version’s
statusflips topublished. - If
effective_atis in the past or today, it becomes the current active version immediately. - If
effective_atis in the future, it sits as a pending change until then. You will see it in the parent subscription’spending_changesarray.
B.5 Delete an unwanted draft
draft versions can be deleted. Published versions are immutable historical records and must be superseded by a new version instead.
Which path should I use?
| If you want to… | Use |
|---|---|
| Bump one scalar field on one price | Path A with update + adjust |
| Replace the tier structure on a tiered price | Path A with update + adjust.<model>.tiers |
| Switch a price from one type to another | Path A with update + new_price |
| Add or remove a single item | Path A with add or remove |
| Swap to a new plan with a few overrides | Path A with plan_id + update |
| Adjust one child price inside a bundle | Path A with update + bundle_id + nested items[].adjust |
| Replace a subscription’s entire item set in one request | Path B |
| Stage a big multi-step edit over several API calls | Path B: create draft, edit with PUT, publish |
| Programmatically migrate a batch of subscriptions to a new shape | Path B (snapshot-based, deterministic) |
| Preview a change before writing it | Path A with /changes/preview |
Common pitfalls
- Editing a published version. Published versions are immutable.
PUTon a published version returns422. Create a new version via Path A or Path B instead. - Forgetting the complete item set in Path B.
POST /versionsandPUT /versions/{vid}are snapshot endpoints. Items you do not include are removed from the new version. - Trying to adjust a bundle child by
product_idalone. Children of a bundle must be addressed through their parent:update[].bundle_idplusupdate[].items[].product_id, not a bare top-levelupdate[].product_id. - Sending
adjustandnew_pricetogether on the same item. These are mutually exclusive. Useadjustfor partial merges andnew_pricefor full replacement. - Expecting tier merges. The
tiersarray on any tiered pricing adjustment replaces the entire tier set. Always send the full intended tier set. The same applies toexpression_pricing_model.charges.