3PL · OPS · INTEGRATIONS · 24 FEBRUARY 2026 · 7 MIN READ
Order routing across warehouses and a 3PL
Shopify already decides which location ships which order, and its default is better than most of the logic people build to replace it. The work is in the inventory numbers underneath and the exceptions the default cannot express.
Shopify splits every order into one or more fulfillment orders — each one a group of items expected to be fulfilled from the same location — and assigns each to a location using its routing rules. The default strategy tries to minimise split fulfilments, stay within the destination market, and ship from the closest location, and it needs at least two active locations before it does anything at all. A 3PL is not a step after that decision; it is one of the locations the decision chooses between. Almost every routing problem we are called in to fix turns out to be an inventory accuracy problem wearing a routing costume: the rules were fine, the numbers they ran on were not.
IN SHORT
- An order is not routed as a whole — Shopify creates a fulfillment order per group of items expected to ship from the same location, and each has its own lifecycle.
- Shopify’s default routing prioritises minimising split fulfilments, staying within the destination market, and shipping from the closest location, and requires at least two active locations.
- A 3PL integration registers as a fulfilment service with its own location, which is why it participates in routing rather than sitting after it.
- The Order Routing Location Rule function returns a ranked list of locations per fulfillment group, with locations selected in ascending rank order; Shopify documents it as available by request to Shopify Plus merchants only.
- Fulfillment constraint functions can require items to ship from the same location or restrict which locations may fulfil them — and Shopify states that if the constraint cannot be met, buyers see no shipping options at checkout.
- A maximum of 25 fulfillment constraint functions can be active on a store.
- Routing quality is capped by inventory accuracy: a rule that sends orders to a location whose stock figure is six hours old will send them to the wrong one.
The object that actually gets routed
Most conversations about routing go wrong in the first sentence, because people talk about routing an order. Shopify does not route orders. It creates fulfillment orders, each of which it documents as “either an item or a group of items in an Order that are expected to be fulfilled from the same location”, and it creates them automatically — you cannot make one by hand.
That matters for three practical reasons. A single customer order can become several fulfillment orders, each with its own status and its own lifecycle, which is why an order can be half shipped and half waiting. Each carries an assignedLocation, which Shopify notes can still change while the fulfillment order is unfulfilled — open, scheduled or on hold — or be changed deliberately with the fulfillmentOrderMove mutation. And each exposes supportedActions, the set of things you are allowed to do to it right now, which varies with status and assignment.
If you are building or buying anything that touches fulfilment, that last field is the one to design against. An integration that assumes it can always move, cancel or fulfil will break the first time it meets a fulfillment order that has already been accepted by a fulfilment service. Read supportedActions and act on what is there.
What the default already does, and when to leave it alone
Before anyone scopes custom routing, it is worth knowing what is running today. Shopify applies routing rules in sequence, and its default strategy has three stated objectives: minimise split fulfilments by choosing locations that can fulfil all items, stay within the destination market where possible, and ship from the closest location to reduce delivery times. Routing needs at least two active locations to do anything — with one location there is no decision to make.
Those three objectives are, in our experience, the right ones for the large majority of merchants, and they are already tuned against constraints most in-house logic ignores. If your setup is a UK 3PL, a small own-warehouse for slow-moving lines, and a US 3PL for North America, the default will route sensibly without configuration.
The prompt to change it is not “we have multiple locations”. It is a specific, nameable behaviour you want that the default cannot produce — a product that must ship from one place regardless of distance, a location you want held back as a reserve, an own-warehouse you want to exhaust before paying a 3PL pick fee, or a fragile line that must never be split across two boxes.
The two function APIs, and which one you actually want
Custom routing on Shopify runs through Functions, and there are two distinct ones that get confused with each other constantly.
The Order Routing Location Rule function decides preference. It receives cart context, fulfillment groups with their location handles, and location metadata including metafields, and it returns a ranked list of locations per fulfillment group. Shopify’s description is direct: locations are selected in ascending rank order, and Shopify picks the location with the highest priority ranking. It covers checkout, draft orders, order editing and order imports. The documented availability is narrow — Shopify states the API is only available by request for merchants on a Shopify Plus plan, with the developer enrolled in the Partners programme. If you are not on Plus, this is not the route, and the honest answer is usually that the default plus better stock data gets you most of the way.
The fulfillment constraint function decides permission. It expresses limits rather than preferences: that cart items must be fulfilled from the same location, or that items may only be fulfilled from a specified list of locations. Up to 25 constraint functions can be active on a store.
The difference is not academic, and there is a sharp edge on the constraint side worth reading twice. Shopify states that if items carrying a must-fulfil-together constraint are not available together, buyers will not see any shipping options at checkout. A preference that cannot be satisfied falls back to the next-best location. A constraint that cannot be satisfied removes the ability to buy. Write a constraint for something that is genuinely a hard requirement — a regulated item, a licensed product, a set that is meaningless split — and never as a way of expressing “we would rather”.
The metafield input is the part worth designing carefully. Because the routing function reads location metafields alongside product metafields, tags and collections, the rules can be expressed as data your operations team edits rather than as logic a developer redeploys. A location metafield for cost band, capacity or cut-off time, read by one function, is a far better arrangement than three functions encoding the same facts in code.
A 3PL is a location, not a stage
The mental model that causes the most trouble is treating the 3PL as something that happens after routing — Shopify decides, then we send it to the warehouse. In Shopify’s model the 3PL integration registers as a fulfilment service with its own location, so it is one of the candidates routing chooses between, on the same footing as your own warehouse and your retail stock rooms.
The consequence is that the request is a conversation with states, not a handover. A fulfillment order carries a requestStatus reflecting where things stand with the fulfilment service — submitted, accepted, and the equivalents for cancellation — and merchantRequests records what was sent. Two operational habits follow from that:
- Design for rejection. A fulfilment service can decline a request. Decide in advance what happens next — reroute to the next-ranked location, hold for a human, or notify the customer — and build it, because the default outcome is a fulfillment order sitting in a state nobody is watching.
- Watch the gap between submitted and accepted. It is the clearest early signal of a 3PL problem, and it appears in the data before it appears in the delivery figures or the support queue.
- Do not route on the 3PL’s stock figure if it is a scheduled export. Routing runs at checkout. A figure refreshed nightly is a figure that is wrong all day.
Splits are not automatically a failure
Minimising splits is a sensible default because a split costs real money: two picks, two boxes, two labels, and a customer who receives one parcel and wonders where the rest is. Most merchants are right to want fewer of them.
But the instinct to eliminate splits entirely is worth resisting, because the alternative is often worse. Forcing a whole order to one location can mean a slower service from a further warehouse, or waiting for a restock, when shipping four of five items today and the fifth tomorrow would have been the better customer outcome. The right question is not “how do we stop splitting” but “which splits are worth the second parcel”, and the answer depends on order value, item margin and whether the customer was shown a delivery date they will now miss.
If you have a delivery promise on the product page or in the cart, this is where routing and that promise have to agree. A routing change that quietly moves a class of orders to a further location will break a date you are advertising, and nothing in the routing configuration will tell you so.
The failures we see, in order of frequency
Almost none of these are routing logic problems.
- Stale inventory. The rules are fine; the numbers are hours old. Routing decisions are made at checkout against whatever Shopify believes right then, so the fix is the sync frequency and the write pattern, not the rules.
- Safety stock set in the wrong system. A buffer held back in the 3PL’s system but not reflected in Shopify produces routing to a location that then cannot pick. Decide which system owns the buffer and hold it in one place.
- A location that cannot ship where the order is going. Routing will happily choose a location whose carrier accounts do not serve the destination, or that has no compliant paperwork for it. This is a constraint, and it is one of the few cases where a constraint function is the right instrument.
- A constraint written as a preference. Somebody expressed “ideally ships together” as a must-fulfil-together rule, and now a mixed cart shows no shipping options at all. The symptom presents as a checkout bug, not as a routing one.
- No owner for exceptions. Rerouted, rejected and held fulfillment orders accumulate somewhere nobody looks. This is a rota problem, and it is the most common reason a technically correct setup produces late orders.
What we would talk you out of
Buying an order management system to solve routing. If the requirement is “ship from the nearest location that has everything”, Shopify already does that, and an OMS in the middle adds a second place for stock to be wrong. Buy one when you genuinely need to route across channels Shopify does not see — a marketplace, a trade portal, a retail estate — not to re-implement a default.
Encoding rules in a middleware layer. Routing logic that lives between Shopify and the 3PL is invisible to the checkout, so it cannot influence the delivery date a customer is shown and it cannot stop an order being taken that nobody can fulfil. Express the rule where the decision is made.
Starting with the rules. Spend the first fortnight on inventory accuracy per location and on the exception rota. If those two are right, the default routing is usually good enough; if they are wrong, no ranking function will save you. That sequencing — data first, rules second — is how we approach it on [support and store management](/services/support) engagements, and it is dull enough that it rarely survives a vendor pitch.
Questions this raises
How does order routing work with multiple locations?
Shopify splits an order into fulfillment orders — each a group of items expected to ship from the same location — and assigns each one to a location. The default strategy minimises split fulfilments, stays within the destination market where possible, and ships from the closest location. Routing requires at least two active locations before it does anything.
Does a 3PL sit inside Shopify’s routing or after it?
Inside it. A 3PL integration registers as a fulfilment service with its own location, so it is one of the candidates routing chooses between rather than a step that happens once the decision is made. The fulfillment order then tracks the request status with that service — submitted, accepted, and the cancellation equivalents.
Can you customise Shopify order routing?
Yes, through Functions. The Order Routing Location Rule function returns a ranked list of locations per fulfillment group, with locations chosen in ascending rank order, and reads location and product metafields as input. Shopify documents it as available by request for Shopify Plus merchants only, so it is not an option on other plans.
What is the difference between routing rules and fulfillment constraints?
Routing rules express preference and fall back to the next location when the best one is unavailable. Fulfillment constraints express permission — items must ship from the same location, or only from a named list. Shopify states that when a must-fulfil-together constraint cannot be met, buyers see no shipping options at checkout, so constraints should only encode hard requirements.
How do I stop orders being split across warehouses?
Shopify’s default already tries to, by preferring locations that can fulfil everything. Before forcing it harder, check the split is actually costing you more than the alternative: consolidating to a single distant location can miss the delivery date you advertised. Decide which splits are worth a second parcel rather than eliminating them on principle.
Why is routing sending orders to the wrong warehouse?
Usually because the stock figures it ran on were stale or a buffer is held in a system Shopify cannot see. Routing decides at checkout against what Shopify believes at that moment, so a nightly export from a 3PL is wrong for most of the day. Fix inventory accuracy and buffer ownership before rewriting any rules.
NEXT STEP
Free store audit
A senior Shopify engineer reviews your storefront, theme performance and checkout, then sends a prioritised list of fixes.
