Reserve stock on demand and freeze prices

Learn how to prevent overselling and improve the checkout experience by reserving items on demand while freezing all prices.

Ask about this Page
Copy for LLM
View as Markdown
This tutorial uses Inventory reservations functionality that is in public beta.

In this tutorial, you will learn how to combine reserving stock with freezing Cart prices. This approach ensures that customers can complete their checkout with the products they want, at the prices they expect.

You will learn to:

  • Enable reservation functionality in your Project.
  • Create a Cart with unreserved Line Items.
  • Reserve stock by changing the inventory mode of each Line Item.
  • Check for warnings indicating unsuccessful reservations.
  • Freeze the prices, discounts, and shipping costs of Line Items.

Prerequisites

Before you begin, you should be familiar with:

You should also have a commercetools Project with the following:
  • A Product: Create a Product with at least one Product Variant. Note the SKU of the variant.
  • API Client: An API Client with the following scopes:
    • manage_orders:{projectKey} to create and update Carts and Orders.
    • manage_project_settings:{projectKey} to update reservation-related Project settings.
    • manage_products:{projectKey} to create and update Inventory Entries.

Instructions

Consider a scenario in which you want to ensure a customer successfully completes the checkout of their Cart. You can use the following two features together to achieve this:

  • Freezing a Cart: prevents price changes to the Cart during checkout, depending on the chosen freeze strategy.
  • ReserveOnCart InventoryMode: reserves items in the Cart. Reservations remain active until the Cart is ordered or the reservation expires.

You can apply this inventory mode in one of two ways:

  • On the Cart: all Line Items are reserved automatically. This is useful when you want to reserve stock for all items as soon as they are added to the Cart.
  • On individual Line Items: some or all Line Items are reserved, but at a specific point in time that you choose. This approach gives you more control over when to reserve stock during your checkout flow.

This tutorial focuses on setting the inventory mode on individual Line Items, allowing you to reserve stock on demand.

Benefits of combining freezing with reservations

Without freezing, a customer's Cart total can change between adding items and completing checkout. For example, a price promotion could end, a discount could be removed, or shipping costs could increase. This creates an unpredictable checkout experience.

Similarly, without reserving stock, items can become unavailable during checkout. For example, a customer has an item in their Cart and is actively checking out. Another customer comes along and purchases the last available item, causing the first customer's order to fail.

By combining both features, you ensure that:

  • The Cart total remains the same during the entire checkout flow.
  • Stock is guaranteed to be available when you create the Order.

This gives customers confidence that Cart prices and items remain accurate throughout checkout.

To implement this, use the setLineItemInventoryMode update action to reserve stock on individual Line Items, then freeze prices with freezeCart. Before doing that, enable reservations on your Project, then create an Inventory Entry and a Cart.

Set the Project-level expiration

Before you can use reservations, you must set the Project-level reservation expiration time. After you set this default, you can use the ReserveOnCart InventoryMode.

This is a mandatory step to ensure that stock is not held indefinitely, which could lead to it being unavailable for other customers. You only have to set this once per Project.

To do this, use the Set Reservation Expiration in Minutes update action on the Project. Specify a value between 1 and 44640 minutes (31 days).
Set Project-level reservation expirationbash
curl -X POST "https://api.europe-west1.gcp.commercetools.com/{{project-key}}" \
-H "Authorization: Bearer {{auth-token}}" \
-H "Content-Type: application/json" \
-d '{
	"version": {{project-version}},
	"actions": [
		{
			"action": "setReservationExpirationInMinutes",
			"reservationExpirationInMinutes": 30
		}
	]
}'
You can override this value by optionally using the Set Reservation Expiration in Minutes update action on an Inventory Entry.

Create an Inventory Entry

To reserve stock you must first have an inventory entry to track the available stock. The inventory entry must have the same sku as your Product Variant (SKU-1 in this example). Set the quantityOnStock to 10.
Create an Inventory Entrybash
curl -X POST "https://api.europe-west1.gcp.commercetools.com/{{project-key}}/inventory" \
-H "Authorization: Bearer {{auth-token}}" \
-H "Content-Type: application/json" \
-d '{
    "sku": "SKU-1",
    "quantityOnStock": 10
}'

Create a Cart

Create a Cart without reserving stock immediately. Omit the inventoryMode property so that Line Items use the default mode: None.
Create a Cart without reserving stockbash
curl -X POST "https://api.europe-west1.gcp.commercetools.com/{{project-key}}/carts" \
-H "Authorization: Bearer {{auth-token}}" \
-H "Content-Type: application/json" \
-d '{
    "currency": "USD",
    "lineItems": [
        {
            "sku": "SKU-1",
            "quantity": 1
        }
    ]
}'

Verify no reservation was made

Since the Inventory Mode has not yet been set to ReserveOnCart, no stock has been reserved. Verify stock availability by checking the availableQuantity of the Inventory Entry. It will still be equal to 10.
Get Inventory Entrybash
curl -X GET "https://api.europe-west1.gcp.commercetools.com/{{project-key}}/inventory/{{inventory-id}}" \
-H "Authorization: Bearer {{auth-token}}"

Reserve stock

