> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.solvimon.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.solvimon.com/_mcp/server.

# Price indexation

How to move every price on a subscription by a percentage in one call, for an annual uplift or a contractual inflation clause, instead of editing each price by hand.

---

## Why this matters

Most enterprise contracts carry an uplift clause: prices rise by a fixed percentage, or by an inflation index, at each renewal. Applying that by hand means editing every price on the schedule, and a plan with tiered pricing, per-currency configs and condition groups has far more prices than the commercial agreement suggests. Missing one produces an invoice the customer disputes.

Indexation applies the percentage to all of them at once and produces a new schedule with the result, so the uplift is a single reviewable action with a single start date.

## How it works

Indexation runs as part of copying a schedule. There is no separate endpoint: you pass indexing settings on [Copy a pricing plan schedule](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-schedules/post-pricing-plan-schedules-by-resource-id-copy), and the copy comes back with the adjusted prices. The source schedule is never modified, so the old prices stay intact for the periods they billed.

```mermaid
flowchart LR
    A[Current schedule<br />prices as agreed] -->|copy + indexing_settings| B[New schedule<br />prices moved by %]
    A --> C[Unchanged<br />keeps billing until the new start date]
```

Each price is recalculated as `original × (1 + percentage / 100)` and then rounded. A positive `percentage` raises prices, a negative one lowers them. Values that are zero or unset are left alone, so an intentionally free tier stays free.

> **Note**
>
> Indexation is available on `copy` only. `migrate` moves a schedule to a different pricing plan version and ignores indexing settings.

### What gets indexed

Indexation moves money, not thresholds. Within every pricing band it adjusts:

| Field                               | Indexed | What it is                              |
| ----------------------------------- | ------- | --------------------------------------- |
| `amount`                            | Yes     | The unit price of the band              |
| `fixed_amount`                      | Yes     | A flat charge on the band               |
| `percentage`                        | Yes     | A percentage-based price                |
| `credits`                           | Yes     | The credit quantity granted by the band |
| `minimum_amount`                    | Yes     | The floor on what the band charges      |
| `maximum_amount`                    | Yes     | The cap on what the band charges        |
| `tier_top_bound`                    | No      | Where one tier ends and the next begins |
| `block_size`                        | No      | The unit a block is charged in          |
| `minimum_number` / `maximum_number` | No      | Quantity bounds on the band             |

For a tiered price this means every tier's price moves and the tier boundaries stay where they are: a customer who used to pay a lower rate above 10,000 units still crosses at 10,000 units. Condition groups are indexed recursively, so a price nested under two conditions is adjusted along with the rest.

### Rounding

`rounding_mode` is required on every set of indexing settings, and rounding is always half-up.

| Rounding mode    | Result                                                                                                       |
| ---------------- | ------------------------------------------------------------------------------------------------------------ |
| `PRESERVE_SCALE` | Rounded to the same number of decimals the original price had, so a price of `0.0250` stays at four decimals |
| `DECIMALS`       | Rounded to `number_of_decimals`, which is required when you choose this mode                                 |

`PRESERVE_SCALE` is the safer default for usage prices, where the fourth or fifth decimal carries real money at volume. Use `DECIMALS` when the output has to match a rate card printed to two decimals.

## Index every price

Send `indexing_settings` and nothing else. Every price the schedule bills is indexed: the prices in its linked pricing plan version, and any pricing defined directly on the schedule.

**`Raise every price by 4%`**

```bash Raise every price by 4%
curl -X POST https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/copy \
  -H "X-API-KEY: <apiKey>" \
  -H "Content-Type: application/json" \
  -d '{
    "start_at": "2027-01-01T00:00:00Z",
    "indexing_settings": {
      "percentage": "4",
      "rounding_mode": "PRESERVE_SCALE"
    }
  }'
```

The response is the new schedule, with the indexed prices in its `combined_pricing_plan_version`.

Where the schedule already carried a [price override](/platform-guides/customers-and-billing/subscriptions/custom-pricing), the override is indexed. Where it did not, indexation creates one from the plan's list price, because the indexed price is by definition specific to this customer. A schedule that started out entirely on list prices therefore comes back fully overridden. That is the intended outcome: from this point on, the customer's prices no longer follow changes to the plan.

## Index a subset

Pass one or more target lists to index part of the schedule. Any price you leave out is copied across unchanged.

