Guide · Shopify Fulfillment API

Shopify Fulfillment Orders and multi-location routing: the API is a nightmare, but InfiPlex still puts orders where you want them

Ask anyone who has built against Shopify's fulfillment API and you'll get the same reaction: it's a genuine nightmare. Two related objects, Fulfillment and FulfillmentOrder, look similar and are not the same thing. Orders route to locations automatically through Shopify's own black-box logic, and a 2024 platform change means an app that isn't careful about which location an order is assigned to can end up reporting zero fulfillable inventory on stock that is sitting right there. InfiPlex doesn't just read whatever Shopify's routing decided, though: it actively moves fulfillment orders to the location a client defines, both when pushing orders into Shopify from another channel and when pulling Shopify orders out to fulfill and track elsewhere.

  • Two Objects, Not OneFulfillmentOrder is the routing assignment; Fulfillment is the actual shipment record
  • Location Ownership Is EnforcedAn app can only create fulfillments for orders assigned to its own location
  • InfiPlex Moves Orders For YouPush or pull, fulfillment orders land at the location your rules define, not wherever Shopify's routing sends them

Why this part of the API is hard

Two objects, similar names, different jobs

A Fulfillment Order is not a Fulfillment, even though the names invite you to assume they are interchangeable. A FulfillmentOrder represents an item or group of items from an order that are expected to be fulfilled from the same location, it is the assignment and the intent. A Fulfillment is created once work actually starts on some or all of those line items, and represents the real, physical shipment: carrier, tracking number, what was sent. One FulfillmentOrder can end up with zero, one, or several associated Fulfillments over its life, since a partial shipment creates a Fulfillment for the portion that shipped while the rest of the FulfillmentOrder remains open, gets split, or moves to another location. Confusing the two, treating a FulfillmentOrder's status as if it described a shipment, or trying to update a Fulfillment's carrier info on a FulfillmentOrder, is one of the most common sources of integration bugs against this API.

Order routing

Automatic, not something you configure per order

After an order is created, a background process Shopify calls order routing determines which location or locations are responsible for fulfilling each line item, and creates one or more FulfillmentOrders assigned to those locations. This happens automatically and cannot be done manually; there is no API call to create a FulfillmentOrder directly. What you configure ahead of time, in Settings, Shipping and delivery, Order routing, is the priority and rules that process uses, not the individual routing decision for a given order.

The field that matters is assigned_location on each FulfillmentOrder. It is the authoritative answer to who is responsible for that group of line items. The older, per-variant fulfillment_service field on a product variant is not reliable for this anymore, since Shopify allows the same variant to be stocked at both merchant-managed and third-party locations simultaneously; an app that still checks the variant field instead of assigned_location will get the wrong answer as soon as multi-location inventory is in play.

Status fields

Two status fields that look alike and are not

