Upgrade or Downgrade a Subscription

Upgrading or downgrading entails moving a customer from one Plan to another, such as from a Basic Plan to a Pro Plan. This guide covers how to make the swap on their existing Subscription and keep their entitlements in sync, so their access matches the new Plan immediately.

Replace the Plan

Use the replace action to swap one Plan for another.

import { Salable } from '@salable/sdk';
const salable = new Salable(process.env.SALABLE_SECRET_KEY);
 
await salable.api.subscriptions.byId('sub_01HXACME').items.put({
    items: [
        {
            action: 'replace',
            planId: 'plan_01HXBASIC',
            newPlanId: 'plan_01HXPRO'
        }
    ],
    proration: 'create_prorations'
});
ParameterDescription
items[].actionreplace to swap one Plan for another. The same endpoint also accepts add and remove.
items[].planIdThe ID of the Plan currently on the Subscription.
items[].newPlanIdThe ID of the Plan to move the customer to. Only applicable when replacing a plan.
items[].metadataOptional. Map of the new Plan's Line Item slugs to { quantity: N }. See below for the default.
prorationHow the mid-cycle difference is billed: create_prorations, always_invoice, or none. Only applicable to Stripe subscriptions.

On a Stripe Subscription the new Plan must have an active Line Item priced in the Subscription's currency and billing interval, or the call returns a 400. An archived Plan can't be added to any Subscription.

Note If the Plans share a Tier Tag, you can only use the replace action, because a Subscription can hold just one Plan per Tier Tag.

Set quantities for the new Plan

Setting a quantity for the new Plan is optional; if you leave metadata out, each Line Item falls back to its default quantity. Include it only when you want to set quantities explicitly, such as giving a customer more seats than the new Plan's minimum.

import { Salable } from '@salable/sdk';
const salable = new Salable(process.env.SALABLE_SECRET_KEY);
 
await salable.api.subscriptions.byId('sub_01HXACME').items.put({
    items: [
        {
            action: 'replace',
            planId: 'plan_01HXBASIC',
            newPlanId: 'plan_01HXPRO',
            metadata: {
                additionalData: {
                    pro_base: { quantity: 1 },
                    pro_seats: { quantity: 25 }
                }
            }
        }
    ],
    proration: 'create_prorations'
});

When you replace a Plan, the new Plan's Line Item quantities don't carry over from the old one. Each Line Item falls back to a default, and the default depends on the Line Item type:

  • Flat-rate Line Items fall back to the Line Item's minimum quantity, so a customer previously on a higher quantity resets to the new minimum unless you pass metadata.
  • Per-seat Line Items fall back to the greater of the Line Item's minimum quantity or the current Group size, so a customer paying for 25 seats but with only five members drops to five (or the minimum, if higher), losing the extra paid seats.

Every quantity must stay within its Line Item's minimum and maximum, unless the Line Item is unlimited. A Group size cannot be larger than the new Plan's per-seat maximum, if the Group size exceeds this number, Grantees must be removed first. To carry over quantities from the previous plan metadata must be set with the quantities you want to keep.

Move between metered Plans

When both the old and new Plan carry metered Line Items, Salable sets the old Line Items' usage records statuses to recorded, then creates a fresh current usage record for the new Plan's metered Line Items starting at zero.

On a Stripe Subscription, removing the old metered items settles that recorded usage on the customer's invoice under your chosen proration behaviour. A Salable Only Subscription has no invoice, so the old usage records are finalised, a new one is created, and a subscription.updated webhook event is emitted.

Choose a proration behaviour

On a Stripe Subscription, the Plan swap and the customer's entitlement changes take effect immediately whichever value you pick; the proration setting only controls how and when the mid-cycle difference is billed.

  • create_prorations works out the prorated difference and bills it on the customer's next invoice date.
  • always_invoice works out the prorated difference and invoices it, charging or crediting the customer immediately.
  • none applies no proration, so the change still takes effect immediately but the customer isn't charged or credited for the rest of the cycle, and the new Plan's price applies from the next renewal.

Warning Because the swap is always immediate, don't reach for none expecting a downgraded customer to keep premium access until renewal; it only suppresses the mid-cycle credit, and their entitlements drop the moment the change lands.

  • Subscriptions & Billing The full changeset model, the proration behaviours in depth, and how add, remove, and replace combine in a single request.