> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paygentic.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Plan versions

> Learn how a plan keeps a version for each set of prices, and how each subscription follows or keeps a version

A plan version is one fixed set of prices for a plan. When you change the prices of a plan,
Paygentic does not change the version that exists. It makes a new version. The old version keeps
the values that it had, so you can always see what you sold.

Each subscription has a version policy. The policy decides if the subscription moves to a new
version of its plan or stays on the version that it holds.

## Prerequisites

* An API key. See [API keys](/api-keys).
* A plan on Standard Billing (`billingVersion: 1`). See
  [Billing Versions](/platform/billing/billing-versions).

## What a version is

A version has a `versionNumber`. The first version of a plan is `1`. Each new version has the next
number.

A version holds a list of price lines. Each line has a `key` and a price. The key is the name of
the line. It stays the same when you change the price of the line, so you can follow one line
through all the versions of the plan.

A version never changes. A later edit to a price does not change the values in an older version.
The plan itself, such as its name, is not part of a version. If you change only the name of a plan,
the API changes the name and makes no new version.

Paygentic creates a version when you:

* create a plan with prices. The plan starts with version `1`.
* change the prices of a plan with `PATCH /v0/plans/{id}`.
* edit a price that a plan uses with `PATCH /v0/prices/{id}`. See
  [Editing prices](/platform/pricing/editing-prices).
* create a version yourself with `POST /v0/plans/{id}/versions`.

## The default version

One version of the plan is the default version. The default version is the version that a new
subscription bills from. `defaultVersionId` in the plan response shows it.

When Paygentic creates a version, it becomes the default version immediately. You can also make an older
version the default again. See [Roll back a plan](#roll-back-a-plan).

## Read the versions

```bash theme={null}
curl https://api.paygentic.io/v0/plans/plan_a1b2c3d4e5f6g7h8/versions \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY"
```

The response lists the versions, newest first.

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "pver_h8i9j0k1l2m3n4o5",
      "object": "plan_version",
      "versionNumber": 2,
      "status": "published",
      "publishedAt": "2026-04-01T09:30:00.000Z",
      "isDefault": true,
      "subscriptionCount": 3
    },
    {
      "id": "pver_u2v3w4x5y6z7a8b9",
      "object": "plan_version",
      "versionNumber": 1,
      "status": "published",
      "publishedAt": "2026-03-01T08:00:00.000Z",
      "isDefault": false,
      "subscriptionCount": 12
    }
  ],
  "pagination": { "limit": 10, "offset": 0, "total": 2 }
}
```

`subscriptionCount` is the number of subscriptions that were on the version when they were
created. It does not show which version each subscription bills from now.

To read the price lines of one version, use its number:

```bash theme={null}
curl https://api.paygentic.io/v0/plans/plan_a1b2c3d4e5f6g7h8/versions/2 \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY"
```

The response has a `prices` list. Each item has the `key` of the line and the fields of the price.
Use the `key` to address the line in a [price interval](/platform/billing/price-intervals).

Only an account that can manage the plan can read its versions. A version can show pricing that
you have not yet released, so read-only access is not enough.

## Create a version

Most edits create a version for you. To create a version yourself, send the full list of prices that
the new version must hold. Make the prices first with `POST /v0/prices`.

```bash theme={null}
curl -X POST https://api.paygentic.io/v0/plans/plan_a1b2c3d4e5f6g7h8/versions \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "basedOnVersionId": "pver_h8i9j0k1l2m3n4o5",
    "prices": [
      { "key": "pk_m4n5o6p7q8r9s0t1", "priceId": "price_b3c4d5e6f7g8h9i0" },
      { "key": "pk_s9t0u1v2w3x4y5z6", "priceId": "price_k1l2m3n4o5p6q7r8" }
    ]
  }'
```

The request names the full set. It does not name a change. The API compares each `key` with the
current default version:

* A key with a different price ID replaces the line.
* A key that is only in the request adds a line.
* A key that the current version has and the request does not have removes the line.

An item with no `key` adds a line with a key that the API makes. You can send a price ID alone
instead of an object.

Send `basedOnVersionId` to protect against an old read. If the default version is not the version
that you name, the API refuses the request with status 409. A price list that you built from an old
read then cannot remove a line that another person added. If you do not send it, the API writes the
set without a check.

The response is the new version. It is published, and it is the default version.

A version must have one price or more. The API refuses an empty list.

## What a new default version does to a subscription

When a new version becomes the default version, the version policy of each subscription decides
what happens.

| Policy | What the subscription does |
| - | - |
| `floating` | The subscription bills from the new default version, from its next billing period. |
| `pinned` | The subscription stays on the version that it holds. |

A subscription that you create without a `versionPolicy` is `floating`. All the subscriptions that
existed before the field was available are also `floating`.

A change of the default version never changes a billing period that has started. A period that the
customer has been invoiced for keeps its price. The change applies from the next period.

### Choose a policy

Send `versionPolicy` when you create the subscription, or when you update it:

```bash theme={null}
curl -X PATCH https://api.paygentic.io/v0/subscriptions/sub_z9a0b1c2d3e4f5g6 \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "versionPolicy": "pinned" }'
```

The subscription response shows the `versionPolicy`. It also shows `planVersionId` and
`versionNumber`, which are the version that the subscription was created on.

Use `pinned` when a customer must keep the prices that you agreed. The subscription then keeps
the prices, the entitlements, and the grants of its version, also when you roll the plan back.
Use `floating` when you want each customer to get your latest prices.

To move a `pinned` subscription to the default version, change its policy to `floating`. The
subscription moves to the default version at its next billing period. It does not go back to
`pinned` by itself.

<Note>
  We are working on a way to move one subscription to a newer version of its plan. The move will
  keep the prices that you set for that subscription. Until it is available, use the version
  policy to control which version a subscription bills from.
</Note>

## Roll back a plan

You can make any published version the default version, to a newer version or to an older
version. To go back to an older price set, make the older version the default:

```bash theme={null}
curl -X PATCH https://api.paygentic.io/v0/plans/plan_a1b2c3d4e5f6g7h8/versions/1 \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "default": true }'
```

The rules of the version policy apply to a rollback in the same way. A `floating` subscription
goes back to version `1` from its next billing period. A `pinned` subscription does not change.
The older version can lack a feature that the pinned version has. The pinned subscription then keeps its entitlement and its grant for that feature.

If the version is already the default version, the request changes nothing.

## What a version change does not change

* **A billed period.** Paygentic never recalculates a period that it has invoiced. A new
  version changes only the periods that start after the change.
* **An invoice that exists.** An issued invoice is final. See [Invoices](/platform/billing/invoices).
* **A subscription that is `pinned`.** It changes only when you change its policy.

## What a version change drops

A version change does not carry over a price that you added to one subscription. If you added a
price with a new `priceKey` in a [price interval](/platform/billing/price-intervals), and the
interval has no `endDate`, the interval stops when the subscription moves to a new version. An
interval with an `endDate` keeps its dates.

This is a rule of the platform. The added price belongs to the subscription, and the new version
does not know about it. After the change, read the intervals of the subscription. If the
customer must keep the price, add it again.

## Next steps

* [Plans](/platform/pricing/plans) — make a plan and its prices
* [Editing prices](/platform/pricing/editing-prices) — how a price edit reaches existing subscribers
* [Price intervals](/platform/billing/price-intervals) — change a price for one subscription only
* [Subscriptions](/platform/billing/subscriptions) — the subscription lifecycle