Field Scope Values
FulfillmentOrder.status Per fulfillment order (one location's piece of the order) OPEN, IN_PROGRESS, INCOMPLETE, ON_HOLD, SCHEDULED, CANCELLED, CLOSED
Order.displayFulfillmentStatus Aggregated across the whole order, for display UNFULFILLED, PARTIALLY_FULFILLED, FULFILLED, IN_PROGRESS, ON_HOLD, SCHEDULED, PENDING_FULFILLMENT, RESTOCKED

Notice IN_PROGRESS, ON_HOLD, and SCHEDULED appear in both lists, describing something different at each level: a single FulfillmentOrder can be IN_PROGRESS while the order as a whole shows PARTIALLY_FULFILLED because another location's FulfillmentOrder for the same order already closed. Shopify's own documentation is explicit that the order-level field is for display summary purposes only and does not capture the full fulfillment state, the FulfillmentOrder object is the source of truth. Building logic against the order-level status when you actually need to know what a specific location has to do is a reliable way to get the wrong answer some of the time.

The rule that trips up most integrations

You can only touch what is assigned to you

Since a platform change effective in the 2024-10 API version, an app can only create a fulfillment for a FulfillmentOrder that is assigned to a location it owns. If an app tries to fulfill an order assigned to merchant-managed inventory, or to a different app's fulfillment service location, the create call fails outright. This is the practical explanation behind a support pattern every multi-location Shopify merchant eventually hits: a warehouse or 3PL app reports zero fulfillable quantity on a SKU the merchant knows is in stock, because the order was never routed to that app's location in the first place, it was routed somewhere else, and the app has no visibility or authority over inventory it was not assigned.

The fix lives in the order routing configuration, not in the integration's code: the rules under Settings, Shipping and delivery, Order routing decide which location an order is assigned to, and if those rules do not rank the intended fulfillment location correctly, the API will faithfully assign orders elsewhere no matter how the integration is built. The same principle shows up in the multi-warehouse order routing guide and, on the CastleGate side, the 1:1 vs pooled inventory sync guide: the platform can only route intelligently to a location it can actually see and identify separately.

Splitting

Splitting a fulfillment order

A single FulfillmentOrder can be split, dividing its line items or quantities into a remaining piece and a new, separate FulfillmentOrder, most often used to ship part of an order now and hold or reroute the rest. The API returns both the remaining and the newly created FulfillmentOrder from that call, and each then follows its own status lifecycle independently from that point on. A merchant-managed FulfillmentOrder can also be moved between merchant-managed locations directly; moving does not change the shipping rate the customer already paid, since that was fixed at checkout.

Closing out

Closing out a fulfillment request

When a fulfillment service cannot ship any remaining items on a request it accepted, it closes the fulfillment order, which marks it incomplete and hands control back to the merchant to move the remaining items to another location, remove and refund them, or re-request fulfillment from the same service. That close action has narrow preconditions: it only works on a FulfillmentOrder assigned to a fulfillment-service location, only after that service has accepted the request, only while its status is exactly IN_PROGRESS, and only the specific app that accepted the request can call it. Any one of those conditions being off is a silent failure waiting to happen if it is not checked explicitly.

At the order level

Why Shopify's own routing isn't always the right answer

Shopify's order routing decides a location based on its own priority rules, which is fine when every order genuinely belongs wherever Shopify sends it. Multichannel sellers rarely have that luxury: an order that reaches Shopify from Amazon, Walmart, or any other connected channel often needs to land at a specific warehouse or 3PL, not wherever Shopify's routing settings happen to rank first, and an order pulled out of Shopify for fulfillment somewhere else usually needs to go to one particular place every time, not wherever it was originally assigned.

Automation

How InfiPlex actively moves fulfillment orders

When InfiPlex is used to send orders into Shopify, whether an order originated on Amazon, Walmart, or any other connected channel, and a client wants those orders fulfilled from a specific location, InfiPlex moves the resulting fulfillment order to that location rather than leaving it wherever Shopify's own order routing assigned it.

When InfiPlex is used to pull orders out of Shopify for fulfillment and push tracking information back once they ship, clients can have every order pulled from Shopify moved to one specific location for fulfillment, so warehouse and 3PL workflows built around that location handle Shopify orders the same way they handle every other channel, with no per-order manual reassignment.

Both directions rely on the same location-assignment mechanics covered above: InfiPlex reads assigned_location and FulfillmentOrder.status to know what Shopify has already decided, then moves the fulfillment order to the location the client's rules call for, the same way a merchant can move a fulfillment order between merchant-managed locations directly. Program page: Shopify integration.

Frequently asked

Questions, answered

The questions that come up once you're actually building against this part of Shopify's API.

Can I manually create a FulfillmentOrder in Shopify?

No. FulfillmentOrders are created automatically by Shopify's order routing process immediately after an order is placed. What you control ahead of time is the routing rules under Settings, Shipping and delivery, Order routing, not individual FulfillmentOrder creation.

Why does my app show zero fulfillable inventory for a SKU I know is in stock?

Almost always because the order was routed, by Shopify's order routing rules, to a location your app does not own. Since the 2024-10 API change, an app can only fulfill orders assigned to its own location; it has no visibility or authority over stock assigned elsewhere, regardless of how the integration is built.

What does closing a fulfillment order actually do?

It marks the order incomplete and returns control to the merchant, who can then move the remaining items to another location, refund them, or re-request fulfillment from the same service. It only works under narrow conditions: assigned to a fulfillment-service location, request already accepted, status exactly IN_PROGRESS, and only callable by the app that accepted the request.

Resources

Related pages and references

Fulfillment orders, routed to wherever you tell InfiPlex to put them

assigned_location read correctly, the right status field used for the right decision, and orders moved to the location your rules call for on push or pull. Live in 1–3 business days.

Fighting Shopify's fulfillment API across multiple locations or 3PLs?

We've built against every part of this API covered on this page, including moving fulfillment orders to the location a client defines on push or pull. Tell us where it's breaking and we'll help you find the actual cause. Contact us, email info@infiplex.com, or call 888‑770‑0857.

InfiPlex™ is a trademark of InfiPlex. Shopify is a trademark of Shopify Inc. InfiPlex is an independent integration provider and is not endorsed by or affiliated with Shopify. API behavior, access scopes, and platform rules are set by Shopify and change; verify current specifics in the Shopify API documentation before relying on them. ©2026 InfiPlex. All rights reserved.