> For the complete documentation index, see [llms.txt](https://docs.limio.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.limio.com/developers/limio-sdk/subscription-update-checkout.md).

# Update Subscription

Subscription update checkouts let existing subscribers change their plan — upgrade, downgrade, or modify add-ons ( [https://docs.limio.com/product/self-service/what-are-the-standard-functionality-of-limio-self-service-manage-my-account/how-to-configure-an-update-subscription-flow](https://docs.limio.com/product/self-service/what-are-the-standard-functionality-of-limio-self-service-manage-my-account/how-to-configure-an-update-subscription-flow "mention") ) . They use a different flow from standard acquisition checkouts because the server drives the available next actions (effective dates, proration, removal side-effects).

{% hint style="info" %}
Subscription update checkouts use `order_type: "update_subscription"`. The SDK detects this automatically and routes mutations through the subscription update API.
{% endhint %}

### How it works

1. From a subscription management page, call `initiateCheckout` with `order_type: "update_subscription"` and a `forSubscription` reference.
2. The server creates an update checkout session and returns a `checkoutId`.
3. Redirect the subscriber to the update checkout page with the `checkoutId`.
4. On the offer selection page, use `selectOfferForSubscriptionUpdate` to let the subscriber pick a new plan.
5. The server resolves effective dates, proration, and next actions, then returns the updated basket.

### Starting an update checkout

Use `initiateCheckout` with `order_type: "update_subscription"` and the subscription ID. This creates the server-side update session.

```tsx
import React from "react"
import { useBasket } from "@limio/sdk"

const SubscriptionUpdateButton = ({ subscription, updatePageUrl = "/update" }) => {
  const { initiateCheckout, basketLoading } = useBasket()

  const handleUpdate = async () => {
    const basket = await initiateCheckout({
      order: {
        order_type: "update_subscription",
        forSubscription: {
          id: subscription.id
        }
      }
    })

    const checkoutId = basket.order.checkoutId
    window.location.href = `${updatePageUrl}?basket=${checkoutId}`
  }

  return (
    <button onClick={handleUpdate} disabled={basketLoading}>
      Change plan
    </button>
  )
}
```

{% hint style="info" %}
The `forSubscription.id` tells the server which subscription is being modified. The response includes the `checkoutId` used to track this update session.
{% endhint %}

***

### Selecting a new offer

Once the update checkout session exists, use `selectOfferForSubscriptionUpdate` on the offer selection page. Only one offer can be active at a time — selecting a new offer resets the checkout and re-resolves server-side state.

```typescript
await selectOfferForSubscriptionUpdate(orderItem: Partial<UpdateSubscriptionBasketItem>): Promise<void>
```

**Parameters:**

| Field      | Type           | Description                               |
| ---------- | -------------- | ----------------------------------------- |
| `offer`    | `ElasticOffer` | The offer the subscriber is switching to  |
| `quantity` | `number`       | Quantity (typically `1` for plan changes) |

```tsx
import React from "react"
import { useBasket } from "@limio/sdk"

const UpgradeOption = ({ offer, currentOfferId }) => {
  const { selectOfferForSubscriptionUpdate, clearOrderItems, basketLoading } = useBasket()

  const handleSelect = async () => {
    // Clear existing items first — the server will re-resolve
    // effective dates and side-effects for the new selection
    if (currentOfferId && currentOfferId !== offer.id) {
      await clearOrderItems()
    }

    await selectOfferForSubscriptionUpdate({
      offer,
      quantity: 1
    })

    // Redirect to the update checkout page
    const params = new URLSearchParams(window.location.search)
    const checkoutId = params.get("basket")
    const ownerId = params.get("ownerId")
    window.location.assign(`/update?basket=${checkoutId}&ownerId=${ownerId}`)
  }

  return (
    <button onClick={handleSelect} disabled={basketLoading}>
      Select {offer.data.attributes.display_name__limio}
    </button>
  )
}
```

{% hint style="warning" %}
Selecting a new offer drops all existing order items and sends the new selection to the server. The server returns updated next actions, effective dates, and any proration details.
{% endhint %}

***

### Clearing items before re-selection

When the subscriber changes their mind and picks a different offer, await `clearOrderItems()` before `selectOfferForSubscriptionUpdate`. This ensures the server re-resolves everything cleanly for the new selection.

```typescript
const { clearOrderItems, selectOfferForSubscriptionUpdate } = useBasket()

// User changed their mind — clear and re-select
await clearOrderItems()
await selectOfferForSubscriptionUpdate({ offer: newOffer, quantity: 1 })
```

`clearOrderItems` is asynchronous. It sends a remove action for every item in the basket to the server and updates the basket state with the response, so it must be awaited before you select the new offer.

***

### Adding items to a subscription update checkout

Use `addSubscriptionOrderItem` to append an order item to the subscription update basket. Unlike `selectOfferForSubscriptionUpdate` (which replaces all items), this method is additive — it keeps existing items and appends the new one.

The caller constructs the full order item shape, giving visibility and control over what's being added.

```typescript
await addSubscriptionOrderItem(orderItem: UpdateSubscriptionBasketItem): Promise<void>
```

**Parameters:**

| Field                 | Type     | Description                                            |
| --------------------- | -------- | ------------------------------------------------------ |
| `orderItemActionType` | `string` | The action type (e.g. `"update"` for quantity changes) |
| `offer`               | `Offer`  | The offer being modified                               |
| `quantity`            | `number` | The target quantity                                    |

{% hint style="info" %}
The server resolves the effective date based on the direction of the quantity change. Increases take effect immediately; decreases take effect at end-of-term.
{% endhint %}

**Example — seat quantity update flow:**

A common pattern is to use `addSubscriptionOrderItem` when initially adding a quantity change to the basket, then `updateItemQuantity` for subsequent adjustments to items already in the basket.

```tsx
import React, { useState } from "react"
import { useBasket } from "@limio/sdk"

const SeatQuantityUpdater = ({ subscription, subscriptionOffer }) => {
  const {
    initiateCheckout,
    addSubscriptionOrderItem,
    updateItemQuantity,
    orderItems,
    basketLoading
  } = useBasket()
  const [quantityUpdates, setQuantityUpdates] = useState([])

  // Step 1: Start the update checkout session if not already initiated
  const handleInitiate = async () => {
    const { nextActions } = await initiateCheckout({
      order: {
        order_type: "update_subscription",
        forSubscription: { id: subscription.id }
      }
    })
    // The server returns available quantity updates for the subscription
    setQuantityUpdates(nextActions.quantityUpdates ?? [])
  }

  // Step 2: Add a quantity change to the basket (item not yet in basket)
  const handleAddQuantityUpdate = async (offer, newQuantity) => {
    await addSubscriptionOrderItem({
      orderItemActionType: "update",
      offer,
      quantity: newQuantity
    })
  }

  // Step 3: Adjust quantity of an item already in the basket
  const handleAdjustQuantity = async (itemId, newQuantity) => {
    await updateItemQuantity(itemId, newQuantity)
  }

  return (
    <div>
      <button onClick={handleInitiate} disabled={basketLoading}>
        Manage seats
      </button>

      {/* Show available quantity updates from the server */}
      {quantityUpdates.map((item, i) => (
        <div key={item.id ?? i}>
          <span>Current seats: {item.currentQuantity}</span>
          <button
            disabled={basketLoading}
            onClick={() => handleAddQuantityUpdate(item, 10)}
          >
            Set to 10 seats
          </button>
        </div>
      ))}

      {/* Show items already in the basket with quantity adjustment */}
      {orderItems.map((item) => (
        <div key={item.id}>
          <span>Seats: {item.quantity}</span>
          <button
            disabled={basketLoading}
            onClick={() => handleAdjustQuantity(item.id, item.quantity + 1)}
          >
            Add seat
          </button>
        </div>
      ))}
    </div>
  )
}
```

**When to use which method:**

| Scenario                                       | Method                                                          |
| ---------------------------------------------- | --------------------------------------------------------------- |
| Item is **not yet** in the basket              | `addSubscriptionOrderItem`                                      |
| Item is **already** in the basket              | `updateItemQuantity`                                            |
| Selecting a completely new plan (replaces all) | `selectOfferForSubscriptionUpdate`                              |
| Removing an offer from the subscription        | `addSubscriptionOrderItem` with `orderItemActionType: "remove"` |

Your component has access to `orderItems` and `nextActions.quantityUpdates` to determine which method to use.

***

### Quantity updates in subscription update checkouts

`updateItemQuantity` behaves differently in subscription update checkouts. Instead of calling the standard basket API, it routes through the subscription update API so the server can recalculate proration and effective dates.

```typescript
const { updateItemQuantity } = useBasket()

// In a subscription update checkout, this automatically
// uses the subscription update API under the hood
await updateItemQuantity(itemId, 3)
```

You don't need to handle this routing yourself — the SDK detects `order_type: "update_subscription"` and uses the correct endpoint.

***

### Removing an offer from a subscription

Use `addSubscriptionOrderItem` with `orderItemActionType: "remove"` to remove an offer from a multi-offer subscription without cancelling the subscription. The removed offer stays active and billed until the end of the current term — the same effective-date semantics as a downgrade.

{% hint style="info" %}
Offer removal is available from Release 116.
{% endhint %}

```typescript
await addSubscriptionOrderItem({
  orderItemActionType: "remove",
  type: "subscription_offer",
  offer: subscriptionOffer
})
```

**Parameters:**

| Field                 | Type                   | Description                                                  |
| --------------------- | ---------------------- | ------------------------------------------------------------ |
| `orderItemActionType` | `"remove"`             | Marks this item as an offer removal                          |
| `type`                | `"subscription_offer"` | Identifies the item as an existing offer on the subscription |
| `offer`               | `Offer`                | The subscription offer being removed                         |

**Example — removal flow:**

```tsx
import React from "react"
import { useBasket } from "@limio/sdk"

const RemoveOffersButton = ({ subscription, offersToRemove, updatePageUrl = "/update" }) => {
  const { initiateCheckout, addSubscriptionOrderItem, basketLoading } = useBasket()

  const handleRemove = async () => {
    // 1. Start the update checkout session
    const basket = await initiateCheckout({
      order: {
        order_type: "update_subscription",
        forSubscription: { id: subscription.id }
      }
    })

    // 2. Add one removal item per offer — batch all removals
    //    into this single order
    for (const offer of offersToRemove) {
      await addSubscriptionOrderItem({
        orderItemActionType: "remove",
        type: "subscription_offer",
        offer
      })
    }

    // 3. Send the subscriber to the update checkout page to
    //    review and submit the order
    window.location.href = `${updatePageUrl}?basket=${basket.order.checkoutId}`
  }

  return (
    <button onClick={handleRemove} disabled={basketLoading}>
      Remove selected offers
    </button>
  )
}
```

**Rules the server enforces:**

* **A subscription always keeps at least one active offer.** An order that would remove every offer is rejected. An offer added in the same order offsets a removal, so swapping (remove one offer, add another) passes.
* **Removals take effect at the end of the current term.** In a mixed order (for example a removal plus a quantity change), the removal drives the whole order's effective date to term-end, so the offer is never stripped immediately. The exception is a swap: when an offer is added in the same order, the effective date follows the added offer's upgrade or downgrade classification instead.
* **Batch all removals into a single order.** Submitting an update order sets a pending change on the subscription that blocks further update checkouts until the effective date passes, so sequential removal orders are not possible.

***

### Notes

* Start every update flow with `initiateCheckout` using `order_type: "update_subscription"` and `forSubscription: { id }`.
* Always await `clearOrderItems()` before selecting a different offer to avoid stale server state.
* The `ownerId` query parameter identifies which subscription is being updated — pass it through when redirecting.
* Server-driven state means the basket response may include fields not present in standard checkouts (effective dates, next actions, proration details).
* See [Basket (Cart)](/developers/limio-sdk/basket.md) for all other basket methods (adding items, promo codes, checkout navigation, etc.).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.limio.com/developers/limio-sdk/subscription-update-checkout.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
