How it works

Follow these 4 simple steps to start automatically updating Shopify inventory from CSV files on your FTP server

ftp icon

Fetch CSV File

This trigger connects to your FTP server on schedule to retrieve inventory data files, enabling automated sync between your warehouse management system or ERP and Shopify without manual CSV uploads.
This scheduled trigger connects to your FTP server hourly and fetches a CSV file that you specify. You need to configure several settings during setup: (1) FTP credentials—configure your FTP server connection details in MESA's FTP integration settings, (2) File path—enter the path to your CSV file (you can use wildcards like "inventory/Stock.csv" to match multiple files), (3) CSV format requirements—your CSV must include columns named "SKU" and "Inventory" (case-sensitive), and (4) Schedule—adjust the frequency if hourly is too often or not frequent enough for your needs. The CSV data is parsed into an array where each row becomes an object accessible as {{ftp}} with properties like {{ftp.SKU}} and {{ftp.Inventory}}.
Time to complete: 8 minutes

Loop

Processes each product in the CSV individually, ensuring every SKU gets its inventory updated even if the file contains hundreds or thousands of products.
This loop step iterates through each row in the CSV file stored in {{ftp}}. For every row, the loop executes the subsequent SKU lookup and inventory update steps using {{loop.SKU}} and {{loop.Inventory}} as the data for that specific product. The loop uses custom code to enqueue each iteration as a separate task, enabling efficient processing of large CSV files without timeout issues. Loop processing code: const Mesa = require("vendor/Mesa.js"); const Loop = require('vendor/Loop.js'); module.exports = new class { script = (payload, context) => { // Loop over each item and enqueue child tasks for each matching item Loop.process(payload, context, (match, key) => { match.key = match.key ? match.key : key; Mesa.output.nextOutput(match, {enqueue: true}); }); } }
Time to complete: Auto-configured (0 minutes)

Custom Code (SKU Lookup)

Translates the human-readable SKU from your CSV into Shopify's inventory_item_id, which is the technical identifier required to update inventory levels via Shopify's API.
This custom code step uses Shopify's GraphQL API to search for a product variant matching {{loop.SKU}} from the current CSV row. The GraphQL query searches for "sku:[YOUR_SKU]" and returns the variant's inventory item ID. The code extracts the numeric ID from Shopify's GraphQL ID format (which looks like "gid://shopify/InventoryItem/12345") and stores it as {{custom.inventory_item_id}}. If no matching SKU is found, the code logs a message and stops processing that row without attempting an inventory update, preventing errors for SKUs that don't exist in your Shopify store. SKU lookup code: const Mesa = require('vendor/Mesa.js'); const ShopifyGraphql = require('vendor/ShopifyGraphql.js'); module.exports = new class { script = (payload, context) => { const vars = context.steps; let inventoryItemIdGid = 0; const productSku = vars.loop.SKU; Mesa.log.info("Product SKU", productSku); // GraphQL query to find variant by SKU let query = ` query($query: String!) { productVariants(first: 3, query: $query) { edges { node { displayName id sku inventoryItem { id } product { id title } } } } } `; const response = ShopifyGraphql.send(query, { "query": "sku:" + productSku, }); Mesa.log.info("Response", response); // Extract inventory item ID from response if ( response && response.data && response.data.productVariants && response.data.productVariants.edges[0] && response.data.productVariants.edges[0].node && response.data.productVariants.edges[0].node.inventoryItem && response.data.productVariants.edges[0].node.inventoryItem.id ) { inventoryItemIdGid = response.data.productVariants.edges[0].node.inventoryItem.id; vars.loop.inventory_item_id = inventoryItemIdGid.match(/\d+/)[0]; Mesa.log.info("Inventory Item ID", vars.loop.inventory_item_id); Mesa.output.next(vars.loop); } else { Mesa.log.info('No Inventory Item ID found. SKU does not exist.'); } } }
Time to complete: Auto-configured (0 minutes)
Shopify logo icon

Set Inventory Level

Updates the product variant's inventory in Shopify to match the quantity from your CSV file, completing the sync between your external system and your store's inventory records.
This step sets the inventory level for the product variant using {{custom.inventory_item_id}} (from the SKU lookup), {{loop.Inventory}} (the quantity from the CSV), and a location_id that you configure during setup. You need to find your Shopify location ID by going to Settings > Locations in Shopify admin, clicking on the location where you track inventory, and looking at the URL which will show a number like "12345678901"—that's your location ID. The inventory is set (not adjusted), meaning the quantity in Shopify will exactly match the CSV value regardless of what it was before. This ensures your CSV is always the source of truth.
Time to complete: 3 minutes

Make it your own

Customize this workflow even further:

Send completion reports
Add steps after the loop ends to count successful updates, list any SKUs that weren't found, and send a summary email or Slack message showing how many products were updated and any errors encountered.
Archive processed files
Enable the "move_file" option in the FTP trigger settings to automatically move CSV files to an "archive" or "processed" folder after they're imported, preventing duplicate processing and maintaining a history of imports.
Handle inventory adjustments instead of sets
Modify the workflow to retrieve current inventory levels first, calculate the difference between CSV and current quantity, and use "adjust" instead of "set" to create an audit trail of inventory changes rather than absolute updates.
Support multiple locations
Add a "Location" column to your CSV and modify the custom code to map location names to IDs, enabling a single CSV to update inventory across multiple warehouses or retail locations.

Frequently asked questions

What happens if a SKU in the CSV doesn't exist in Shopify?
The custom code step logs that the SKU wasn't found and stops processing that row without updating anything. The workflow continues to the next CSV row. Check your MESA logs to see which SKUs failed to match if you suspect missing products.
Can I use this to update inventory at multiple locations?
This workflow updates inventory at a single location specified in the "Set Inventory Level" step. To update multiple locations, either run separate workflows configured for each location, or add a "Location" column to your CSV and modify the logic to use that value instead of a hardcoded location ID.
Does this workflow adjust inventory or set it to an absolute value?
The workflow uses Shopify's "set" endpoint, which sets inventory to the exact quantity from the CSV regardless of the current value. If your CSV says "10" and Shopify currently shows "15," it will change to "10." This ensures your CSV is always the source of truth but doesn't create an adjustment audit trail.
What is a template?
Templates are pre-made workflows by our team of experts. Instead of building a workflow from scratch, these have all the steps needed to complete the task.
Can I personalize a template?
Yes! Every step can be customized to meet your exact requirements. Additionally, you can even add more steps and make it more sophisticated.
Are templates free?
Yes! Our entire library containing hundreds of templates are free to use and customize to your exact needs.

Ready to start automatically updating Shopify inventory from CSV files on your FTP server?

Join thousands who've automated their work and saved an average of 3.5 hours every week.

Use this template — It's free
7-day free trial • 13 min setup • Cancel anytime