Skip to main content

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.

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.

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: draft or published. 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 like immediate or end_of_term.
Before mutating anything, read the current state with get-current-subscription-version.
curl https://api.alguna.io/subscriptions/CQCncgVu/versions/current \
  -H "Authorization: Bearer sk_live_..." \
  -H "Alguna-Version: 2026-04-01"
The returned 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:
  1. Reads the source version (default: the current active one, override with source_version_id).
  2. Applies your delta on top.
  3. Creates a new version with the result.
  4. Publishes it, unless you pass draft: true.
Use preview-subscription-change to compute the same result without writing a new version.

Supported effective values

ValueMeaning
immediateTakes effect now. Default.
end_of_termAt the end of the current contract term.
billing_cycle_startAt the next billing cycle boundary.
end_of_contractAt the contract end date.
next_renewalAt the next renewal date.
YYYY-MM-DDA specific date, for example 2026-07-01.

A.1 Adjust a single product’s scalar price field

The most common mutation. Use update with a partial adjust. Every field is optional and merged onto the existing price. Here we bump the platform fee from 500to500 to 600/mo effective end of term.
curl -X POST https://api.alguna.io/subscriptions/CQCncgVu/changes \
  -H "Authorization: Bearer sk_live_..." \
  -H "Alguna-Version: 2026-04-01" \
  -H "Content-Type: application/json" \
  -d '{
    "update": [
      {
        "product_id": "prod_032wMej82trlC5RulBsDJY",
        "adjust": {
          "fixed_pricing_model": {
            "price_per_unit": "600.00"
          }
        }
      }
    ],
    "effective": "end_of_term",
    "description": "Q3 price adjustment"
  }'
Only 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 from unit to graduated_tiered, use new_price instead of adjust. See the pricing models reference for the full list of supported types.
{
  "update": [
    {
      "product_id": "prod_04ab8Nej82trlC5RulBsDJY",
      "new_price": {
        "type": "graduated_tiered",
        "billing_interval": "monthly",
        "fee_type": "metered",
        "billing_direction": "arrears",
        "metric_ids": ["mtr_032wMej82trlC5RulBsDJY"],
        "graduated_tiered_pricing_model": {
          "tiers": [
            { "min_units": 0, "max_units": 10000, "price_per_unit": "0.00", "fixed_fee": null },
            { "min_units": 10001, "max_units": null, "price_per_unit": "0.03", "fixed_fee": null }
          ]
        }
      }
    }
  ],
  "effective": "billing_cycle_start"
}

A.3 Replace tiers on a tiered price

Tiered pricing adjustments behave differently from other fields. The tiers array inside a tiered pricing adjustment is not merged onto the existing tiers. The array you send becomes the entire tier set.
When you send a tiers array inside any tiered pricing adjustment (graduated_tiered_pricing_model, tiered_pricing_model, tiered_percentage_pricing_model, graduated_percentage_pricing_model, prepaid_tiered_pricing_model, or prepaid_fixed_tiered_pricing_model), the existing tier set is replaced wholesale. There is no way to add, remove, or modify a single tier in isolation. Always send the full intended tier set.The same replace-wholesale behavior applies to the charges array on expression_pricing_model adjustments.
The example below restructures the API usage product’s tiers without changing the billing interval, metric bindings, or any other field on the price.
{
  "update": [
    {
      "product_id": "prod_04ab8Nej82trlC5RulBsDJY",
      "adjust": {
        "graduated_tiered_pricing_model": {
          "tiers": [
            { "min_units": 0, "max_units": 50000, "price_per_unit": "0.00", "fixed_fee": null },
            { "min_units": 50001, "max_units": 500000, "price_per_unit": "0.02", "fixed_fee": null },
            { "min_units": 500001, "max_units": null, "price_per_unit": "0.015", "fixed_fee": null }
          ]
        }
      }
    }
  ],
  "effective": "billing_cycle_start",
  "description": "New volume tiers for API usage"
}
If you want to keep the existing zero-priced free tier, include it in the array. If you omit it, it is gone.

A.4 Add a new product

{
  "add": [
    {
      "product_id": "prod_07fx2Nej82trlC5RulBsDJY",
      "new_price": {
        "type": "fixed",
        "billing_interval": "monthly",
        "fee_type": "fixed",
        "billing_direction": "arrears",
        "billing_frequency": "recurring",
        "fixed_pricing_model": { "price_per_unit": "2500.00", "units": 1 }
      }
    }
  ],
  "effective": "immediate",
  "description": "Upsell: add dedicated support"
}

A.5 Remove a product