| Field                  | Targets                              | Desk calls it |
| ---------------------- | ------------------------------------ | ------------- |
| `pricings`             | A pricing, and everything under it   | Products      |
| `pricing_items`        | A single revenue item                | Revenue items |
| `pricing_item_configs` | A single pricing rule within an item | Pricing rules |

Each entry is an object with an `id` and an optional `indexing_settings` that overrides the top-level one for that target only. This is what a contract with different uplifts per product needs: a global 3% with 5% on one product is one request.

**`Index two products at different rates`**

```bash Index two products at different rates
curl -X POST https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/copy \
  -H "X-API-KEY: <apiKey>" \
  -H "Content-Type: application/json" \
  -d '{
    "start_at": "2027-01-01T00:00:00Z",
    "indexing_settings": {
      "percentage": "3",
      "rounding_mode": "DECIMALS",
      "number_of_decimals": 2
    },
    "pricings": [
      { "id": "pric_hwDeeN0vcYJaBLAc0C1W" },
      {
        "id": "pric_QwDeeN0vcYJaBLAc0C29",
        "indexing_settings": { "percentage": "5", "rounding_mode": "PRESERVE_SCALE" }
      }
    ]
  }'
```

A target must resolve to something the schedule actually bills, either through its pricing plan version or through its own pricings; an id that belongs to neither is rejected naming the index that carried it, for example `pricings.1.id`. A target list that is present but empty is rejected as well, because "index nothing" is expressed by leaving indexation out.

Every target needs settings from somewhere. If a target has no `indexing_settings` of its own and the request has no top-level `indexing_settings`, the request fails on `pricings.{index}.indexing_settings` rather than silently skipping that price.

Overlapping targets do not compound. Within one request the pricing rules are indexed first, then the revenue items, then the pricings, and a rule that has already been indexed is skipped when a broader target would reach it again. Naming a product and one of its revenue items in the same request moves that item's price once.

## Via Desk

On the panel that adds a schedule, turn on **Index pricings**, then choose the scope:

| Scope         | Sends                    |
| ------------- | ------------------------ |
| All pricings  | `indexing_settings` only |
| Products      | `pricings`               |
| Revenue items | `pricing_items`          |
| Pricing rules | `pricing_item_configs`   |

**All pricings** takes one percentage and one rounding mode for the whole schedule.

![New subscription schedule form with indexation settings for all products at once, with percentage and rounding mode options with a pricing preview panel on the right displaying the schedule configuration details](/_fern-img/c9bac68063c27aab141bf950bdfb5a62c2c823149975ade6f8b82027908b3979.webp)

Any other scope lists the entries individually, each with its own percentage and rounding mode. Leave a percentage blank to leave that entry out: only the entries you fill in are indexed, so raising one product on a schedule with twenty does not mean typing the same number twenty times. Leaving every entry blank is treated as indexation being switched off.

![New subscription schedule form with indexation settings for multiple products, showing separate percentage and rounding mode options for API Platform and Online payment products, with a pricing preview panel on the right displaying the schedule configuration details](/_fern-img/6045a768ac5cfad86f4302edbee41f94ab1edcc0a8129c50556bda3a494fb75a.webp)

The preview panel on the right shows the schedule that will be created. Confirming creates it.

## Edge cases

* **Rate cards cannot be indexed.** A schedule based on a [rate card](/platform-guides/products-and-pricing/rate-cards) is rejected with an `indexing_settings` error. Rate cards do not support price overrides, and an indexed price is an override. Move the customer to a standard pricing plan if they need a negotiated uplift.
* **A schedule with nothing to index is rejected.** A schedule with neither a pricing plan version nor its own pricings fails on `indexing_settings` rather than producing an empty copy.
* **Only the matching currency and billing period are indexed.** Where a pricing item carries configs for several currencies or billing periods, indexation touches the variant matching the schedule's own `pricing_currency` and `billing_period`. The others are copied unchanged.
* **`percentage` is a string and must parse as a number.** It is validated up front, so `"4"`, `"4.5"` and `"-2"` all work and a malformed value fails the request instead of leaving prices untouched.
* **The copy inherits the source schedule's status.** Copying a draft produces another draft. Set `status` on the request to override that, and see [Draft schedules](/platform-guides/customers-and-billing/subscriptions/draft-schedules) if you want the uplift reviewed before it bills.