--- title: Order management for enterprise description: >- Common patterns for how enterprise merchants integrate external order management systems with Shopify at scale. source_url: html: >- https://shopify.dev/docs/apps/build/orders-fulfillment/order-management-apps/enterprise-oms-integration md: >- https://shopify.dev/docs/apps/build/orders-fulfillment/order-management-apps/enterprise-oms-integration.md --- # Order management for enterprise This guide describes three common integration patterns for using order management system (OMS) layers with Shopify at enterprise scale. The following sections give guidance on when to use each pattern, and discuss some typical architectural constraints and tradeoffs. *** ## Three integration patterns The following table is a quick reference comparing the three primary integration patterns. "Shopify location model" refers to how inventory data is mapped onto locations inside of Shopify. Each pattern is described in detail in the sections below. | Dimension | External OMS | Hybrid | Shopify-native | | - | - | - | - | | Who orchestrates orders? | External OMS | Shared. Lightweight OMS as decision engine, Shopify for execution. | Shopify | | Shopify's role | Checkout, storefront (possibly), payment gateway integration, and status reflection | Commerce platform and order state management | Checkout through fulfillment | | Shopify location model | Single aggregated location. OMS owns real inventory topology. | Multiple locations as inventory buckets | Locations fully mapped inside of Shopify | | Order management interface | OMS/External | Shopify admin (enhanced) | Shopify admin | | System of record | OMS | Shopify | Shopify | These three patterns represent common architectures seen at enterprise scale, and aren't strict rules to follow. *** *** ## Shopify's role across all patterns Two architectural constraints apply equally across all three patterns: payment operations must flow through Shopify, and order identifiers come from a fixed set of fields on Shopify's Order type. ### Payment operations must flow through Shopify All capture, refund, void, re-authorization, and related payment calls must go through Shopify's GraphQL Admin API. Shopify doesn't support exporting payment tokens to third parties. The OMS calls [`orderCapture`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/orderCapture), [`refundCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/refundCreate), and related mutations, and Shopify communicates with the gateway. Shopify has three native settings for how capture (settlement) is handled after checkout: | Capture setting | Behavior | Typical use | | - | - | - | | Automatic at checkout | Payment captured immediately at order creation. | Shopify-native pattern. Simplest, no OMS capture action needed. | | Automatic at fulfillment | Captured when a `Fulfillment` is created. | OMS controls capture indirectly by controlling fulfillment timing. | | Manual | OMS calls `orderCapture` explicitly. | Most control. OMS decides when, how much, and which authorization to target. This setting is very common at enterprise scale. | Fulfillment and capture are independent. Shopify doesn't gate fulfillment on payment capture. If the merchant requires capture before shipment, then the OMS must implement that hold logic. ### Order identifiers Shopify exposes multiple order identifiers on the GraphQL Admin Order type: * [`id`](https://shopify.dev/docs/api/admin-graphql/latest/objects/Order#field-id): the global GID, for example `gid://shopify/Order/123456`. Stable and unique. * [`name`](https://shopify.dev/docs/api/admin-graphql/latest/objects/Order#field-name): the merchant-facing identifier with merchant-configured prefix and suffix, for example `#1001`, `EN1001`, or `1001-A`. Not guaranteed to be unique. * [`number`](https://shopify.dev/docs/api/admin-graphql/latest/objects/Order#field-number): the bare integer used to generate `name`, for example `1001`. Not guaranteed to follow a consecutive sequence or to be unique across (or within) stores. * [`confirmationNumber`](https://shopify.dev/docs/api/admin-graphql/latest/objects/Order#field-confirmationnumber): a random alphanumeric string assigned at checkout, for example `XPAV284CT`. Not guaranteed to be unique. External systems often have strict ID requirements such as digits only, maximum character length, or numeric range limits. Choose the right identifier early in the integration design, and confirm it meets the external system's constraints. *** *** ## Pattern 1: external OMS as orchestrator In this pattern, the external OMS owns the entire post-checkout order lifecycle, and employees can perform fulfillment updates, order-level changes, and financial transactions such as capture or refund from the order management interface. Shopify handles checkout, payment authorization, order creation, and customer-facing status. ![A diagram showing the external OMS as the central hub, with Shopify on the left sending orders and receiving status and captures, and external systems such as Warehouse, WMS, Retail POS, Marketplaces, 3PL, DSV, and ERP connected to the OMS on the right.](https://shopify.dev/assets/assets/images/apps/fulfillments/order-management-apps/enterprise-model-1-external-oms-o5_ziALL.png) ### When to use this pattern We recommend this pattern when the merchant has: * A mature OMS with existing 3PL, warehouse, and vendor integrations. * Complex sales channels beyond Shopify (marketplaces, wholesale, and retail POS) where the OMS already serves as the central hub and the channels can't be migrated to Shopify. * Cross-channel customer service visibility (buy online return in store, buy in store return online) between Shopify and other sales channels. * Fulfillment logic which exceeds what Shopify currently expresses natively. This logic gap could be in the rules themselves (vendor acceptance windows, split routing across legal entities, hold-until-confirmation gating) or in input data which Shopify doesn't currently model (factory-level SKU sourcing, vendor-specific allocation, legacy ERP business logic). * Complex, conditional rules about payment capture timing (capture at different points for different payment methods, split-tender orders, or capture gated on vendor acceptance or shipment confirmation). * Multi-brand or multi-entity structures where fulfillment routing spans legal entities, not just locations. ### How it works Shopify creates the order at checkout and authorizes payment. The OMS takes over from there. Routing, allocation, fulfillment, and capture decisions all happen in the OMS. The OMS evolves the fulfillment lifecycle and writes payment operations back to Shopify through the [GraphQL Admin API](https://shopify.dev/docs/api/admin-graphql/latest). This pattern pairs naturally with headless storefronts. ### Inventory In this pattern, the OMS owns the real inventory topology. Physical locations, in-transit stock, allocated reservations, and available-to-sell calculations all live outside Shopify. Shopify needs only what the storefront needs to gate purchase decisions: a single number per SKU representing what's available to sell online. **The standard model:** * Configure a **single Shopify location**, often named something like "Online" or "Available to sell". * The OMS aggregates available-to-sell quantities from across its real inventory topology and pushes one number per SKU to that single Shopify location. * All Shopify-originated orders allocate against that single location. The OMS receives the order via the `orders/create` webhook and decides where to physically fulfill from based on its own routing logic. **Sync follows three stages:** 1. **Initial sync**: Before orders begin flowing, the OMS pushes current available-to-sell quantities for every SKU to establish a baseline. Use [`inventorySetQuantities`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/inventorySetQuantities) since the OMS is the authoritative source. 2. **Threshold-based ongoing sync**: After the baseline is established, push updates only when available quantity crosses a meaningful boundary. The right cutoffs depend on per-SKU velocity, available-to-sell volume, and how aggressively the merchant wants to manage low-stock messaging on the storefront. The goal is to keep the storefront accurate at decision-relevant boundaries without spending API calls on every single inventory change in the OMS. Use [`inventoryAdjustQuantities`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/inventoryAdjustQuantities) for delta updates or [`inventorySetQuantities`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/inventorySetQuantities) for absolute writes, depending on whether the OMS is sending a change or the new authoritative value. 3. **Cleanup reconciliation**: On a daily or more frequent basis, run a reconciliation job that reads inventory quantities from Shopify, compares them against the OMS's current state, and corrects any drift. Use [`inventorySetQuantities`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/inventorySetQuantities) for reconciliation writes. Coordination is bidirectional. The OMS pushes available-to-sell into Shopify, and Shopify pushes order events out through `orders/create` and `orders/paid` so the OMS can decrement its real inventory. ### Receiving orders: webhook and API enrichment The recommended pattern is for the OMS to receive webhook notifications from Shopify, and then to use the GraphQL Admin API to read additional data. Webhook payloads don't include metafields, custom attributes, or enriched line-item metadata that the OMS might need for routing. The OMS should query the full order after each webhook, and periodically make API calls to catch any missed orders. ### Writing status back to Shopify Customer-facing features such as Shop Pay, the Shop app, transactional emails, customer account order status pages, and order tracking links all derive their shipping and delivery information exclusively from Shopify's fulfillment data. Because of this, it's critical that the OMS accurately updates order and fulfillment status in Shopify and keeps this data current throughout the fulfillment lifecycle. If the OMS fulfills an order but doesn't write that back, the customer sees no shipping update and support teams can't determine order status from the Shopify admin. The OMS doesn't set an order status directly. Instead, it calls the following mutations that progress the [`FulfillmentOrder`](https://shopify.dev/docs/api/admin-graphql/latest/objects/FulfillmentOrder) lifecycle: * [`fulfillmentCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fulfillmentCreate): records a shipment with tracking information. The shipping confirmation notification to the customer is opt-in via the [`notifyCustomer`](https://shopify.dev/docs/api/admin-graphql/latest/input-objects/FulfillmentInput#field-notifycustomer) input field, which defaults to `false`. * [`fulfillmentOrdersSetFulfillmentDeadline`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fulfillmentOrdersSetFulfillmentDeadline): sets expected fulfillment timelines, visible in the Shopify admin. Shopify updates the customer-facing order status as a consequence of these GraphQL mutations. ### API surface #### Webhooks (inbound to OMS) | Webhook | Purpose | | - | - | | `orders/create` | New order placed. Triggers OMS to pull full order through the API. | | `orders/updated` | Order edited or address changed after creation. | | `orders/cancelled` | Order cancelled. OMS stops fulfillment if in progress. | | `orders/paid` | Payment captured. OMS tracks payment state for reconciliation. | | `refunds/create` | Refund processed. OMS updates records and handles restock. | | `order_transactions/create` | Payment transaction created (auth, capture, or refund). OMS tracks payment state. | #### Mutations (outbound to Shopify) | Mutation | Purpose | | - | - | | [`fulfillmentOrderSubmitFulfillmentRequest`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fulfillmentOrderSubmitFulfillmentRequest) | Requests fulfillment from a 3PL or fulfillment service location. | | [`fulfillmentCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fulfillmentCreate) | Creates a shipment with tracking. Optionally notifies the customer (set `notifyCustomer: true`; defaults to `false`). | | [`orderCapture`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/orderCapture) | Captures authorized payment (full or partial). | | [`refundCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/refundCreate) | Processes a refund with optional restock. | | [`orderEditBegin`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/orderEditBegin) / [`orderEditCommit`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/orderEditCommit) | Modifies an order post-creation. | | [`inventorySetQuantities`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/inventorySetQuantities) | Sets absolute inventory (reconciliation sync). | | [`inventoryAdjustQuantities`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/inventoryAdjustQuantities) | Adjusts inventory by delta for incremental sync. | *** *** ## Pattern 2: hybrid Shopify-centric with external routing In this pattern, Shopify is the operational center. Employees manage orders, fulfillment, and transactions through the Shopify admin. An external layer handles routing, cross-store coordination, and vendor management that Shopify can't do natively. ![A diagram showing Shopify as the operational center with an external routing layer connected for order routing, cross-store coordination, and DSV or EDI management. Staff connects to Shopify, and additional Shopify stores connect through the routing layer.](https://shopify.dev/assets/assets/images/apps/fulfillments/order-management-apps/enterprise-model-2-hybrid-B21qawes.png) ### When to use this pattern We recommend this pattern when the merchant: * Wants staff to work in the Shopify admin, not a separate OMS. * Needs routing beyond native capabilities, such as cross-store fulfillment, complex fallback logic, DSV coordination, or order splitting. * Operates multiple Shopify stores that need coordination. * Has other channels or systems but wants Shopify as the operational center. * Needs to perform re-authorizations or custom capture processes. ### How it works An external application layer acts as a headless decision engine that makes order routing decisions, but has no independent staff-facing UI. It communicates decisions back to Shopify primarily through the order, fulfillment, and payment APIs. It can also communicate through [metafields](https://shopify.dev/docs/apps/build/metafields), [tags](https://shopify.dev/docs/api/admin-graphql/latest/mutations/tagsAdd), or [fulfillment holds](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fulfillmentOrderHold) with reason notes. ### Inventory In this pattern, an external system (an OMS or similar) still owns the source-of-truth inventory and the allocation decisions. What changes from Pattern 1 is that more of the inventory data is mirrored into Shopify. Two reasons drive this: 1. **Reduce dependencies for the online shopping experience**: When per-SKU availability is held in Shopify directly, the storefront can render product pages, cart eligibility, and low-stock indicators without waiting on a synchronous call to the external system. The shopping path is faster and more resilient when the external system is slow or unavailable. 2. **Support Shopify-connected channels that need per-location accuracy**: Buy online pick up in store (BOPIS), ship-from-store, ship-to-customer, and Shopify POS all require Shopify to know real per-location quantities. The single aggregated location used in Pattern 1 is not sufficient for these channels. **The standard model:** * Configure **one Shopify Location per physical fulfillment point**: stores, warehouses, and any offsite pool that serves a Shopify-connected channel. * For Shopify-aware locations (stores running Shopify POS, warehouses with a Shopify-aware WMS or fulfillment service), Shopify is the source of truth for that location and updates flow naturally from POS sales and fulfillment events. * For external-source locations (third-party warehouses, drop-ship vendor inventory, offsite pools managed by the OMS), the external system pushes quantity updates into Shopify so the connected channels see accurate per-location stock. **Operations:** * The external routing layer can intervene on `fulfillment_orders/order_routing_complete` to override Shopify's default location assignment when its rules disagree with Shopify's choice. * Use [`fulfillmentOrderMove`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fulfillmentOrderMove) to reassign a fulfillment order to a different location when the original location can't fulfill, or when the routing layer decides another location is a better fit (closer to the customer, lower-cost shipping, vendor acceptance). * For external-source locations, use the same threshold-based sync approach as Pattern 1 to keep external quantities current in Shopify without excessive API traffic. ### What the external layer handles * Complex routing and fallback logic that exceeds what [order routing functions](https://shopify.dev/docs/api/functions/reference/order-routing-location-rule) can express. * Cross-store coordination across separate Shopify stores, because the Shopify admin is per-store. * Drop-ship vendor coordination including EDI lifecycle (850/855/856), vendor acceptance, and PO management. * Order splitting across multiple fulfillment sources based on availability or vendor. ### API surface #### Webhooks (inbound to external layer) | Webhook | Purpose | | - | - | | `orders/create` | New order. Routing layer evaluates where to fulfill. | | `fulfillment_orders/order_routing_complete` | Shopify assigned `FulfillmentOrder` objects. Routing layer checks or overrides. | | `fulfillment_orders/placed_on_hold` | Order held. Routing layer tracks for coordination. | | `fulfillment_orders/cancelled` | `FulfillmentOrder` cancelled. Routing layer reroutes. | | `fulfillment_orders/moved` | `FulfillmentOrder` moved to a different location. | | `fulfillment_orders/split` | `FulfillmentOrder` split for partial fulfillment. | | `order_transactions/create` | Payment state change. Routing layer might gate fulfillment on capture. | #### Mutations (outbound to Shopify) | Mutation | Purpose | | - | - | | [`metafieldsSet`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/metafieldsSet) | Writes routing decisions to order metafields. | | [`fulfillmentOrderMove`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fulfillmentOrderMove) | Moves a `FulfillmentOrder` to a different location. | | [`fulfillmentOrderHold`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fulfillmentOrderHold) | Places a hold pending a routing decision. | | [`fulfillmentOrderReleaseHold`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fulfillmentOrderReleaseHold) | Releases a hold when fulfillment can proceed. | | [`fulfillmentCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fulfillmentCreate) | Creates a fulfillment with tracking. | | [`inventorySetQuantities`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/inventorySetQuantities) / [`inventoryAdjustQuantities`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/inventoryAdjustQuantities) | Syncs external inventory into Shopify locations. | *** *** ## Pattern 3: Shopify-native orchestration In this pattern, Shopify owns the entire order lifecycle using native capabilities. No external OMS is required. ![A diagram showing Shopify as a self-contained system handling checkout, routing, fulfillment, inventory, and customer status, with only optional connections to a 3PL fulfillment service and a custom dashboard.](https://shopify.dev/assets/assets/images/apps/fulfillments/order-management-apps/enterprise-model-3-native-TJw05F_V.png) ### When to use this pattern * The merchant operates within a single Shopify instance, or a small number of stores without cross-store fulfillment. * Routing logic fits within [order routing Functions](https://shopify.dev/docs/api/functions/reference/order-routing-location-rule). * The fulfillment network maps to Shopify locations as physical fulfillment points, including natively supported 3PL [fulfillment services](https://shopify.dev/docs/apps/build/orders-fulfillment/fulfillment-service-apps) and custom fulfillment services. * No legacy OMS dependency. ### Inventory Shopify is the authoritative source for inventory. Use the standard [Inventory API](https://shopify.dev/docs/api/admin-graphql/latest/objects/InventoryItem) for any quantity adjustments. No external sync is required. If your store uses a third-party logistics provider (3PL) or [fulfillment service](https://shopify.dev/docs/apps/build/orders-fulfillment/fulfillment-service-apps), they can push inventory updates into Shopify using their associated Location, like any other merchant-facing inventory write. The native primitives that support this pattern are documented in: * [Apps in order management](https://shopify.dev/docs/apps/build/orders-fulfillment/order-management-apps). * [Fulfillment service apps](https://shopify.dev/docs/apps/build/orders-fulfillment/fulfillment-service-apps). * [Order Routing Location Rule Function](https://shopify.dev/docs/api/functions/reference/order-routing-location-rule). * [Build fulfillment solutions](https://shopify.dev/docs/apps/build/orders-fulfillment/order-management-apps/build-fulfillment-solutions). If fulfillment complexity outgrows native capabilities, then the architecture shifts toward the hybrid or external OMS pattern. *** *** ## Next steps * [Apps in order management](https://shopify.dev/docs/apps/build/orders-fulfillment/order-management-apps). * [Fulfillment service apps](https://shopify.dev/docs/apps/build/orders-fulfillment/fulfillment-service-apps). * [Order Routing Location Rule Function](https://shopify.dev/docs/api/functions/reference/order-routing-location-rule). * [Build fulfillment solutions](https://shopify.dev/docs/apps/build/orders-fulfillment/order-management-apps/build-fulfillment-solutions). * [`orderCapture` mutation](https://shopify.dev/docs/api/admin-graphql/latest/mutations/orderCapture). * [`refundCreate` mutation](https://shopify.dev/docs/api/admin-graphql/latest/mutations/refundCreate). * [`fulfillmentCreate` mutation](https://shopify.dev/docs/api/admin-graphql/latest/mutations/fulfillmentCreate). * [Webhooks overview](https://shopify.dev/docs/apps/build/webhooks). * [Metafields](https://shopify.dev/docs/apps/build/custom-data/metafields). ***