{
  "remove": [
    { "product_id": "prod_0legacyNej82trlC5RulBsDJY" }
  ],
  "effective": "end_of_term"
}

A.6 Swap to a different plan

Provide plan_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.
{
  "plan_id": "pln_052xMej82trlC5RulBsDJY",
  "update": [
    {
      "product_id": "prod_032wMej82trlC5RulBsDJY",
      "adjust": {
        "fixed_pricing_model": { "price_per_unit": "800.00" }
      }
    }
  ],
  "effective": "next_renewal",
  "description": "Renewal to 2027 enterprise plan with negotiated platform fee"
}

A.7 Bundle operations

Bundles use the same add, 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.
{
  "update": [
    {
      "bundle_id": "bnd_032wMej82trlC5RulBsDJY",
      "items": [
        {
          "product_id": "prod_09tx7Nej82trlC5RulBsDJY",
          "adjust": {
            "unit_pricing_model": { "price_per_unit": "18.00" }
          }
        }
      ]
    }
  ],
  "effective": "end_of_term"
}
Only that one child is touched. The other children and all non-bundle items pass through unchanged. Add a new child to an existing bundle. Use add_items on a bundle update.
{
  "update": [
    {
      "bundle_id": "bnd_032wMej82trlC5RulBsDJY",
      "add_items": [
        {
          "product_id": "prod_0analytNej82trlC5RulBsDJY",
          "new_price": {
            "type": "fixed",
            "billing_interval": "monthly",
            "fee_type": "fixed",
            "billing_direction": "arrears",
            "billing_frequency": "recurring",
            "fixed_pricing_model": { "price_per_unit": "100.00", "units": 1 }
          }
        }
      ]
    }
  ],
  "effective": "immediate"
}
Remove a child from a bundle. Use remove_items.
{
  "update": [
    {
      "bundle_id": "bnd_032wMej82trlC5RulBsDJY",
      "remove_items": [
        { "product_id": "prod_0legacyNej82trlC5RulBsDJY" }
      ]
    }
  ],
  "effective": "end_of_term"
}
Remove an entire bundle. Use top-level remove.
{
  "remove": [
    { "bundle_id": "bnd_032wMej82trlC5RulBsDJY" }
  ],
  "effective": "end_of_term"
}

A.8 Preview a change before writing it

Every change body is also accepted by preview-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.
curl -X POST https://api.alguna.io/subscriptions/CQCncgVu/changes/preview \
  -H "Authorization: Bearer sk_live_..." \
  -H "Alguna-Version: 2026-04-01" \
  -H "Content-Type: application/json" \
  -d '{ "update": [ ... ], "effective": "end_of_term" }'

A.9 Draft a change for later

Pass draft: true to produce an unpublished draft version. Useful for staging changes that need review before going live.
{
  "update": [ ],
  "effective": "2027-01-01",
  "draft": true,
  "description": "Proposed 2027 pricing"
}
The response includes the new version’s id (a plain 8-char string, for example vavGpNlc). Publish it later via publish-subscription-version.
curl -X POST https://api.alguna.io/subscriptions/CQCncgVu/versions/vavGpNlc/publish \
  -H "Authorization: Bearer sk_live_..." \
  -H "Alguna-Version: 2026-04-01"
To iterate on a draft before publishing, see Path B.

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:
  1. create-subscription-version: POST /subscriptions/{id}/versions. Create a new version from a full item set. Returns a draft by default.
  2. replace-subscription-version: PUT /subscriptions/{id}/versions/{vid}. Replace a draft’s contents in place.
  3. publish-subscription-version: POST /subscriptions/{id}/versions/{vid}/publish. Publish the draft. It becomes active at effective_at.
  4. delete-subscription-version: DELETE /subscriptions/{id}/versions/{vid}. Discard an unwanted draft.
The version endpoints are snapshot-based. POST /versions and PUT /versions/{vid} require the complete item set every time. There are no deltas. Items you omit are removed from the new version.

B.1 Create a draft with standalone items

