Mechanic is a development and ecommerce automation platform for Shopify. :)
Use this task to automatically maintain minimum inventory levels. Optionally, filter by variant or product ID, or by variant option, or by product type, choosing whether to blacklist or whitelist your filter selections.
Runs Occurs whenever an inventory level is updated. Configuration includes minimum inventory level, location id, whitelist filter mode, blacklist filter mode, filter for these variant ids, filter for these variant options, filter for these product types, and filter for these product ids.
Use this task to automatically maintain minimum inventory levels. Optionally, filter by variant or product ID, or by variant option, or by product type, choosing whether to blacklist or whitelist your filter selections.
This task runs as inventory levels are updated, auto-updating qualifying levels to meet the configured minimum.
This task can be configured with several resource IDs. Learn how to find these.
If a location ID is provided, this task will only adjust inventory for items at that location.
Additional filters may be configured, using either whitelist mode to only perform inventory adjustments for items that match the filter, or blacklist mode to perform adjustments for all items that do not match the filter. All filters are optional, and each filter will be ignored if it is left unconfigured.
Mechanic is designed to benefit everybody: merchants, customers, developers, agencies, Shopifolks, everybody.
That’s why we make it easy to configure automation without code, why we make it easy to tweak the underlying code once tasks are installed, and why we publish it all here for everyone to learn from.
(By the way, have you seen our documentation? Have you joined the Slack community?)
shopify/inventory_levels/update
{% assign minimum_inventory_level = options.minimum_inventory_level__number_required %}
{% assign location_id = options.location_id__number %}
{% assign whitelist_filter_mode = options.whitelist_filter_mode__boolean %}
{% assign blacklist_filter_mode = options.blacklist_filter_mode__boolean %}
{% assign filter_for_these_variant_ids = options.filter_for_these_variant_ids__array_number %}
{% assign filter_for_these_variant_options = options.filter_for_these_variant_options__keyval %}
{% assign filter_for_these_product_types = options.filter_for_these_product_types__array %}
{% assign filter_for_these_product_ids = options.filter_for_these_product_ids__array_number %}
{% if location_id != blank %}
{% assign location_id = location_id | append: "" %}
{% endif %}
{% comment %}
-- validate mode and filter combinations
{% endcomment %}
{% if whitelist_filter_mode and blacklist_filter_mode %}
{% error "Choose only one of 'Whitelist filter mode' or 'Blacklist filter mode'" %}
{% elsif whitelist_filter_mode or blacklist_filter_mode %}
{% if filter_for_these_variant_ids == blank
and filter_for_these_variant_options == blank
and filter_for_these_product_types == blank
and filter_for_these_product_ids == blank
%}
{% error "When using a filter mode, at least one filter must be configured." %}
{% endif %}
{% elsif filter_for_these_variant_ids != blank
or filter_for_these_variant_options != blank
or filter_for_these_product_types != blank
or filter_for_these_product_ids != blank
%}
{% error "When using filters, a filter mode must be selected." %}
{% endif %}
{% comment %}
-- get extended inventory level data, including item, variant, and product
-- as of 2026-01, the item variant should be queried using variants connection
{% endcomment %}
{% capture query %}
query {
inventoryLevel(id: {{ inventory_level.admin_graphql_api_id | json }}) {
location {
id
legacyResourceId
}
quantities(names: "available") {
quantity
}
item {
id
legacyResourceId
variants(first: 1) {
nodes {
legacyResourceId
selectedOptions {
name
value
}
product {
legacyResourceId
productType
}
}
}
}
}
}
{% endcapture %}
{% assign result = query | shopify %}
{% if event.preview %}
{% capture result_json %}
{
"data": {
"inventoryLevel": {
"location": {
"id": {{ location_id | default: "1234567890" | prepend: "gid://shopify/Location/" | json }},
"legacyResourceId": {{ location_id | default: "1234567890" | json }}
},
"quantities": [
{
"quantity": {{ minimum_inventory_level | minus: 1 }}
}
],
"item": {
"variants": {
"nodes": [
{
"legacyResourceId":
{% if whitelist_filter_mode %}
{{ filter_for_these_variant_ids.first | append: "" | json }}
{% else %}
{{ "now" | date: "%s" | json }}
{% endif %}
,
"selectedOptions": [
{% if whitelist_filter_mode %}
{
"name": {{ filter_for_these_variant_options | first | first | json }},
"value": {{ filter_for_these_variant_options | first | last | json }}
}
{% else %}
{
"name": {{ "now" | date: "%s" | sha256 | json }},
"value": {{ "now" | date: "%s" | sha256 | json }}
}
{% endif %}
],
"product": {
"productType":
{% if whitelist_filter_mode %}
{{ filter_for_these_product_types.first | json }}
{% else %}
{{ "now" | date: "%s" | sha256 | json }}
{% endif %}
,
"legacyResourceId":
{% if whitelist_filter_mode %}
{{ filter_for_these_product_ids.first | append: "" | json }}
{% else %}
{{ "now" | date: "%s" | json }}
{% endif %}
}
}
]
}
}
}
}
}
{% endcapture %}
{% assign result = result_json | parse_json %}
{% endif %}
{% assign inventory_level = result.data.inventoryLevel %}
{% assign available = inventory_level.quantities.first.quantity %}
{% assign variant = inventory_level.item.variants.nodes.first %}
{% assign variant_id = variant.legacyResourceId | times: 1 %}
{% assign product_id = variant.product.legacyResourceId | times: 1 %}
{% assign product_type = variant.product.productType %}
{% unless event.preview %}
{% log inventory_level: inventory_level %}
{% endunless %}
{% comment %}
-- only process inventory levels below the minimum, and at a specific location if configured
{% endcomment %}
{% if location_id != blank and inventory_level.location.legacyResourceId != location_id %}
{% log %}
"This inventory level change was not from the configured location ({{ location_id }}); skipping."
{% endlog %}
{% break %}
{% elsif available >= minimum_inventory_level %}
{% log %}
"Available inventory level at this location meets or exceeds the minimum of {{ minimum_inventory_level }}; skipping."
{% endlog %}
{% break %}
{% endif %}
{% comment %}
-- check filter conditions if any are configured
{% endcomment %}
{% assign inventory_level_filtered = nil %}
{% if filter_for_these_variant_ids contains variant_id %}
{% assign inventory_level_filtered = "variant_id:" | append: variant_id %}
{% endif %}
{% for filter_option_keyval in filter_for_these_variant_options %}
{% assign filter_option_name = filter_option_keyval[0] %}
{% assign filter_option_value = filter_option_keyval[1] %}
{% assign matching_filter_option = variant.selectedOptions | where: "name", filter_option_name | first %}
{% if matching_filter_option and matching_filter_option.value == filter_option_value %}
{% assign inventory_level_filtered = "variant_option:" | append: filter_option_name | append: "=" | append: filter_option_value %}
{% break %}
{% endif %}
{% endfor %}
{% if filter_for_these_product_ids contains product_id %}
{% assign inventory_level_filtered = "product_id:" | append: product_id %}
{% endif %}
{% if filter_for_these_product_types contains product_type %}
{% assign inventory_level_filtered = "product_type:" | append: product_type %}
{% endif %}
{% assign adjust_quantity = false %}
{% if whitelist_filter_mode == false and blacklist_filter_mode == false %}
{% log filter_mode: nil %}
{% assign adjust_quantity = true %}
{% elsif whitelist_filter_mode %}
{% log filter_mode: "whitelist", filter_value: inventory_level_filtered %}
{% if inventory_level_filtered != blank %}
{% assign adjust_quantity = true %}
{% endif %}
{% elsif blacklist_filter_mode %}
{% log filter_mode: "blacklist", filter_value: inventory_level_filtered %}
{% if inventory_level_filtered == blank %}
{% assign adjust_quantity = true %}
{% endif %}
{% endif %}
{% unless adjust_quantity %}
{% break %}
{% endunless %}
{% comment %}
-- inventory level qualified to be adjusted
-- changeFromQuantity field is required as of 2026-04
https://shopify.dev/changelog/making-changefromquantity-field-required
-- Shopify will enforce idempotency for several mutations as of 2026-04
https://shopify.dev/changelog/making-idempotency-mandatory-for-inventory-adjustments-and-refund-mutations
{% endcomment %}
{% assign inventory_adjustment = hash %}
{% assign inventory_adjustment["inventoryItemId"] = inventory_level.item.id %}
{% assign inventory_adjustment["locationId"] = inventory_level.location.id %}
{% assign inventory_adjustment["delta"] = minimum_inventory_level | minus: available %}
{% assign inventory_adjustment["changeFromQuantity"] = available %}
{% capture idempotent_key %}
{
"task_id": {{ task.id | json }},
"event_id": {{ event.id | json }},
"data": {{ inventory_adjustment | json }}
}
{% endcapture %}
{% action "shopify" %}
mutation {
inventoryAdjustQuantities(
input: {
reason: "correction"
name: "available"
changes: {{ array | push: inventory_adjustment | graphql_arguments }}
}
) @idempotent(key: {{ idempotent_key | json | md5 | json }}) {
inventoryAdjustmentGroup {
reason
changes(quantityNames: "available") {
name
delta
item {
id
sku
}
location {
name
}
}
}
userErrors {
code
field
message
}
}
}
{% endaction %}
0