How to Sync Unleashed Stock Levels to Shopify
Keep Shopify inventory in line with Unleashed every 30 minutes: read available quantities from the Unleashed API, compare them with Shopify by SKU, and adjust only the products whose stock changed.
What This Integration Does
When Unleashed is your inventory system and Shopify is where you sell, stock has to flow from one to the other, or you sell items you do not have. This Spojit workflow reads available quantities from Unleashed's StockOnHand endpoint, matches each product code to a Shopify variant SKU, works out the difference between Unleashed's available quantity and Shopify's, and applies that difference with Shopify's inventory adjustment. Products whose stock is already correct are left alone, so a run with no changes makes no writes.
Unleashed is the source of truth: each run sets Shopify to Unleashed's numbers, so it is safe to re-run and it corrects any drift caused by manual edits in Shopify. Because the adjustment is calculated from Shopify's current level at run time, orders placed between runs are accounted for, as long as Unleashed also receives those orders (through your existing order integration).
Prerequisites
- The signed Unleashed request from the "How to Connect to the Unleashed API" tutorial in this help center (API ID, API key and the Encoding Tools
hmacstep). - A Shopify connection with
read_products,read_inventoryandwrite_inventory. - Matching codes: each Unleashed
ProductCodemust equal the Shopify variant SKU. - The Shopify location ID that represents your Unleashed warehouse (find it with
get-shopor theraw-graphqltool).
Step 1: Schedule Trigger
Add a Trigger node with type Schedule and cron */30 * * * *. Every 30 minutes is a good balance between freshness and API usage for most catalogues.
Step 2: Read Unleashed Stock
Sign warehouseCode=MAIN&pageSize=500 with the Encoding Tools hmac tool (sha256, base64), then call https://api.unleashedsoftware.com/StockOnHand/{{ page }}?warehouseCode=MAIN&pageSize=500 with HTTP Requests http-get and the Unleashed headers, looping until PageNumber equals NumberOfPages. Replace MAIN with your warehouse code, or drop the filter (and sign pageSize=500) to use the total across warehouses. Collect ProductCode and AvailableQty from each item into unleashed.
Step 3: Read Shopify Variants and Levels
Add a Shopify step with raw-graphql that pages through product variants and returns, for each one, its sku, its inventoryItem.id and the available quantity at your location. Fetching it all in one query per page is much cheaper than calling get-inventory-levels per product. Collect the results into shopify.
Step 4: Calculate the Changes
Add a Code Runner step with execute-javascript. Set its input to { "unleashed": {{ unleashed }}, "shopify": {{ shopify }} }:
const target = new Map(data.unleashed.map(r => [r.ProductCode, Math.max(0, Math.floor(r.AvailableQty))]));
const changes = [];
for (const v of data.shopify) {
if (!target.has(v.sku)) continue; // not stocked in Unleashed
const delta = target.get(v.sku) - v.available;
if (delta !== 0) changes.push({ inventoryItemId: v.inventoryItemId, sku: v.sku, delta });
}
return changes;
Name the output changes; the list is in {{ changes.result }}. AvailableQty in Unleashed is on hand minus allocated, which is what you can actually sell.
Step 5: Apply the Adjustments
Add a Loop in ForEach mode over {{ changes.result }} with On iteration error set to Continue with the next item. Inside it, a Shopify step with adjust-inventory:
inventoryItemId:{{ item.inventoryItemId }}locationId: your Shopify location IDdelta:{{ item.delta }}reason:correction
Step 6: Summarise the Run
After the loop, post a one-line summary to Slack with send-message ("Unleashed sync: 14 SKUs adjusted") only when changes were made, and a daily list of Shopify SKUs with no matching Unleashed product code. That second list catches new products that were never set up in Unleashed.
Tips
- Safety buffer: to avoid overselling fast movers, subtract a small buffer (for example 2 units) from
AvailableQtyin the Code Runner before comparing. - Only changed products: sign and send
modifiedSince=...toStockOnHandfor very large catalogues, and run a full comparison once a night to catch anything missed. - Several warehouses: run Steps 2 to 5 once per warehouse code, each mapped to its own Shopify location.
- Bundles and kits: Unleashed assemblies may not have stock of their own. Calculate their availability from component stock in the Code Runner, or exclude them.
Common Pitfalls
- SKU mismatches: trailing spaces or different capitalisation stop codes matching. Trim and upper-case both sides in the Code Runner if your data is inconsistent.
- Untracked Shopify variants: variants with inventory tracking turned off ignore adjustments. Turn tracking on for every synced variant.
- Two writers: if another app also changes Shopify stock, the two fight. Make this workflow the only stock writer for the synced location.
- Signature mismatch: the signed text must equal the query string exactly, including the parameter order. Change both together.
Testing
Pick three SKUs, set different quantities for them in Unleashed, and run the workflow manually with the Unleashed request filtered to those products. Check the changes output and the products' inventory in Shopify admin. Once correct, remove the filter and turn the schedule on.