Recipes

Change a price without touching code

Raise the price of a plan, or change what it includes, for new subscribers only. Publish a new plan version; existing subscribers keep theirs and your code stays as it is.

This recipe raises Pro from $20 to $25 a month and its export allowance from 5,000 to 10,000. When you are done:

  • new subscribers get the new price and allowance;
  • the customers already on Pro keep paying $20 for 5,000 exports, until they change plan themselves;
  • your backend and frontend have not changed, and nothing was deployed.

This works because your code never names a plan or a price. Your backend rules on capability keys, and the frontend components read the catalog from your application's region at runtime.

How it works

A plan's price, currency, billing interval and entitlement rules live on its numbered versions, and a published version never changes. A subscription points at the version it was taken on. Changing the price is therefore publishing a new version: the catalog offers the latest published version of each plan, and every existing subscription stays where it is. Plan versions covers the model in full.

1. Create the next version

In the operator console, under Products & Plans, open Pro and choose Create Next Draft. Through the Management API:

http
POST /mgt/v1/projects/{projectId}/plans/{planId}/versions
Authorization: Bearer <operator session token>

The draft is numbered after the latest version and copies its price, currency, billing interval, overage discount and rules. Here it is version 2, priced $20 with an allowance of 5,000, until you change it.

2. Change the price and the allowance

The price
PATCH /mgt/v1/projects/{projectId}/plans/{planId}/versions/2
Authorization: Bearer <operator session token>
Content-Type: application/json

{ "price_amount": 25 }
The rules
PUT /mgt/v1/projects/{projectId}/plans/{planId}/versions/2/rules
Authorization: Bearer <operator session token>
Content-Type: application/json

{ "rules": [{ "capability_id": "<report_exports>", "allowance": 10000, "reset_interval": "MONTHLY" }] }

PUT …/rules replaces all the rules of the draft, so list every capability the version grants, not only the one you change.

In the console, choose Save Draft after changing the rules and before Publish Version; a rule added and published without saving is left out of the published version.

3. Publish

http
POST /mgt/v1/projects/{projectId}/plans/{planId}/versions/2/publish
Authorization: Bearer <operator session token>

From this moment the version is frozen. Changing it is refused with VERSION_IMMUTABLE, as is changing version 1; publishing it again is refused with VERSION_ALREADY_PUBLISHED.

What happens on Stripe depends on the application's payment connection:

Payment connectionWhen you publish version 2
ConnectedShipRev creates a new product and a new recurring price of $25 on your Stripe account first, then freezes the version. If Stripe refuses, the version stays a draft; fix the cause and publish again
Not connected yetThe version is frozen at once and waits to be synced; its external_price_ref is null. It is synced when you connect an account. Until then checkout on it is refused with PAYMENT_PROVIDER_NOT_CONFIGURED

The price created for version 1 stays on Stripe as it was, and existing Stripe subscriptions keep renewing on it.

What everyone sees afterwards

WhoGets
A signed-in end user without a subscriptionGET /public/v1/catalog/plans lists Pro with latest_version 2: $25 and 10,000 exports. PricingTable shows the same, and checkout uses version 2
An organization already on ProIts subscription still points at version 1. Rulings keep limit at 5,000 and renewals stay at $20. Its entitlements are not rebuilt when you publish
Your backendNothing to change: it still rules on report_exports. New subscribers on version 2 are ruled against 10,000

Moving existing subscribers

An existing subscriber moves to version 2 only by changing plan themselves. PricingTable lists Pro with its latest version, so a subscriber on version 1 sees Pro as a plan they can switch to, and the switch is charged like any plan change; see Change plan. Moving existing subscribers to a new version in bulk is not available.

A subscription that was activated by hand, rather than through Stripe checkout, cannot be changed by the end user: the change is refused with PLAN_CHANGE_NOT_ALLOWED. You move it with POST /mgt/v1/projects/{projectId}/subscriptions/{subscriptionId}/change-plan and the new plan_version_id.

Try it

  1. Activate a test organization A on Pro version 1, through checkout in Stripe's test mode or by hand with POST /mgt/v1/projects/{projectId}/subscriptions/activate and { "organization_id": "<A>", "plan_version_id": "<version 1>" }.
  2. Run steps 1 to 3.
  3. Rule for A in your backend: limit is still 5,000. Read GET /public/v1/subscriptions as A: plan_version is still 1.
  4. Activate a second test organization B on the latest version. Its rulings show limit 10,000.

Where to go from here

© 2026 ShipRev