Inventory & suppliers
Sync inventory between Shopify and a second sales channel
When you sell the same SKUs on Shopify and a second channel — Amazon, eBay, TikTok Shop, a wholesale store, a second Shopify — the stock counts drift apart the moment either one makes a sale. Here's how to keep both numbers honest from a single source of truth, decrement on every sale wherever it happens, and hold a safety buffer so the gap between 'sold' and 'synced' never turns into an oversell.
- Shopify
- n8n
The manual way today
Someone keeps a spreadsheet, or just a mental note, of what's on hand. A sale comes in on the second channel, so they open Shopify and knock the count down by hand — when they remember. A sale comes in on Shopify, so they go adjust the second channel's listing. Most of the time they don't, because there's no trigger telling them to, so they batch it: once a day, or once a week, someone reconciles the two by eye and patches whatever's obviously wrong.
The cost shows up as oversells. You have one of something left, it sells on both channels in the same hour, and now you owe two customers one unit. The fix is never just a refund — it's the apology, the 'sorry, that's actually out of stock' email, the hit to your seller rating on the marketplace, and on Amazon specifically a cancellation that counts against your account health. Run the other way and it's just as expensive: to feel safe, people pad every listing low and quietly leave sellable stock dark on one channel or the other. Either you oversell, or you under-sell to avoid overselling. The manual reconcile is the tax you pay to do neither well.
What running looks like
One channel is the source of truth for what's on hand. A sale anywhere decrements that master, and the master — minus a small safety buffer — gets pushed to every other channel within a minute, untouched. Both storefronts show the same sellable number, the buffer absorbs the seconds between a sale and the sync, and a scheduled reconcile catches anything a dropped webhook missed. SKUs that don't map cleanly get flagged to a human instead of silently going out of sync.
01The build
How to build it
Pick one source of truth, map the SKUs, and set a buffer
Needs a humanThis is the decision that makes the whole thing safe, so make it before you wire anything. Choose one channel — almost always Shopify — as the authoritative count for each SKU; everything else is a mirror of it, never a co-owner. Two-way 'both can edit' sync is where oversells and infinite loops are born. Then build the join: a SKU-to-listing map (Google Sheet or Airtable is fine) pairing each Shopify SKU with the second channel's listing or offer ID — match on SKU, never on product title or internal product ID, which don't line up across platforms. Last, pick a safety buffer per SKU (a flat 5, or a percentage for fast movers): the master is the truth, but every mirror publishes master-minus-buffer so the lag between a sale and its sync can't sell a unit you don't have.
Catch Shopify's inventory webhook and resolve it to a SKU
Runs itselfSubscribe to Shopify's inventory_levels/update webhook and point it at an n8n webhook node. This one topic fires for everything that moves the master — a Shopify sale draining stock, a manual count correction, a restock, a return going back on the shelf — so it's the single trigger for keeping the mirrors current. The payload is lean: inventory_item_id, location_id, and the new available quantity, with no SKU. Resolve it with Shopify's GraphQL inventoryItem query (inventoryItem → variant → sku) and sum available across only the locations that fulfill that channel's orders, so a damages or staff bin never inflates what you publish.
Push master-minus-buffer to the second channel
Runs itselfLook the SKU up in your map from step one to get the second channel's listing/offer ID. Compute the publish number — available across fulfilling locations, minus the SKU's buffer, floored at 0 — and write it to the second channel's inventory endpoint (Amazon SP-API's listings/inventory feed, eBay's Inventory API, TikTok Shop's stock update, or inventorySetQuantities on a second Shopify store). Set absolute quantities, not deltas, so the mirror is always told exactly what to show rather than nudged and left to drift. If the SKU isn't in the map, don't guess a listing — route it to step seven.
Catch a sale on the second channel and decrement the master
Runs itselfSubscribe to the second channel's order/sale event (a webhook where it offers one, a poll of its orders endpoint every few minutes where it doesn't). On each new order, take the SKU and quantity sold and apply it to the master with Shopify's inventoryAdjustQuantities mutation — a relative -N adjustment at the fulfilling location, keyed to the channel's order ID so the same order can never be subtracted twice. That single decrement is what closes the loop: the master drops, which fires the inventory_levels/update from step two, which republishes the new, lower number back out to every mirror. Sales flow in from anywhere; the master stays the one true count.
Suppress the echo so the channels don't ping-pong
Runs itselfHere's the trap that breaks naive builds. When step three writes a quantity to the second channel, that channel emits its own 'inventory changed' event — and if you're listening to it, you'll write that value back to Shopify, which fires Shopify's webhook, which writes back to the channel, forever. Two defenses: first, before any write, read the destination's current value and skip the call entirely if it already matches what you're about to set, which kills the round-trip at the source. Second, stamp each outbound sync (a short-lived key in a cache or sheet: SKU + value + timestamp) and ignore an inbound event that matches a write you just made. The buffer math helps too — Shopify holds the real count, the mirror holds master-minus-buffer, so an unbuffered echo would be a visible no-op anyway.
Handle rate limits and retries, because a dropped write oversells
Runs itselfOn inventory sync, a silently failed API call is the same as a wrong count — the mirror keeps showing stock that's gone. Respect each platform's limits: Shopify's GraphQL cost budget (back off and retry on a THROTTLED response) and the marketplace's quota (Amazon's feed throttling, eBay's call limits). Queue writes per SKU rather than firing them in parallel, collapse a burst of changes to one SKU into its latest value before sending, and wrap every write in a bounded retry with backoff. If a write still fails after its retries, don't let it vanish — push the SKU to the same exception lane as step seven so a human or the next reconcile catches it.
Reconcile on a schedule and flag what doesn't match
Runs itselfWebhooks get dropped and polls miss windows, so don't trust the live stream alone. Once or twice a day, run an n8n schedule that pulls the full SKU-to-quantity list from both channels, computes what each mirror should show (master-minus-buffer), and corrects any SKU that's drifted. Where the rule is unambiguous, fix it automatically. Where it isn't — a SKU live on the second channel that has no match in Shopify, a count that's somehow negative, a listing whose buffer would push it below zero — post it to wherever you watch ops for a person to settle. The automation owns the rule; the reconcile is its safety net.
Review unmapped SKUs and new listings
Needs a humanNew products and renamed SKUs are the one place this needs a human. When the reconcile or a live event hits a SKU that isn't in the map, it shouldn't invent a pairing — it should drop it into a short review list with both channels' details. A person confirms the match (or that it's intentionally single-channel) and adds the row to the map. From then on it syncs itself. This keeps the automation from ever pushing stock to the wrong listing, which is the one mistake worse than not syncing at all.
02The stack
What it’s built on
- Shopify
- The source of truth for on-hand stock. It emits the inventory_levels/update webhook that drives every mirror, answers the inventory-item-to-SKU lookup, and is where second-channel sales get decremented via inventoryAdjustQuantities.
- n8n
- The glue between the channels: it catches Shopify's inventory webhook and the second channel's order events, resolves SKUs against your map, applies the buffer, makes the absolute-quantity writes, suppresses the echo loop, and runs the scheduled reconcile.n8n is open-source and self-hostable for free; the cloud tier just saves you running it yourself. This is the kind of build where it earns its place over Zapier — the SKU mapping, buffer math, echo suppression, and per-SKU retry queue need real branching and code, not single-step zaps.
03Questions
Before you build
Why one source of truth instead of true two-way sync?
Because two channels both editing the same count is how you get oversells and sync loops. With one master, every sale anywhere decrements that single number, and the others mirror it. You still sell on both channels and stock still flows both directions — what you give up is the ambiguity of two systems disagreeing about who's right, which is exactly the ambiguity that causes the overselling in the first place.
How does the safety buffer stop oversells?
There's always a short gap between a sale on one channel and the sync landing on the other. The buffer covers that gap: if your master shows 8 and your buffer is 5, each mirror publishes 3, so even if both channels sell at once before the sync catches up, you're subtracting from real stock you actually have. Set the buffer higher for fast movers and lower for slow ones — it's the dial between safety and leaving stock dark.
What stops the two channels from updating each other forever?
Writing to a channel makes it emit its own inventory event, which would bounce back without a guard. Step five handles it two ways: it reads the destination's current value and skips the write entirely if it already matches, and it stamps each outbound sync so a matching inbound echo is ignored. Together those collapse the round-trip before it can loop.
Will this work with Amazon, eBay, TikTok Shop, or a second Shopify store?
Yes — the Shopify half is identical and only the second channel's API changes. Shopify stays the master and emits the same webhook; you swap in that channel's inventory-write endpoint (Amazon SP-API, eBay Inventory API, TikTok Shop, or inventorySetQuantities on a second Shopify) and its order event. The SKU map, buffer, echo suppression, and reconcile are the same regardless of which channel you're mirroring to.
What happens if a webhook is dropped or an API call fails?
That's why this isn't only a live stream. Every failed write after its retries gets pushed to an exception lane, and the scheduled reconcile in step seven pulls both channels' full counts once or twice a day and corrects any SKU that's drifted. A single missed event self-heals at the next reconcile instead of quietly festering into an oversell.
Can I build this myself, or should you build it?
Every trigger, field, mutation, and guardrail is laid out above, so if you're comfortable with two APIs and have a free day, you can wire it up. The reason most brands hand this one off is that it's genuinely the hardest recipe in the inventory set — the echo suppression, per-SKU retry queue, and reconcile are where a careless build creates the oversells it was meant to prevent. If you'd rather not own that, we'll build it on the Shopify and n8n accounts you already pay for, document every piece, and hand you the keys — you own the workflow outright, no monthly retainer. It starts with a free 20-minute teardown, not a contract.
Related recipes
Inventory & suppliers
Send low-stock Slack alerts from Shopify inventory
A bestseller running down to zero with nobody watching is the most avoidable stockout there is. Here's how to watch Shopify's live inventory and drop a clear, actionable alert in Slack the moment a SKU crosses its low-stock line — once per dip, not on every sale.
- Shopify
- Slack
Inventory & suppliers
Auto-hide sold-out products and republish on restock
A product that's sold out shouldn't keep taking clicks and ad spend to a dead 'sold out' button. Here's how to detect when a product is genuinely out across every variant, hide it from your storefront, and bring it back on its own the moment it restocks — without the listing flickering on and off during a busy sale.
- Shopify
- n8n
Inventory & suppliers
Auto-generate purchase orders at the reorder point
Keep a reorder point and a supplier for every SKU in Airtable, read Shopify's live inventory on a schedule, and draft the purchase order automatically the moment stock drops to the line — then approve it before it sends. You reorder on lead time instead of finding out you're empty from a customer.
- Shopify
- Airtable
- n8n
Inventory & suppliers
Build a reorder-point dashboard in Airtable
Pull live Shopify inventory and trailing sales into Airtable on a schedule, let formula fields turn raw counts into days of cover and a health status per SKU, then read it all on one Interface sorted by urgency. The point isn't to act for you — it's to give you the single screen that tells you what to order next, before a customer finds the gap.
- Airtable
- Shopify
- n8n