Managing subscriptions
Add items, change quantities, and update metadata.
Update a running subscription — change quantities, add or remove items, swap prices, and edit metadata — with predictable proration. This page covers the subscriptions.update API and how each change lands on the next invoice.
What you can changeAsk
A subscription is a container of subscription items, each pairing a price with a quantity. Most edits are a mutation of that item list plus subscription-level settings.
| Change | How | Billing impact |
|---|---|---|
| Quantity (seats, units) | Update an item's quantity | Prorated |
| Swap plan (price) | Update an item's price | Prorated |
| Add a line (add-on) | Append a new item | Prorated |
| Remove a line | Delete an item | Prorated credit |
| Metadata, fields | Update the subscription | None |
Quantity, price, and item changes generate proration line items — a credit for unused time on the old rate and a charge for the new one. See Trials & proration for the math.
Identifying itemsAsk
Every subscription item has its own si_-prefixed id. Fetch the subscription first so you know which item to mutate.
import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });
const sub = await vinr.subscriptions.retrieve('sub_8Q2k...');
sub.items.forEach((item) => {
console.log(item.id, item.price, item.quantity); // "si_..." "price_..." 3
});Changing quantityAsk
The most common edit: a customer adds seats. Update the item by id.
const updated = await vinr.subscriptions.update('sub_8Q2k...', {
items: [{ id: 'si_47xR...', quantity: 5 }],
prorationBehavior: 'create_prorations', // default
});prorationBehavior controls how mid-cycle changes are billed:
Prop
Type
Swapping a price (upgrade or downgrade)Ask
To move a customer between plans, replace the item's price. Keep the same item id so VINR treats it as a swap rather than an add + remove.
await vinr.subscriptions.update('sub_8Q2k...', {
items: [{ id: 'si_47xR...', price: 'price_proYearly' }],
});Changing recurring.interval (e.g. monthly to yearly) resets the billing cycle anchor to now and invoices the remaining balance immediately. To preserve the original anchor, set billingCycleAnchor: 'unchanged'.
Adding and removing itemsAsk
Append an add-on by passing an item with no id; remove one by passing deleted: true.
await vinr.subscriptions.update('sub_8Q2k...', {
items: [
{ price: 'price_supportAddon', quantity: 1 }, // add
{ id: 'si_oldAddon', deleted: true }, // remove
],
});A subscription with no remaining active items is invalid — to end billing entirely, cancel the subscription instead of deleting its last item.
Updating metadataAsk
Metadata changes never affect billing and never prorate. Use them to track internal references.
await vinr.subscriptions.update('sub_8Q2k...', {
metadata: { account_owner: 'team-emea', crm_id: 'AC-4192' },
});Resulting eventsAsk
Each update emits events you can react to from a webhook endpoint:
| Event | When |
|---|---|
subscription.updated | Any item, quantity, price, or metadata change. |
invoice.created | A proration draft invoice is generated (with always_invoice). |
invoice.paid | The proration invoice is collected. |
const event = vinr.webhooks.verify(payload, signatureHeader); // x-vinr-signature
if (event.type === 'subscription.updated') {
const sub = event.data.object;
syncEntitlements(sub.customer, sub.items); // grant/revoke seats
}Always reconcile entitlements from subscription.updated, not from your API response — the webhook is the source of truth and fires even when changes originate from the dashboard or dunning.
Edge casesAsk
Next stepsAsk
Trials & proration
How mid-cycle changes are calculated.
Canceling subscriptions
End billing and handle final invoices.
Subscriptions
The full subscription lifecycle.
Last updated on