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

# Add Addons to a Subscription

> Attach addons at subscription creation, then add and remove them in batches through the subscription modification API, with cadence, quantity, and proration control

You can attach addons when the subscription is created. After that, attach and remove them through the subscription modification API with `type: "addon"` and `addon_bulk_params`. One request can carry any mix of attaches and removals. They apply as a single transaction and settle as one netted charge or credit, which you can preview before applying. The dashboard sends the same request.

## Before you start

An attachment is validated before anything is written:

* The addon must be `published`.
* The subscription must be `active` or `draft`. Trialing subscriptions cannot take addons.
* The addon needs at least one charge compatible with the subscription: same currency, and a billing period that equals or divides evenly into the subscription's (daily and weekly charges must match it exactly). An addon with no compatible charge, including an entitlement-only addon, is rejected. See [compatibility rules](/docs/product-catalogue/addons/overview#compatibility-rules).
* One-time charges qualify whatever their billing period, and each bills on the invoice whose period contains its start date. On an existing subscription, a one-time charge attached with `none` to start mid-period is never invoiced, because that period's advance invoice has already gone out; attach it with `change_at: "end_of_period"` to bill it on the renewal invoice. With `create_prorations`, attaching or removing an addon that carries a one-time charge fails with `line item billing_period must equal or strictly divide the subscription billing_period`.
* Metered features must not conflict: if the plan, an addon already on the subscription, or another addon in the same request entitles the same feature, their `usage_reset_period` values have to agree, or the request fails with `Feature <feature_id> is already measured over <period>, but this addon measures it over <period>`. The exception is an addon removed in the same request: when it ends on or before the new addon starts, their reset periods may differ, so you can swap them in one request.
* A future-dated change cannot cut into a running entitlement grant window. That covers attaching an addon that grants a metered feature with a grant window already running on the subscription, and removing an addon whose grant window is running. Apply such changes immediately or at the period end; a date in between fails with `Apply this change immediately, or schedule it for the period end`.

When a request changes several addons, every entry is checked, and one failing entry rejects the whole request.

## At subscription creation

Pass addons in the `addons` array. Each entry takes the same fields as a post-creation attach.

```bash theme={null}
curl -X POST https://us.api.flexprice.io/v1/subscriptions \
  -H "x-api-key: <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "<customer_id>",
    "plan_id": "<plan_id>",
    "currency": "usd",
    "billing_period": "MONTHLY",
    "start_date": "2026-10-01T00:00:00Z",
    "addons": [
      { "addon_id": "<addon_id>" },
      {
        "addon_id": "<addon_id_2>",
        "cadence": "onetime",
        "override_line_items": [
          { "price_id": "<price_id>", "quantity": 3 }
        ]
      }
    ]
  }'
```

* `start_date` defaults to the subscription start date.
* No separate proration settlement is created at creation. The addon's in-advance charges land on the subscription's first invoice, and its in-arrear charges on the period-end invoice.
* An addon `start_date` after the subscription start is charged only for the time it covers. A short first period from a calendar billing cycle is prorated only when the subscription is created with `proration_behavior: "create_prorations"`.
* The new subscription must start `active` or `draft`. If it starts `trialing`, or `incomplete` because of its `payment_behavior`, the request fails with `Addon can only be added to active or draft subscriptions`.
* A single request accepts at most 20 addon entries.

## Attach addons to an existing subscription

Call the [modification API](/api-reference/subscriptions/execute-subscription-modification) with `type: "addon"` and one entry per addon in `addon_bulk_params.adds`:

```bash theme={null}
curl -X POST https://us.api.flexprice.io/v1/subscriptions/<subscription_id>/modify/execute \
  -H "x-api-key: <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "addon",
    "addon_bulk_params": {
      "adds": [
        {
          "addon_id": "<addon_id>",
          "proration_behavior": "create_prorations"
        },
        {
          "addon_id": "<addon_id_2>",
          "change_at": "end_of_period"
        }
      ]
    }
  }'
```

