Orders put on hold with "Insufficient inventory" despite available stock in the store
Summary
An order is put on hold during routing with the error "Insufficient inventory", even though the fulfillment location has enough stock for all items. Manually rerouting the order to the same location via the Handle Exception flow in NewStore Omnichannel Manager succeeds. This behavior is usually not an inventory problem. It is caused by the store capacity configuration, specifically a capacity policy that defines fulfillment hours for a location without an accompanying limit value.
Symptoms
- Orders containing items that are in stock at a fulfillment location are put on hold with the routing error "Insufficient inventory".
- Manually rerouting the order to that location from NewStore Omnichannel Manager using the Handle Exception flow works without issues.
- The remaining capacity for the location, as returned by the store capacity API (
GET /v0/store_capacity/remaining), is0at the time of routing, even though no item limit was configured.
Cause
The capacity based routing feature uses two independent controls per fulfillment location:
limitcaps how many items can be routed to the location per day.hoursdefines the time windows, per weekday and in UTC, during which the location is eligible for routing at all.
These are separate mechanisms, not one combined setting. When a location has hours configured but no limit, the routing engine resolves the capacity as follows:
- Inside the configured time window, no limit is set, so capacity is treated as unlimited.
- Outside the configured time window, the remaining capacity of the location is forced to 0.
A remaining capacity of 0 removes the location from the list of routing candidates before inventory availability is even evaluated. As a result, if no other location can fulfill the order, the order is put on hold with the error "Insufficient inventory", even though the stock itself is available.
Manual rerouting via the Handle Exception flow bypasses this eligibility check, which is why it succeeds and can make the issue look like a defect in automatic routing.
Example
Consider the following anonymized store capacity policy for a fulfillment location:
{
"store_001": {
"hours": {
"monday": [10, 15],
"tuesday": [10, 15],
"wednesday": [10, 15],
"thursday": [10, 15],
"friday": [10, 15],
"saturday": [10, 15],
"sunday": [12, 15]
}
}
}
An order placed on a Tuesday at 16:30 UTC falls outside the configured window of 10:00 to 15:00 UTC. The routing engine therefore calculates a remaining capacity of 0 for store_001:
get_remaining_store_capacities: Remaining capacities: 'store_001': 0
The location is excluded from routing, and if it was the only viable candidate, the order goes on hold with "Insufficient inventory".
Important: hours are evaluated in UTC
The time windows in the hours configuration are always interpreted in UTC, not in the local time zone of the store. If the intent is to restrict fulfillment to the store's local business hours, the values must be converted to UTC before they are submitted. Otherwise routing is cut off earlier or later than expected.
For stores in time zones where the local business day spans two UTC calendar days, the window must be split accordingly. For example, 8 AM to 8 PM in a UTC+9 time zone corresponds to [23, 11] in UTC.
Diagnosis
- Retrieve the current store capacity configuration with
GET /v0/store_capacityand check whether the affected location has anhoursblock, and whether alimitis present. - Retrieve the remaining capacity for the location with
GET /v0/store_capacity/remaining. This endpoint accepts an ISO 8601 timestamp, so you can check the remaining capacity for the exact time the affected order was routed. - Compare the routing timestamp of the order, in UTC, with the configured
hourswindow for that weekday. If the timestamp falls outside the window, the remaining capacity of 0 explains the hold.
Resolution
Choose the option that matches your intent:
- If no capacity restriction is intended for the location, remove its entry from the store capacity policy entirely.
- If the hours restriction is intentional, verify that the values were entered in UTC rather than local store time, and correct or widen the window so it reflects the hours during which the store should be eligible for fulfillment.
- If you want a daily cap rather than a full block outside business hours, add an explicit
limitvalue alongsidehours. This changes the behavior from "inside hours = unlimited, outside hours = zero" to "inside hours = capped at N items, outside hours = zero", which is usually closer to what an hours-only configuration was intended to achieve.
Configuration changes are made via the store capacity API, using POST /v0/store_capacity to replace the policy or PATCH /v0/store_capacity (JSON Patch, with the ETag returned by the GET endpoint) to modify individual entries.
Additional notes
- A wildcard
*entry in the policy applies to all locations; longer, more specific patterns take precedence over shorter ones. - Daily quotas reset at the configured start time of the window, or at 00:00 UTC by default. Orders that are still pending after the reset do not consume capacity again.
- Store capacity limits are not applied when prerouting is used, for example for BOPIS orders.
- Under heavy parallel routing, limits can be slightly exceeded because simultaneous routing requests may observe the same point-in-time capacity.
- Capacity based routing does not override stock checks. Even a location with unlimited capacity is skipped if it does not have sufficient inventory.
Related documentation
- Managing capacity based routing for stores: https://docs.newstore.com/docs/managing-capacity-based-routing-for-stores
- Store capacity configuration API reference: https://docs.newstore.net/api/configuration/order-management/logistic_order-store_capacity_config/