Bug: Property "available" is merged in webhook: inventory_levels/update

I am using a webhook: inventory_levels/update to watch the restock status of a product variant by location. But there is a problem:

If there are 2 events that came from the same inventory_item_idat the same time
Property available is merged (using the value from the most recent event)

Example: I update the available stock of my product variant in Shopify Admin from 0 → 10, then update from 10 → 0 immediately
2 events my app received look like this:
{“inventory_item_id”:48513488879793,“location_id”:75596366001,“available”:0,“updated_at”:“2026-03-16T15:35:06+07:00”,“admin_graphql_api_id”:"
gid://shopify/InventoryLevel/110012006577?inventory_item_id=48513488879793"}
{“inventory_item_id”:48513488879793,“location_id”:75596366001,“available”:0,“updated_at”:“2026-03-16T15:35:10+07:00”,“admin_graphql_api_id”:"
gid://shopify/InventoryLevel/110012006577?inventory_item_id=48513488879793"}

I expect to receive available: 10 in the first event

Shopify webhooks are designed to deliver the current state of the resource at the time the payload is assembled, not a snapshot of the state at the exact moment the change occurred. When two inventory changes happen in rapid succession, there’s a small window where both webhook payloads get built after the second change has already been applied — so both end up reflecting the final available value of 0.

The recommended approach is to treat webhooks as notifications that something changed rather than as a reliable event log of every intermediate state. When your app receives an inventory_levels/update webhook, use it as a trigger to fetch the current inventory level via the API (e.g., GET /admin/api/2024-01/inventory_levels.json) to confirm the actual value. If you need a full audit trail of every inventory adjustment, consider polling the Inventory Adjustments API or using the InventoryAdjustmentGroup resource via GraphQL, which will give you the complete history of changes including intermediate states.

You can also deduplicate incoming webhooks by keying on inventory_item_id + location_id with a short debounce window (e.g., 2–3 seconds), processing only the last received event and then verifying via the API. This avoids redundant processing and ensures your app always works with the true current state.

1 Like

Just +1ing @HookdeckGareth here - webhooks are great as triggers, but we do also reccommend using polling patterns as backup as needed since we can’t always guarantee webhook delivery:

Wanted to share the link above in case it helps as well.

1 Like