Run the same body against [`/modify/preview`](/api-reference/subscriptions/preview-subscription-modification) first to see the resulting line items, invoice, or credit without writing anything. In a preview, new associations and line items carry the ID `(preview-created)`, and the invoice or wallet credit carries `(preview-invoice)` or `(preview-wallet-credit)` with `status: "preview"`. Previews quote the addon's list prices and default quantities; `override_line_items` are applied only on execute.

The execute response reports everything the change touched, with one `addon_associations` entry per attach:

```json theme={null}
{
  "subscription": { "...": "..." },
  "changed_resources": {
    "addon_associations": [
      {
        "id": "addon_assoc_01JD2M4G5H6J7K8L9M0N1P2Q3R",
        "addon_id": "addon_01JD2K3F4G5H6J7K8L9M0N1P2Q",
        "addon_status": "active",
        "start_date": "2026-10-12T10:00:00Z",
        "change_action": "created"
      },
      {
        "id": "addon_assoc_01JD2M4G5H6J7K8L9M0N1P2Q3S",
        "addon_id": "addon_01JD2K3F4G5H6J7K8L9M0N1P2R",
        "addon_status": "active",
        "start_date": "2026-11-01T00:00:00Z",
        "change_action": "created"
      }
    ],
    "line_items": [ { "change_action": "created" } ],
    "invoices": [ { "action": "created", "status": "PENDING" } ]
  }
}
```

`invoices[].status` is the payment status of the settlement invoice, such as `PENDING` or `SUCCEEDED`. A credit to the wallet appears instead as `action: "wallet_credit"` with `status: "issued"`.

<Warning>
  Store the `addon_associations[].id` values from the response. Removing an addon later requires its association ID, not the addon ID. `GET /v1/subscriptions/{id}/addons/associations` also lists active associations, but only those that overlap the current billing period; an addon scheduled to start after the period end appears there once its period begins.
</Warning>

### Addon attach fields

Each entry in `adds` takes:

| Field | Type | Required | Description |
| - | - | - | - |
| `addon_id` | string | Yes | The addon to attach |
| `cadence` | string | No | `recurring` (default) or `onetime`. A one-time addon ends with the billing period that contains its start date. |
| `proration_behavior` | string | No | `create_prorations` or `none`. Only `create_prorations` produces immediate charges or credits; omitting it behaves like `none`. |
| `start_date` | timestamp | No | When the addon starts. Defaults to now. Mutually exclusive with `change_at`. |
| `change_at` | string | No | `immediate`, or `end_of_period` for the current period end, as an alternative to an explicit `start_date` |
| `override_line_items` | array | No | Per-charge overrides: each entry takes a `price_id` plus `quantity`, `amount`, `billing_model`, or tier changes, the same shape as [subscription override line items](/docs/subscriptions/override-line-items) |
| `line_item_commitments` | object | No | Map keyed by `price_id` setting `commitment_amount` or `commitment_quantity`, `overage_factor`, and `enable_true_up` on the addon's usage charges. See [Commitment](/docs/subscriptions/commitment/overview). A subscription-level commitment and line-item commitments cannot be combined. |
| `metadata` | object | No | Stored on the association |