When the customer reaches a desired point in the checkout flow, you can reserve stock for the items in the Cart. After that, you can freeze the Cart to lock in prices, discounts, and shipping costs. Reserve stock before freezing to ensure that stock is actually available.

To reserve stock for a specific Line Item, use the setLineItemInventoryMode update action.
For Carts with multiple Line Items, add a setLineItemInventoryMode update action for each Line Item that you want to reserve.
Reserve stock on the Line Itembash
curl -X POST "https://api.europe-west1.gcp.commercetools.com/{{project-key}}/carts/{{cart-id}}" \
-H "Authorization: Bearer {{auth-token}}" \
-H "Content-Type: application/json" \
-d '{
    "version": {{cart-version}},
    "actions": [
      {
        "action": "setLineItemInventoryMode",
        "lineItemId": "{{line-item-id}}",
        "inventoryMode": "ReserveOnCart"
      }
    ]
}'

Verify the reservation

You can verify that the reservation was successful by checking the response. If all of the reservations were successfully created, then the inventoryMode of each Line Item will be ReserveOnCart. The absence of any warnings is also a good indicator that everything worked as expected.
If a Line Item's inventory mode is not ReserveOnCart and the Cart's warnings field contains a CannotCreateReservationWarning, then the reservation was unsuccessful. You'll need to handle these warnings before proceeding to freeze the Cart.

When a reservation fails, you have several options:

  • Increase the quantity of the low stock inventory entry and retry the setLineItemInventoryMode update action.
  • Set the inventoryMode of the low stock line item to TrackOnly. Using this inventory mode, an Order can be created even if there is insufficient stock. The line item quantity will be deducted from the inventory entry quantity when ordering, which means the inventory can go negative. This is the intended behavior when using TrackOnly as a fallback.
  • Remove the low stock line item from the Cart. You can alternatively use this same update action to reduce the quantity to a value that doesn't exceed the inventory entry's availableQuantity.
  • Set restockableInDays on the inventory entry and retry setLineItemInventoryMode. Reservations using ReserveOnCart or ReserveOnOrder will succeed, even when there is insufficient stock, if restockableInDays is defined.

Freeze the Cart

If the reservations were successful, you can now freeze the Cart using one of two freeze strategies: SoftFreeze or HardFreeze. Using the HardFreeze strategy ensures shipping costs and all discounts are frozen, which guarantees that the Cart total doesn't change during checkout.
Freeze the Cartbash
curl -X POST "https://api.europe-west1.gcp.commercetools.com/{{project-key}}/carts/{{cart-id}}" \
-H "Authorization: Bearer {{auth-token}}" \
-H "Content-Type: application/json" \
-d '{
    "version": {{cart-version}},
    "actions": [
      {
        "action": "freezeCart",
        "strategy": "HardFreeze"
      }
    ]
}'
You can verify that the Cart is frozen by checking the response. The cartState field should be set to Frozen. At this point, stock is reserved for your specified Line Items and all prices, discounts, and shipping costs are frozen.

Create the Order

Before the reservations expire, you must create an Order from the Cart. When you do this, the reserved stock is permanently deducted from inventory. If you don't create an Order before the reservation expiration time, the stock will be released and become available for other Carts to reserve.

Best practices

To ensure a smooth checkout process, you should:

  • Handle unsuccessful reservations before ordering: if any Line Item's inventoryMode was not updated to ReserveOnCart, the inventory entry's stock will not be deducted when you create the Order.
  • Set appropriate expiration times: to prevent Denial of Stock attacks, all reservations have a mandatory expiration time between 1 and 44640 minutes (31 days). Configure a reservationExpirationInMinutes value that gives customers enough time to complete checkout without holding stock unnecessarily long. When a Line Item reservation expires, the stock becomes available again for other Carts to reserve.
  • Extend expiration times proactively: if a customer's checkout takes longer than expected, extend the expiration of every Line Item reservation in the Cart before they expire. This prevents the Cart from becoming unorderable.
  • Check reservation status: before ordering a Cart, check for reservations that have expired or are about to expire. Use reference expansion on the reservation field of a Line Item to check the reservation validity.
  • Handle re-reservation failures: the Cart will automatically attempt to re-reserve stock when creating an Order. If the stock is no longer available, then creating the Order will fail with an OutOfStock error. Use the same logic to handle reservation failures that can occur when renewing expired Line Item reservations.
  • Account for eventual consistency: changes to the availableQuantity and quantityOnStock fields resulting from reservation operations are eventually consistent and may take up to 10 seconds to be reflected. See Inventory checks and consistency for details on how this affects your implementation.

Summary

You have learned how to:

  • Enable reservation functionality in your Project.
  • Reserve stock on demand, at any stage during your checkout process.
  • Check for warnings indicating unsuccessful reservations.
  • Handle failures when reserving stock.
  • Freeze prices, discounts, and shipping costs to ensure a consistent checkout experience.

This approach gives you flexibility in when to commit inventory, allowing you to balance customer experience with inventory management needs. When implementing this in a real project, consider your specific checkout flow and adjust the timing of reservations accordingly.

Further reading

To learn more about implementing reservations, see the following resources:

  • Reserve stock on Cart: prevent overselling and improve the checkout experience by automatically reserving items in a Cart.
  • Inventory overview: information about managing inventory, reservations, and stock levels.