curl -X POST https://api.alguna.io/subscriptions/CQCncgVu/versions \
  -H "Authorization: Bearer sk_live_..." \
  -H "Alguna-Version: 2026-04-01" \
  -H "Content-Type: application/json" \
  -d '{
    "effective_at": "2027-01-01",
    "draft": true,
    "description": "2027 enterprise configuration",
    "items": [
      {
        "product_id": "prod_032wMej82trlC5RulBsDJY",
        "price": {
          "type": "fixed",
          "billing_interval": "monthly",
          "fee_type": "fixed",
          "billing_direction": "arrears",
          "billing_frequency": "recurring",
          "fixed_pricing_model": { "price_per_unit": "800.00", "units": 1 }
        }
      },
      {
        "product_id": "prod_04ab8Nej82trlC5RulBsDJY",
        "price": {
          "type": "graduated_tiered",
          "billing_interval": "monthly",
          "fee_type": "metered",
          "billing_direction": "arrears",
          "metric_ids": ["mtr_032wMej82trlC5RulBsDJY"],
          "graduated_tiered_pricing_model": {
            "tiers": [
              { "min_units": 0, "max_units": 100000, "price_per_unit": "0.00", "fixed_fee": null },
              { "min_units": 100001, "max_units": null, "price_per_unit": "0.02", "fixed_fee": null }
            ]
          }
        }
      }
    ]
  }'
The response includes the draft’s 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 into items 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.
{
  "effective_at": "2027-01-01",
  "draft": true,
  "items": [
    {
      "bundle_id": "bnd_032wMej82trlC5RulBsDJY",
      "items": [
        {
          "product_id": "prod_032wMej82trlC5RulBsDJY",
          "price": {
            "type": "fixed",
            "billing_interval": "monthly",
            "fee_type": "fixed",
            "billing_direction": "arrears",
            "billing_frequency": "recurring",
            "fixed_pricing_model": { "price_per_unit": "250.00", "units": 1 }
          }
        },
        {
          "product_id": "prod_09tx7Nej82trlC5RulBsDJY",
          "price": {
            "type": "unit",
            "billing_interval": "monthly",
            "fee_type": "fixed",
            "billing_direction": "arrears",
            "billing_frequency": "recurring",
            "unit_pricing_model": { "price_per_unit": "18.00" }
          }
        }
      ]
    }
  ]
}

B.3 Replace a draft’s contents

While a version is still a draft, edit it in place with replace-subscription-version. This is a full replacement. Send the entire item set.
curl -X PUT https://api.alguna.io/subscriptions/CQCncgVu/versions/vavGpNlc \
  -H "Authorization: Bearer sk_live_..." \
  -H "Alguna-Version: 2026-04-01" \
  -H "Content-Type: application/json" \
  -d '{
    "effective_at": "2027-02-01",
    "description": "Pushed back to February",
    "items": [ ]
  }'
Replacing a version that is not in draft status returns 422.

B.4 Publish a draft

Once the draft is correct, publish it with publish-subscription-version.
curl -X POST https://api.alguna.io/subscriptions/CQCncgVu/versions/vavGpNlc/publish \
  -H "Authorization: Bearer sk_live_..." \
  -H "Alguna-Version: 2026-04-01"
On publish:
  • The version’s status flips to published.
  • If effective_at is in the past or today, it becomes the current active version immediately.
  • If effective_at is in the future, it sits as a pending change until then. You will see it in the parent subscription’s pending_changes array.

B.5 Delete an unwanted draft

curl -X DELETE https://api.alguna.io/subscriptions/CQCncgVu/versions/vavGpNlc \
  -H "Authorization: Bearer sk_live_..." \
  -H "Alguna-Version: 2026-04-01"
Only 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 pricePath A with update + adjust
Replace the tier structure on a tiered pricePath A with update + adjust.<model>.tiers
Switch a price from one type to anotherPath A with update + new_price
Add or remove a single itemPath A with add or remove
Swap to a new plan with a few overridesPath A with plan_id + update
Adjust one child price inside a bundlePath A with update + bundle_id + nested items[].adjust
Replace a subscription’s entire item set in one requestPath B
Stage a big multi-step edit over several API callsPath B: create draft, edit with PUT, publish
Programmatically migrate a batch of subscriptions to a new shapePath B (snapshot-based, deterministic)
Preview a change before writing itPath A with /changes/preview
Both paths produce the same underlying data shape (a new version), so you can mix them freely across a subscription’s lifetime.

Common pitfalls

  • Editing a published version. Published versions are immutable. PUT on a published version returns 422. Create a new version via Path A or Path B instead.
  • Forgetting the complete item set in Path B. POST /versions and PUT /versions/{vid} are snapshot endpoints. Items you do not include are removed from the new version.
  • Trying to adjust a bundle child by product_id alone. Children of a bundle must be addressed through their parent: update[].bundle_id plus update[].items[].product_id, not a bare top-level update[].product_id.
  • Sending adjust and new_price together on the same item. These are mutually exclusive. Use adjust for partial merges and new_price for full replacement.
  • Expecting tier merges. The tiers array on any tiered pricing adjustment replaces the entire tier set. Always send the full intended tier set. The same applies to expression_pricing_model.charges.
For the create flow, see Creating a Subscription.