# Release 112

### Highlights

* **Omnichannel commerce:** Use the new Limio Partner Portal, integrated with the Limio Catalog, to explore new commerce channels
* **Flexible pricing and packaging:** Volume pricing, Evergreen subscriptions
* **Improve retention:** New Update Subscription capabilities for complex subscription upgrades, downgrades, and add-on management

{% hint style="danger" %}

### Breaking Changes

Please note there are several breaking changes in this release. Please read the **Stability and Quality Improvements** section below to review in detail.&#x20;
{% endhint %}

### New features and improvements

#### **Introducing the Limio Partner Portal**

The new Limio Partner Portal lets you create partner-only pricing pages and checkouts, so partners can purchase subscriptions on behalf of customers without exposing partner pricing to end users.

After a purchase, partners can also manage customer subscriptions using our out-of-the-box subscription management capabilities, such as upgrades, downgrades, add-ons, and cancellations.

Partner capabilities are also available via API, allowing trusted third parties to submit partner orders programmatically.

* [Partner APIs](https://docs.limio.com/api/partner-portal-api)

![](/files/UYrb7K4rIrwpoJDjzKK9)

To support the Partner Portal, we have developed these new out-of-the-box Limio components:&#x20;

* [Partner Customers Table](https://docs.limio.com/components/component-library/partner-portal-components/component-partner-customers-table)
* [Partner Invoices Table](https://docs.limio.com/components/component-library/partner-portal-components/component-partner-invoice-table)

Learn more: <https://docs.limio.com/product/partner-portal/partner-portal-overview>

#### **Upgrades, downgrades, and add-on management with Update Subscription**

With our new Update Subscription capabilities, customers can upgrade, downgrade, or manage add-ons for an existing subscription.

Unlike the existing Switch experience, Update Subscription is designed for multi-product businesses and supports proration visibility, multi-product changes, hybrid pricing, and add-on purchases within the same flow.

Release 112 also adds platform event support for Update Subscription, making subscription changes easier to integrate with Salesforce.

Supported scenarios:

* Upgrade a subscription (effective immediately)
* Downgrade a subscription (scheduled for end of term)
* Add add-ons without changing the base offer
* Change the base offer and add-ons in a single flow
* Automatically remove incompatible add-ons during upgrades or downgrades

<figure><img src="/files/YdTS286a8qZdahjvOpuu" alt=""><figcaption></figcaption></figure>

To support Update Subscription, we have developed these new out-of-the-box Limio components:&#x20;

* [Update subscription offers](https://docs.limio.com/components/component-library/self-service-components/component-update-subscription-offers)
* [Update cart items](https://docs.limio.com/components/component-library/self-service-components/component-update-cart-items)
* [Compatible add-ons](https://docs.limio.com/components/component-library/self-service-components/component-compatible-add-ons)
* [Change summary](https://docs.limio.com/components/component-library/self-service-components/component-change-summary)
* [Billing schedule](https://docs.limio.com/components/component-library/self-service-components/component-billing-schedule)

{% hint style="warning" %}
Note: The Billing Schedule component can no longer be used in Acquisition use cases. It has been reworked to be in Update Subscription use cases only, so please remove it from Acquisition pages.&#x20;
{% endhint %}

#### **Volume pricing now supported**

Limio now supports volume pricing for offers and add-ons, allowing you to model unit-based charging directly in Limio. This is well suited to B2B and SaaS use cases such as seat-based pricing, data usage, or API call bundles, and enables tiered pricing without adding complexity to the customer journey.

Volume-priced offers and add-ons are supported across acquisition and Update Subscription flows, including initial purchases, add-on purchases, and upgrades or downgrades.

Limio’s out-of-the-box components have also been updated to support quantity selection and tier handling for a consistent checkout and update experience.

<figure><img src="/files/tW0gMZKeDoWatqG46ilG" alt=""><figcaption></figcaption></figure>

Learn more: <https://docs.limio.com/integrations/zuora-integration/manage-your-pricing/how-to-configure-subscriptions-in-limio-offers-with-initial-price-and-zuora-rate-plans/volume-pricing>

#### **Improved out-of-the-box styling in the Modular Checkout**

Modular Checkout now has improved styling out-of-the-box to reduce reliance on developer-led styling and to better align with our cart components. This helps non-technical teams ship checkout changes faster, while keeping the experience consistent across journeys.

We've also updated the payment method selector to use a more modern tile interface.

![](/files/7dnQu8IZRRLymhwAeroP)

{% hint style="warning" %}
Note: Due to the redesign, we recommend testing your checkout for styling issues, especially the payment method selector, and we also recommend testing styling of any popovers you use through your Limio pages.&#x20;
{% endhint %}

#### **Write custom events to the LFS Timeline**

A new API lets you write external events into the Limio for Salesforce timeline, so customer service representatives can see a more complete customer history including events that happen outside Limio.

Previously, the timeline only displayed Limio-initiated events sourced from the Limio database. With this API, trusted external systems (such as Zuora, CRM, or partner platforms) can write events to the same event store.&#x20;

Learn more: <https://docs.limio.com/integrations/setting-up-limio-for-salesforce/subscriber-timeline/activity-timeline>

#### **Use delivery address for tax calculation when available**

The tax preview logic in the Cart Summary component has been updated. When a delivery address is provided, tax is calculated using the delivery address. Otherwise, the billing address is used.&#x20;

Learn more: <https://docs.limio.com/components/component-library/cart-components/component-cart-summary>

#### **New journey condition based on subscription billing country**

A new condition type, Subscription Billing Country, is now available in Limio Journeys. This allows you to show different Pages to customers based on the billing country of their subscription, rather than their detected IP location.

Learn more: <https://docs.limio.com/product/journey/what-are-limio-journeys/create-a-journey-based-on-the-customer-location>

#### **Support evergreen subscriptions from initial term**

You can now configure your offers to be Evergreen from initial term, instead of only in the renewal term as it was previously. Setting the Initial Term offer attribute to Evergreen creates the subscription as evergreen in Zuora immediately on purchase, rather than requiring a renew-to-evergreen transition. All self-service and partner flows (upgrade, downgrade, add-on, cancellation) handle evergreen subscriptions correctly.

#### **Improvements to Salesforce integration**

* Our Salesforce integration can now be used even when you are not using Limio for Salesforce.&#x20;
* New orders purchased using anonymous authentication include the owner in the platform event.&#x20;

### Bug fixes

#### **Cross-sells not removed in cascade removal scenario**

We fixed an issue where cross-sell items were not removed when the base offer was removed, even when the cascade removal prop was enabled.

#### **Billing Schedule component styling issues**

We fixed a UI issue where the Billing Schedule background container could appear clipped, improving layout consistency across screen sizes.

#### **Intermittent user creation issue**

We fixed intermittent failures where creating a new Limio Commerce user did not consistently create the user or send the expected email.

#### **Subscription owner not displayed when purchased anonymously**

We fixed an issue where the subscription owner was not displayed in the Subscriptions Tab view when the subscription was originally purchased with anonymous authentication. Now, the subscription owner is always visible in the Subscription Tab views.&#x20;

#### **Cart Items prices not aligned in read-only mode**

We fixed a display issue where cross-sell add-on pricing could appear misaligned compared to other prices when Cart Items was shown in read-only mode (for example on order complete pages).

#### **Duplicating offers and pages error (#111231, #110848)**

We fixed an issue with duplicating offers or pages after selecting a specific offer or page. Now, users can duplicate a specific offer or page, or choose to duplicate offers or pages from the offers or pages tables.

#### **Promo codes applied to rate plan charges that are discount model**

We have added a new check to our promo codes to prevent them from being applied to rate plan charges which are a discount model, which is not supported by Zuora.&#x20;

### Stability and quality improvements

#### Flattened Add-on Order Items

Add-ons are now top-level items in the order, rather than being nested inside their parent offer's `orderItem.addOns[]` array. This aligns with industry standards and simplifies how add-ons are processed throughout the system.

* Add-ons now appear as separate `OrderItem`s at the same level as main offers, with `type: "add_on"` and a `parentId` linking them to their parent
* Main offers have `type: "offer"`
* The `addOn` property on order items has been renamed to `offer` — add-ons share the same structure as regular offers

Two helper functions are available to filter the order items array:

* `isOfferOrderItem(item)` — true for main offers
* `isAddOnOrderItem(item)` — true for add-ons

{% hint style="warning" %}
This improvement has potentially breaking changes:&#x20;

* `orderItem.addOns[]` no longer exists, so custom components reading nested add-ons from an order item should now read add-ons from the top-level order items array. Use filtering by  `type === "add_on"` and matching on `parentId`&#x20;
* `orderItem.addOn` has been renamed to `orderItem.offer` , so custom components or integrations referencing `orderItem.addOn` should now reference `orderItem.offer`&#x20;

Backward compatibility for legacy nested structures is preserved during normalisation — existing process events (e.g. credit memo events) using the old format will continue to be handled correctly.
{% endhint %}

#### Country logic more consistent&#x20;

* The payment gateway displayed to the customer is now selected based on the customer's billing address country rather than a browser cookie or the offer's allowed countries list.
* Address objects (billing and delivery) no longer include a `stateCode` field. For US and Canadian addresses, the `state` field already contains the full state/province name (e.g. "New Jersey", "Ontario") — the abbreviated code (e.g. "NJ", "ON") is no longer returned as a separate field.
* The way `order.country` is set during the checkout lifecycle has been clarified and made more predictable:
  * At checkout initiation: `order.country` is only set if it is explicitly provided in the request payload. For renewal checkouts, it is taken from the existing subscription's purchase country. The API no longer requires a `country` field to be sent.
  * When fetching a basket: If `order.country` is not already set, the system will now automatically populate it based on the customer's detected location (from CDN/browser headers or the `limio-country` cookie).
  * Billing address updates no longer automatically change `order.country` — the two are now independent.

{% hint style="warning" %}
Note: This has 2 potentially breaking changes:&#x20;

* The stateCode on address objects no longer exists. Use the state field instead.
* Systems should send data in order.country instead of a top-level country.&#x20;
  {% endhint %}

#### Tracking data inside order object

Checkout tracking information (UTM parameters, offer paths, campaign tags, referrer) was previously stored as a separate top-level field alongside the order in the checkout session. It is now stored **inside the order** as `order.tracking`.

{% hint style="warning" %}
Note: This has a breaking change for any component or integration reading basketSession.tracking or checkout.tracking. Any components or integrations should read from basketSession.order.tracking instead, though backwards compatibility is ensured in this release.&#x20;
{% endhint %}

#### Basket ID stored in landing-state cookie

The basket (checkout) ID is now managed server-side and stored in the `lmo_ls` landing-state cookie, replacing the previous approach of storing it in `sessionStorage` or passing it via URL parameters.

This makes the basket more reliably available across page navigations, browser history traversal, and shared-computer scenarios. Checkout redirect URLs now always include a `?basket=<id>` parameter, making the active basket explicitly visible in navigation and access logs.

As part of this change, several top-level fields in the basket response have been deprecated and consolidated under `order.*`:

| Deprecated field            | Now at                                 |
| --------------------------- | -------------------------------------- |
| `basketSession.basket`      | `basketSession.order`                  |
| `basketSession.country`     | `basketSession.order.country`          |
| `basketSession.locale`      | `basketSession.order.locale`           |
| `basketSession.tracking`    | `basketSession.order.tracking`         |
| `basketSession.redirectUrl` | Removed — see Basket SDK section below |

The basket response no longer includes private server context — only the data relevant to the frontend is returned.

* `getCheckoutId()` has been replaced by `getCurrentBasketId()`, which reads from Redux (backed by the cookie) rather than `sessionStorage`
* The `limio_order` `sessionStorage` key is no longer written to

{% hint style="warning" %}
This improvement has potentially breaking changes:&#x20;

* If your custom components use any of the deprecated fields, replace them with the new fields.&#x20;
* `getCheckoutId()` has been removed, so custom components calling it should now use `getCurrentBasketId()`&#x20;
* `sessionStorage["limio_order"]` is no longer written to, so custom components reading the basket ID from `sessionStorage` should now use `getCurrentBasketId()` from the SDK, or read from the `lmo_ls` cookie
* `lmo_ls` cookie may be missing `domain` attribute if `oauth.cookie_domain_mapping` is not explicitly configured, so explicitly configure `oauth.cookie_domain_mapping` if the cookie needs to be set on a parent domain
  {% endhint %}

#### Basket SDK improvements

* In `useBasket()`:
  * `addOfferToBasket` has been removed — use `addToBasket` instead
  * `addAddOnToBasket` has been removed — use `addToBasket` with `parentId` instead
  * `syncBasket` has been removed— no longer needed; every basket action now automatically syncs to the server
* In `addToBasket()` :
  * The `pushToCheckout: true` option no longer clears the basket before adding a new item. The campaign attribute `push_to_checkout__limio` no longer affects basket behaviour either. Use `clearOrderItems`  explicitly if this is needed.
  * `redirectUrl` is no longer accepted as an option in `addToBasket`
  * `redirectUrl` is no longer read from the `?redirect_url=` URL query parameter
  * `redirectUrl` is no longer stored in or returned from basket/checkout session responses
  * `reinitialiseOrderItems` option also removed from `addToBasket`
  * Google Pay's fallback button no longer accepts a `redirectUrl` prop
  * `redirectState` is unaffected and remains in place
* `updateBasketDetails`  is now included in the SDK. This function is useful to update basket metadata such as billing address, customer details, or custom fields, so the information is available on abandoned baskets

{% hint style="warning" %}
Note: This has a breaking change for any custom components using these affected functions. Please review your custom components, and update them where applicable.&#x20;
{% endhint %}

#### Checkout API Restructuring

The acquisition checkout APIs have been reorganised to follow consistent REST semantics with explicit `GET` / `POST` / `PUT` endpoints.&#x20;

| Operation              | Endpoint                          |
| ---------------------- | --------------------------------- |
| Create new basket      | `POST /api/checkout/subscription` |
| Update existing basket | `PUT /api/checkout/subscription`  |
| Get existing basket    | `GET /api/checkout/subscription`  |

The public API function `initiateCheckoutWithOffer` has also been renamed to `initiateCheckout`.&#x20;

{% hint style="warning" %}
Note: This has potentially breaking changes:&#x20;

* `initiateCheckoutWithOffer` was renamed to `initiateCheckout` and the payload changed, so any integrations calling `initiateCheckoutWithOffer` directly should now use `initiateCheckout`&#x20;
  {% endhint %}

#### Removed trialist concept

Limio no longer automatically checks whether a customer has previously used a trial offer and blocks them from accessing another. Specifically:

* The backend no longer checks the `trialist` flag on customer records at checkout
* The `limio-invalid` cookie is no longer set when a customer attempts to take a second trial
* Offer components (Offers, Grouped Offers) no longer filter out trial offers based on the `limio-invalid` cookie
* The "ineligible heading" and "ineligible message" props on the Grouped Offers component no longer display a trial warning

{% hint style="warning" %}
Note: This has a breaking change for any customers relying on the trial eligibility check. Use alternative methods like journeys or custom components moving forward.&#x20;
{% endhint %}

#### Improvement to Zuora Order Processing

The Zuora subscription creation pipeline has been refactored to use `OrderItem` as the single source of truth across all flows — replacing separate code paths for preview orders and new orders. Each order item now creates its own subscription entry with its own start date and subscription reference.

#### **Unified currency formatting**

Previously, each component was responsible for reading the locale itself to format currency, which caused inconsistencies across the shop. We now have a unified function for reading the locale that all Limio components use.


---

# Agent Instructions: 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:

```
GET https://docs.limio.com/release-notes/limio-commerce-release-notes/readme/release-112.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
