There's a specific moment every growing D2C brand hits: the single warehouse that used to hold everything stops being enough. Maybe you opened a second facility for faster coast-to-coast delivery. Maybe you signed with a 3PL to get out of the packing-and-shipping business entirely. Maybe you opened a retail counter, or started selling through Amazon and Walmart alongside your own store. Whatever the trigger, the result is the same: inventory now lives in more than one place, and the tidy single number you used to trust — "47 units in stock" — has quietly stopped meaning anything. This is where Shopify's multi-location inventory system stops being a settings-page curiosity and starts being the thing standing between you and a stream of oversold orders.
The symptoms of outgrowing "one warehouse, ship everything"
You usually don't decide to solve this problem — you get forced into it. The pattern is familiar: someone on the ops team notices that Shopify says 12 units of a bestseller are available, but the warehouse floor has 3, or the 3PL's dashboard says something different from both. A flash sale sells through stock that was already technically gone, and now support is issuing refunds and apology discount codes. An order gets routed to a fulfillment center on the wrong coast because nobody set up priority correctly, so a customer in Seattle waits an extra four days for a package that shipped from New Jersey when a nearer location had the item in stock the whole time.
None of this is because anyone is bad at their job. It's because single-location thinking doesn't scale past a certain point, and Shopify — like any commerce platform handling real logistics — requires you to model your business the way it actually works, not the way it worked when you had one shelf and one person packing boxes. The fix isn't a single app or a single setting. It's understanding how Shopify actually represents locations, stock, and fulfillment priority under the hood, and then deciding deliberately how much of that machinery you run natively versus hand off to a 3PL or connect via API.
How Shopify actually models locations and stock
In Shopify, a "location" is a formal object with its own address, its own settings, and its own inventory counts — not just a label. A location can be a warehouse you operate yourself, a retail store with a POS terminal, or a location that represents a third-party fulfillment provider. Shopify supports up to a thousand locations on a single store, which sounds excessive until you remember that some multi-brand or franchise operations genuinely need that many.
Every product variant's inventory is tracked per location, not as one global number. A hoodie in size medium might show 40 units at your primary warehouse, 15 at a satellite facility, and 8 at a retail store — and the number a shopper sees on your storefront is the sum across whichever locations are eligible to fulfill online orders. Within each location, Shopify further distinguishes between stock that's physically on hand, stock that's already committed to unfulfilled orders, and stock that's available to sell right now. That distinction matters enormously: "on hand" and "available" are not the same number, and conflating them is one of the most common sources of phantom oversells when a merchant builds a custom dashboard or sync job without accounting for committed quantity.
You control which locations are even allowed to sell online, which can fulfill in-store pickup, and which are purely for internal stock-holding (a returns processing location, for instance, that should never appear as sellable inventory). Getting this configuration right is unglamorous, but it's the foundation everything else sits on — a lot of "our inventory sync is broken" tickets actually trace back to a location that was left toggled on for online sales when it shouldn't have been.
Fulfillment priority: who actually ships the order
Once you have more than one location holding sellable stock, Shopify needs a rule for deciding which location fulfills a given order. This is fulfillment priority, and it works as a ranked list you configure at the store level: when a new order comes in, Shopify walks down that priority list and assigns the order to the highest-ranked location that has enough stock to fulfill it. Newly added locations get dropped to the bottom of that list by default, which trips people up constantly — merchants add a shiny new 3PL location, wonder why orders keep routing to the old warehouse, and eventually realize nobody reordered the priority list.
You can set priority as a simple fixed order, or lean on logic that factors in which location is closest to the customer, which has the deepest available stock, or which is cheapest to ship from — though the more sophisticated the routing logic you want (true "ship from nearest location with real-time carrier rate comparison"), the more likely you are to need an app or custom logic layered on top of Shopify's native list, since the built-in priority system is deliberately simple rather than a full transportation-optimization engine. For a brand running two owned warehouses on opposite coasts, getting this list right is usually the single highest-leverage five minutes you can spend — it directly determines shipping cost and delivery speed on every order that ships.
It's also worth knowing that priority isn't purely automatic and untouchable. Staff can manually reassign which location fulfills a specific order from the admin, which matters when you know something the routing logic doesn't — a location is short-staffed that week, or a customer called in asking for their order to ship from a specific facility for a return-adjacent reason.
Split shipments: when no single location has it all
Fulfillment priority assumes one location can cover the whole order. That assumption breaks constantly in real multi-location operations, especially once a cart has more than one SKU. If a customer orders a jacket and a hat, and your top-priority warehouse has the jacket but not the hat while your second warehouse has the reverse, Shopify will split that order into two fulfillments from two different locations rather than fail the whole order. The customer gets two shipments and two tracking numbers instead of one.
Split shipments aren't a bug, and refusing them isn't usually the right instinct either — the alternative is telling a customer "sorry, we can't fulfill your order" over a single unavailable item, which is worse. But splits are expensive: two shipments cost more than one, and a customer receiving a half-order followed by a mystery second box a day later isn't a great experience if it happens often. The practical fix is upstream, not downstream — better stock distribution across locations (keeping your fastest-moving SKUs stocked in more than one place) and tighter fulfillment priority tuning reduce how often splits happen in the first place. Some brands set a policy of consolidating certain product bundles to a single "home" location specifically so they never split, accepting slightly worse average shipping speed on that bundle in exchange for a single clean shipment.
Running your own warehouse in Shopify vs. connecting a 3PL
There are two fundamentally different operating models here, and a lot of brands blend them without ever explicitly deciding to. The first is the warehouse-you-operate model: your team has admin access, adjusts stock counts manually or via barcode scanning, and Shopify's numbers are a direct reflection of what's on your own shelves. This is simple to reason about because there's no intermediary — if the count is wrong, it's wrong because someone on your team miscounted or a return wasn't logged, not because a sync job failed somewhere.
The second model is a 3PL connection, and it works completely differently even though it can look identical to a customer. Most 3PLs — ShipBob, Deliverr, Flexport, and similar providers — integrate with Shopify either through a public app or a private API connection, and they typically register themselves as one or more locations in your Shopify account. But critically, the 3PL's own warehouse management system is the source of truth for physical stock, not Shopify. Shopify is downstream: it receives inventory updates pushed from the 3PL's system, usually via webhook or scheduled API sync, and reflects what the 3PL reports rather than what a human typed into the Shopify admin.
That distinction — Shopify as source of truth versus Shopify as a mirror of someone else's system — changes how you debug problems. If a self-operated warehouse's count is wrong, you fix it in Shopify directly. If a 3PL-fed location's count is wrong, fixing it in Shopify does nothing, because the next sync will just overwrite your fix with whatever the 3PL's system still thinks is true. You have to fix it at the source, which means you need visibility into the 3PL's own inventory dashboard or API, not just Shopify's.
The hybrid reality: warehouse, 3PL, retail, and marketplaces at once
Very few growing brands stay in one clean model for long. The typical trajectory looks like: start with one owned warehouse, add a 3PL for a second region or for overflow capacity during peak season, open a retail location or two, and start selling through Amazon or Walmart Marketplace alongside the Shopify store. Each of those channels has its own relationship to your actual stock, and Shopify's native multi-location system was built primarily to handle the first three — owned warehouses, 3PL locations, and retail/POS — cleanly. Marketplaces are where it gets genuinely harder.
Amazon and Walmart don't read Shopify's location and inventory data natively. They have their own listings, their own inventory feeds, and their own timing expectations, so keeping a marketplace listing's stock count consistent with what's actually available across your Shopify locations requires either a dedicated app built for that purpose or a custom integration that reads Shopify's inventory levels and pushes updates out to each marketplace on a schedule or via webhook. The complexity compounds because marketplaces enforce their own oversell penalties — sell out of stock on Amazon and you're not just refunding a customer, you're risking account-level performance metrics that affect your visibility across the entire platform.
The honest framing for a hybrid setup is that you're not running one inventory system with several front doors — you're running several inventory-adjacent systems (Shopify, your 3PL's WMS, each marketplace's seller backend) that all need to agree with each other closely enough that nobody oversells. "Closely enough" is doing a lot of work in that sentence, and it's the subject of the next section.
Why "real-time" inventory sync is harder than it sounds
Every vendor pitch says "real-time inventory sync," and it's worth being honest about what that phrase actually means in practice: near-real-time, with a delay measured in seconds to low minutes under normal conditions, and potentially longer under load. Shopify pushes inventory changes out via webhooks — an inventory_levels/update webhook fires when a quantity changes — and any system consuming that webhook has to receive it, process it, and update its own state, all of which takes time and can fail. If your ERP or 3PL's listener is slow, rate-limited, or briefly down, updates queue up or get missed, and the two systems drift out of sync without either side immediately knowing it.
The genuinely hard problem isn't average-case lag, though — it's what happens during a spike. Picture a flash sale or a limited restock: hundreds of customers hit "add to cart" within the same few seconds for a SKU that has, say, 30 units available. Shopify's own checkout has logic to reserve stock as a customer moves through checkout so two people can't both "win" the last unit, but the moment you introduce an external system — a 3PL updating counts, an ERP recalculating safety stock, a marketplace feed pushing its own reservation — you've created multiple systems that can each believe they have authority over the true available count at the same instant. That's a classic race condition, and it's the actual mechanism behind most "we sold 45 units of something we only had 30 of" stories. It's rarely one dramatic failure; it's several systems each making a locally correct decision a few seconds apart, based on data that was accurate when they read it and stale by the time they acted on it.
This is also why "just sync more often" isn't a complete answer. Polling every ten seconds instead of every minute reduces the window for drift but doesn't eliminate it, and aggressive polling runs into Shopify's API rate limits, especially on stores with large catalogs across many locations. The systems that handle this well combine faster sync with deliberate buffer strategy rather than trying to sync their way to zero latency, because zero latency across independent systems isn't actually achievable.
Practical safety-stock and buffer strategies that actually prevent oversells
Given that perfect real-time sync isn't realistic, the practical answer is to design for the lag rather than pretend it doesn't exist. The simplest and most effective lever is a safety buffer: instead of showing 30 units as available when you have 30 physically in stock, you deliberately show 25 or 27, holding back a small reserve that absorbs the gap between "what the system knows" and "what's physically true" during high-velocity moments. This is especially important for any SKU feeding more than one sales channel — if the same 30 units are sellable on both your Shopify store and an Amazon listing, the buffer needs to account for the fact that two channels are drawing from one pool with sync delay between them.
The buffer doesn't need to be a flat number across your whole catalog. It makes more sense sized per SKU based on sell-through velocity and channel count — a slow-moving SKU sold only on your own site barely needs a buffer, while a hero product launching simultaneously on Shopify, Amazon, and a retail counter needs a meaningfully larger one, at least during the launch window. Some brands tighten the buffer down to near zero during quiet periods and widen it deliberately before a known high-traffic event — a product drop, a sale, a restock announcement — treating buffer size as a dial they turn rather than a fixed setting.
Beyond raw buffer stock, a few other mechanisms help. Shorter checkout reservation windows reduce how long a unit sits "committed" to an abandoned cart before it becomes available again — useful during high-traffic events where cart abandonment spikes but you can't afford stock sitting in limbo. Waitlists or queue mechanisms for genuinely limited drops take pressure off the checkout race condition entirely by controlling the order in which people are even allowed to attempt a purchase. And for anything synced from an external ERP or 3PL, building in a sanity check — refusing to accept an inventory update that looks physically implausible, like a sudden jump from 5 units to 500 — catches a category of sync bugs before they cause a false "back in stock" announcement.
When an off-the-shelf app is enough, and when you need custom integration work
Not every multi-location setup needs custom engineering, and it's worth being honest about that rather than defaulting to "build something custom" as the answer to every inventory question. If you're running two or three owned warehouses plus a single well-supported 3PL, and your order volume is moderate, Shopify's native location and fulfillment priority tools combined with a well-reviewed inventory-sync app usually cover the ground fine. The apps built specifically for this — multi-location stock visibility, 3PL connectors, ERP bridges — exist because this is a common enough problem that someone already solved the general case well.
The signals that push a brand toward custom integration work are fairly consistent. High order volume during concentrated windows — flash sales, drops, festival-season spikes — where even small sync delays translate into real oversell risk at scale is one. Selling across three or more channels that each have their own inventory logic (your own store, a 3PL, one or more marketplaces, retail) is another, because off-the-shelf apps are usually built to connect two systems well, not to arbitrate priority and buffer logic across five simultaneously. A genuinely custom business rule — routing certain SKUs to specific locations based on logic no general-purpose app anticipated, or a safety-stock formula tied to your own demand forecasting — is a strong signal, since apps are built for the common case and your edge case either gets ignored or requires workarounds that become their own maintenance burden. And if you're already running an ERP that needs to be the actual system of record, with Shopify and every sales channel treated as satellites reporting up to it, that's almost always a custom API and webhook build rather than something an app handles out of the box.
The honest failure mode to watch for is reaching for custom development before you've actually tried the simpler option, or conversely, trying to force a general-purpose app to handle five-way channel arbitration it was never designed for and then blaming the app when it can't. Carryup does both kinds of work — this is the territory our Shopify Deep Engineering service covers when a brand needs real API and webhook-level integration, and our Care & Support Retainer covers the ongoing tuning once something is live — but the right starting question is always "what's the simplest thing that actually solves this," not "what's the most impressive thing we could build."
What a real multi-location and 3PL integration project actually involves
When this does need custom work, it's useful to know roughly what the project actually looks like, because "inventory sync" undersells how much groundwork is involved before any code gets written. It starts with an audit: mapping every location, every channel, and every system that currently touches inventory, and — usually the most revealing part — documenting where they currently disagree with each other and why. Most brands are surprised by how many small inconsistencies have accumulated quietly, because nobody was looking at all the systems side by side until this point.
From there, the real design work is deciding on a source-of-truth model: which system is authoritative for which piece of data, how buffer stock gets calculated and where it lives, and what the priority and routing logic should be across locations and channels. This is where the earlier sections of this guide stop being background reading and become the actual spec — fulfillment priority rules, split-shipment tolerance, per-SKU safety stock, and channel-specific reservation behavior all need to be decided explicitly rather than left to whatever each app's defaults happen to do.
The build itself is typically webhook listeners for real-time-ish updates, scheduled reconciliation jobs that catch anything the webhooks missed (webhooks can and do fail silently, so a periodic full-state comparison is what actually catches drift), and API integrations into each 3PL, ERP, and marketplace involved. Testing has to include simulated high-load scenarios specifically, since the failure modes that matter — race conditions during traffic spikes — don't show up in normal-volume testing at all. For a brand with a moderately complex setup — a couple of owned locations, one 3PL, one or two marketplaces — this kind of project realistically runs from several weeks to a couple of months depending on how much custom logic is involved and how clean the existing data is going in, with ongoing tuning afterward as sales patterns and channel mix keep evolving. It's not a project you finish once and forget; buffer sizes, priority rules, and sync frequency are settings that deserve revisiting every time the business adds a channel, a location, or hits a new order-volume tier.
If any of this sounds like your situation, talk to us. We'll tell you exactly where your revenue is leaking and what it would take to fix it. Explore Strategy & Consulting →