To gate a change behind a payment, add a `checkout` object at the top level of the modification request, next to `type` and `addon_bulk_params`; it is not a field of an attach entry. See [payment-gated addon changes](#payment-gated-addon-changes).

## Addon quantity

There is no quantity field on the attach itself. To sell more than one unit:

* **Override the charge quantity**: pass `override_line_items` with the fixed charge's `price_id` and the `quantity`. This is what the dashboard's Quantity input does.
* **Attach the addon again**: list the same `addon_id` more than once in `adds`, or attach it again in a later request. Each attachment is an independent association with its own line items and entitlements, so two attachments of a seat addon are two seats.

Without an override, a fixed charge's quantity defaults to its `min_quantity`, or 1.

## Remove addons from a subscription

Send one entry per association in `addon_bulk_params.removes`:

```bash theme={null}
curl -X POST https://us.api.flexprice.io/v1/subscriptions/<subscription_id>/modify/execute \
  -H "x-api-key: <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "addon",
    "addon_bulk_params": {
      "removes": [
        {
          "addon_association_id": "<addon_association_id>",
          "effective_date": "2026-10-15T00:00:00Z",
          "proration_behavior": "create_prorations"
        }
      ]
    }
  }'
```

### Addon removal fields

Each entry in `removes` takes:

| Field | Type | Required | Description |
| - | - | - | - |
| `addon_association_id` | string | Yes | The association to end, from the attach response or the associations endpoint |
| `effective_date` | timestamp | No | When the addon ends. Defaults to the current period end. Must fall inside the current billing period. Mutually exclusive with `change_at`. |
| `change_at` | string | No | `immediate` or `end_of_period` |
| `proration_behavior` | string | No | `create_prorations` credits unused time; `none`, or omitting it, does not |
| `reason` | string | No | Stored on the association as the cancellation reason |

Removal behavior to know:

* The default, removing at period end, produces zero credit: the customer keeps the addon until the date they already paid for.
* A `onetime` addon cannot be removed at all: attaching it already set its end date, so a removal fails with `This addon is already marked for removal`.
* An addon that carries a one-time charge can be removed only without `create_prorations`; with it, the removal fails with `line item billing_period must equal or strictly divide the subscription billing_period`.
* The association's `addon_status` flips to `cancelled` as soon as the removal is accepted, even when the end date is in the future. Billing and entitlements continue until the `end_date`, but the association no longer appears in `GET /v1/subscriptions/{id}/addons/associations`, which lists active associations only.
* A second removal of the same association fails with `This addon is already marked for removal`.

## Attach and remove addons in one request

Combine `adds` and `removes` to change several addons at once, for example to move a customer from one support tier to another:

```bash theme={null}
curl -X POST https://us.api.flexprice.io/v1/subscriptions/<subscription_id>/modify/execute \
  -H "x-api-key: <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "addon",
    "addon_bulk_params": {
      "removes": [
        {
          "addon_association_id": "<basic_support_association_id>",
          "change_at": "immediate",
          "proration_behavior": "create_prorations"
        }
      ],
      "adds": [
        {
          "addon_id": "<premium_support_addon_id>",
          "change_at": "immediate",
          "proration_behavior": "create_prorations"
        }
      ]
    }
  }'
```

* A request holds between 1 and 20 entries across `adds` and `removes`.
* The same addon may appear more than once in `adds`. An association may appear only once in `removes`; a repeat fails with `Each addon association can be removed at most once per request`.
* Each entry keeps its own timing and proration setting, with one exception: two attaches that start within the current period and grant the same feature through [entitlement grants](/docs/product-catalogue/features/entitlement-grants) must use the same start date, or the request fails with `Send them as separate changes, or give both the same date`.
* Everything runs in a single transaction: removals first, then attaches. If any entry fails, nothing is applied.
* An addon removed here no longer blocks a replacement that measures the same metered feature over a different reset period, as long as the removal takes effect no later than the attach. See [Before you start](#before-you-start).
* All charges and credits from the request are netted into one settlement document, described below.
* The response lists every association the request touched: attaches with `change_action: "created"`, and removals with `change_action: "ended"`, `addon_status: "cancelled"`, and their `end_date`.

## Proration and settlement

With `proration_behavior: "create_prorations"`, a mid-cycle change is priced for the remainder of the period:

* Fixed charges billed in advance are prorated on attach, and credited for unused time on removal, capped at what was invoiced for the period containing the removal date, or at list price when nothing has been invoiced for it yet.
* Fixed charges billed in arrear raise no upfront charge; the next invoice bills the prorated amount.
* Usage charges are never prorated; usage is billed at period end as usual.
* A charge on a different cadence from the subscription is prorated against its own period, not the subscription's.

The charges and credits of every entry in the request are then netted into a single document:

| Net result | What is created |
| - | - |
| Customer owes money | One one-off invoice with billing reason `SUBSCRIPTION_UPDATE`, and payment is attempted |
| Customer is owed money | A credit to the customer's prepaid wallet, created if none exists |
| Zero | Nothing |

An entry with `proration_behavior: "none"`, or with no `proration_behavior`, adds nothing to that document. Its in-advance charges are first billed on the renewal invoice, for the next period, so the rest of the current period goes unbilled. Its in-arrear charges are billed at period end for the time the addon was active.

## Entitlements and credit grants on attach

* The addon's entitlements join the subscription's from the association start and leave at its end. With `create_prorations`, a mid-cycle attach prorates entitlement grant quotas for the time left in the window, and the first credit grant for the time left in the period; with `none`, both are granted in full. Quota already granted is never taken back.
* The `override_entitlements` array on subscription creation rejects addon entitlements.
* The addon's credit grants are cloned to the subscription and credit the prepaid wallet. Recurring grants repeat each period while the association is active. Removing the addon cancels future grant applications without clawing back credits already granted. Grants are tracked per addon, not per attachment, so removing one attachment of a duplicated addon stops future grants for all of its attachments.

## Payment-gated addon changes

Pass a `checkout` object at the top level of the modification request to hold the change until the customer pays, using the same checkout session flow as subscription creation:

```json theme={null}
{
  "type": "addon",
  "checkout": {
    "payment_provider": "razorpay",
    "success_url": "https://example.com/success"
  },
  "addon_bulk_params": {
    "adds": [
      { "addon_id": "<addon_id>", "proration_behavior": "create_prorations" }
    ]
  }
}
```

* Checkout requires an `active` subscription; a request against a draft subscription is rejected.
* `override_line_items` and `line_item_commitments` cannot be combined with `checkout`; the request is rejected.
* Only entries with `create_prorations` put an amount on the checkout. When the netted amount is not a charge, including when no entry uses `create_prorations`, no session opens and the change applies immediately without payment. The request must still pass the checks above.
* While payment is pending, each attached association's `addon_status` is `pending`: no line items, no entitlements, no credit grants. Removals in the same request wait too; the addons they end stay active until the customer pays.
* Completion applies the whole change, removals included. Expiry or cancellation of the session archives the pending associations, and the removals never happen.
* While the session is open, an addon it is set to remove cannot be removed by another request (`Complete or cancel the pending checkout for this subscription first`), and neither can a pending association (`Complete or cancel the pending checkout for this addon first`).
* One open checkout session is allowed per subscription.

See [Checkout sessions](/docs/checkout/checkout-sessions) for the session object, redirect URLs, and expiry cleanup.

## Plan changes and cancellation

* A swap-in-place plan change (`/change/v2`) keeps addon associations by default. Its `entity_policies.addons` object sets a `default_behaviour` of `carry` or `drop`, with per-association overrides. Dropped addons end at the change, and are credited for unused time only when the change uses `proration_behavior: "create_prorations"` or `billing_period_behaviour: "anchor_at_effect"`.
* The legacy plan change (`/change/execute`) cancels the subscription and creates a new one; addons are not carried over.
* Cancelling a subscription cancels its addon associations, ends their line items, and stops future credit grant applications. Scheduled cancellations do this when they take effect.

## Webhooks

An addon change fires one `subscription.updated` once it is applied, however many addons the request touched. A payment-gated change fires it when its checkout completes. There are no `addon.*` events. Payment-gated flows also emit the `checkout.session.*` events.

## Via dashboard

### While creating a subscription

After you select a plan in the subscription form, an **Addons** section appears. Click **Add** to open the **Add addons** dialog:

1. Pick an addon under **Addon**. It opens as a card listing the charges compatible with the subscription's billing period and currency. To add more, pick them under **Add another addon**; each gets its own card, and an addon you picked leaves the list.
2. Set the **Quantity** on each fixed charge. Usage charges show "pay as you go".
3. Use a charge's row menu for **Override Price**, or **Configure Commitment** on usage charges.
4. Optionally set a **Start date** on each card, then click **Add addons**.

Each addon becomes a row in the section. The row menu's **Edit** reopens that one addon, and **Remove** takes it off before the subscription is created.

<Frame>
  <img src="https://mintcdn.com/flexprice/488Or8wBFgDqZF_G/images/docs/product-catalogue/addons/subscription-add-addon.png?fit=max&auto=format&n=488Or8wBFgDqZF_G&q=85&s=098fb49bb96f32d9b2413998ac626995" alt="The Add addons dialog in the subscription form with two addons staged" width="2940" height="1880" data-path="images/docs/product-catalogue/addons/subscription-add-addon.png" />
</Frame>

### On an existing subscription

The **Addons** card on the subscription details page is read-only. To manage addons, choose **Edit Subscription** from the menu on the **Subscription details** card; the option is unavailable for cancelled and inherited subscriptions. On the edit page, the **Addons** card shows **Add** while the subscription has no addons and **Modify** once it has some. Both open one dialog that collects every attach and removal and sends them as a single `addon_bulk_params` request when you click **Add** or **Save**:

* **Remove**: under **Current addons**, the trash icon marks an addon for removal. The row is tagged **Removing** and asks when it **Ends**: **End of current period** (preset), **Now**, or a **Custom date** inside the current period. Its **Proration** is preset to **No proration**; choose **Prorate** to credit unused time. **Undo** keeps the addon. An addon already scheduled to end shows its end date and cannot be marked.
* **Attach**: pick an addon under **Addon** (labelled **Add addon** once the subscription has addons), and more under **Add another addon**. Each opens as a card with its charges, quantities, overrides, and commitments, as in the subscription form. **Starts** sets **Now** (preset), **End of current period**, or a **Custom date**. **Advanced options** holds the **Cadence** (Recurring or One-time) and **Proration** (Prorate or No proration); opening it presets Recurring and No proration.

The dialog sends **Now** and **End of current period** as `change_at`, and a custom date as `start_date` or `effective_date`. One change holds at most 20 addons across attaches and removals. Each addon can be picked once per change; to sell two, raise its **Quantity**, or attach it again in a later change.

The example below makes the swap from [Attach and remove addons in one request](#attach-and-remove-addons-in-one-request): Basic Support ends **Now** and Premium Support starts **Now**, both with **Prorate**.

<Frame>
  <img src="https://mintcdn.com/flexprice/488Or8wBFgDqZF_G/images/docs/product-catalogue/addons/edit-subscription-modify-addons.png?fit=max&auto=format&n=488Or8wBFgDqZF_G&q=85&s=7a210ad6356efc0bf51e03289bb2b6da" alt="The Modify addons dialog on the Edit Subscription page with one addon marked for removal and one being attached" width="2940" height="2476" data-path="images/docs/product-catalogue/addons/edit-subscription-modify-addons.png" />
</Frame>

The row menu of each attached addon also offers two actions, both unavailable for an addon that is already scheduled to end:

* **Configure**: changes the addon's charges on this subscription. A fixed charge takes a new quantity, a new price on a flat-fee charge without a custom price unit, and an effective date; click **Preview** to see the resulting charges or credits, then **Apply changes**. A usage charge opens a price override with an optional effective date and the charge's commitment settings. An addon with several charges lists them first.
* **Cancel**: removes that one addon. It asks for an **Effective end date** (preset to the current period end and limited to the current period) and a **Proration** choice, preset to None.

## Legacy addon requests

Two older ways to change addons still work, and both run through the same batch logic:

* `addon_params` on the modification API changes one addon per request: `"action": "add"` with the attach fields under `add`, or `"action": "remove"` with the removal fields under `remove`. It is applied as a batch of one entry. It cannot be sent together with `addon_bulk_params`, and a removal sent this way cannot carry `checkout`.
* `POST /v1/subscriptions/addon` and `DELETE /v1/subscriptions/addon` are deprecated. `POST` takes the attach fields plus `subscription_id` and an optional inline `checkout`; `DELETE` takes the removal fields and finds the subscription from the association.

Use `addon_bulk_params` for new integrations: a single attach or removal is a request with one entry. Unlike the deprecated endpoints, the modification API also offers previews and reports every change in one response.
