Manage fulfillments as an order management app

API versions 2020-01 and higher

This guide describes how order management apps can use the Admin API to manage fulfillments in Shopify. If your app assigns orders to be fulfilled by the merchant or by third-party locations, then follow this guide. If your app uses its own location (such as a third-party warehouse that prepares and ships orders on behalf of the store owner), then follow our guide on managing fulfillments as a fulfillment service app.

If you're looking to migrate to fulfillment orders from the legacy fulfillment platform, follow the migration guide for instructions on how to add the permissions your app needs without requiring the merchant to reinstall your app.

Fulfillment resources

Before you create fulfillments, it's helpful to understand some of the different fulfillment-related resources:

  • Order: Contains information about an order, including an array of the fulfillment orders which include line items that were purchased.
  • Location: Represents a geographical location where a line item can be fulfilled from. A fulfillment service always has its own location, and variants managed by the fulfillment service should always be fulfilled from that location.
  • FulfillmentOrder: Represents either an item or a group of items in an order that are to be fulfilled from the same location. There can be more than one fulfillment order for an order at a given location.
  • Fulfillment: Represents a shipment of one or more items in an order. It includes the line item that the fulfillment applies to, its tracking information, and the location of the fulfillment.
  • FulfillmentService: Represents a third-party warehousing service that prepares and ships orders on behalf of the store owner. Each fulfillment service is associated with its own location. When you create a fulfillment service, a new location is automatically created and associated with it.

Getting started

Before creating a fulfillment, you'll first need to have some unfulfilled orders on your test store. If you need to learn how to create test orders, then refer to our tutorial, Manage test orders with the REST Admin API.

Apps that fall under this category also need to request either (or both) of the following permissions using OAuth:

  • read or write permissions for merchant_managed_fulfillment_orders to manage fulfillment orders assigned to merchant-managed locations
  • read or write permissions for third_party_fulfillment_orders to manage third-party fulfillment orders on behalf of the merchant

Query the order to see its fulfillment orders and assigned locations

To get started, retrieve the fulfillment orders of the order that you want to create a fulfillment for.

Get the inventory levels

Using the inventory item ID from the fulfillment order, you can find the inventory level for the items and the locations where they're stocked.

The response shows that the variant is stocked at three different locations, the Shopify Ottawa Office, the Montreal Office, and the Toronto Office.

Managing orders by using fulfillment order actions

Each fulfillmentOrder object includes a list of supported fulfillment order actions to determine which actions it can take. This information is available in the payload recieved in the first step in this tutorial. To request this information independently, you can query specifically for the supported actions.

Clients should filter out CLOSED fulfillment orders. Closed fulfillment orders have no supported actions, and cannot be changed. If a fulfillment order is OPEN, clients can inspect the list of supported actions to see what is available to them. The list of supported fulfillment order actions includes:

Each action is used at a different stage in the fulfillment process.

MOVE

You can move a fulfillment order between locations if it returns MOVE as a supported action. To move a fulfillment order from one location to another, use the fulfillmentOrderMove mutation. Only fulfillment orders assigned to merchant-managed locations can be moved between locations, and moving a fulfillment order has no impact on the shipping rate that the buyer has already paid.

To move our fulfillmentOrder, you can make a call to FulfillmentOrderMove:

When all of the items that are a part of a fulfillment order are successfully moved, the following information is returned:

  • originalFulfillmentOrder - This is the original fulfillmentOrder that was requested to be moved. The status of this fulfillmentOrder is now CLOSED.
  • movedFulfillmentOrder - This is a new fulfillmentOrder assigned to the new location, consisting all line items. The status of this fulfillmentOrder is OPEN.
  • remainingFulfillmentOrder - In this case, it is NULL since there are no remaining items at location one that need to be fulfilled.

If a fulfillment order has multiple items and only some of them can be moved, the following information is returned:

  • originalFulfillmentOrder - This is the original fulfillmentOrder that was requested to be moved. The status of this fulfillmentOrder is now CLOSED.
  • movedFulfillmentOrder - This is a new fulfillmentOrder assigned to location two that includes the moved items. The status of this fulfillmentOrder is OPEN.
  • remainingFulfillmentOrder - This is a new fulfillmentOrder assigned to the original location, consisting of only the remaining items that could not be moved. The status of this fulfillmentOrder is OPEN.

CREATE_FULFILLMENT

Now that we have all of the information that we need to determine how to fulfill a given fulfillmentOrder including the line items, destination, and origin, we can create a fulfillment. Note that fulfillmentOrderLineItem.remainingQuantity is the most reliable way to determine the remaining quantity to be fulfilled for a line item that’s included in a given fulfillmentOrder.

For fulfillment orders that return CREATE_FULFILLMENT as a supported action, the fulfillmentCreateV2 mutation will allow you to create fulfillments. It’s important to note that if the fulfillmentOrder in question is assigned to a third party, then you need to have the third_party_fulfillment_orders permission to perform this action. For fulfillmentOrders assigned to a merchant-managed location, you need the merchant_managed_fulfillment_orders permission.

In the following example, we can fulfill the fulfillment order that we have been using in prior examples:

If successful, this request transitions the fulfillmentOrder to a CLOSED status since there is no more remaining work to be done on it. If this was a partial fulfillment (for example, only fulfilling fulfillmentOrderLineItem quantity 1), then the fulfillmentOrder would be in an IN_PROGRESS status.

Note that fulfillmentCreateV2 accepts an array of FulfillmentOrderLineItemsInputs in GraphQL, which lets apps create a single fulfillment that bridges multiple fulfillment orders. To do so, all the fulfillment orders in question have to be on the same order, and assigned to the same location.

REQUEST_FULFILLMENT

If a store is using a third party fulfillment service, the service may support the REQUEST_FULFILLMENT, CANCEL_FULFILLMENT_ORDER, and REQUEST_CANCELLATION actions. To perform any of these actions, an app requires write on the third_party_fulfillment_orders permission.

For fulfillment orders that return REQUEST_FULFILLMENT as a supported action, you can request fulfillment with the assigned fulfillment service by calling the fulfillmentOrderSubmitFulfillmentRequest mutation. Clients can also provide an optional message to facilitate communication with the fulfillment service.

In the following example, the fulfillmentOrder has a single line item, with a remaining quantity of 3. The request submits the entire fulfillmentOrder to the third party (including all line items):

The submitted fulfillment order is always returned in the submittedFulfillmentOrder field. In this case, the requestStatus is SUBMITTED, and the status is OPEN.

Based on the same example above, if the app wanted to submit only one of the line items on the fulfillmentOrder for fulfillment, then the request might instead look like this:

Note how the above submits a request for a single FulfillmentOrderLineItem. Given that this is a partial request, Shopify closes the original fulfillmentOrder for which a fulfillment request was submitted, and opens two new fulfillment orders:

  • originalFulfillmentOrder - This is the original fulfillmentOrder, which contained all line items. The status is CLOSED, and the requestStatus is UNSUBMITTED.
  • submittedFulfillmentOrder - This is a new fulfillmentOrder, which contains a FulfillmentOrderLineItem with a quantity of 1 that is submitted to the fulfillment service. The status is OPEN, and the requestStatus is SUBMITTED.
  • unsubmittedFulfillmentOrder - This is a new fulfillmentOrder, which contains the remaining line items that were not submitted. The status is OPEN, and the requestStatus is UNSUBMITTED.

CANCEL_FULFILLMENT_ORDER

For fulfillment orders that return CANCEL_FULFILLMENT_ORDER as a supported action, apps can call the fulfillmentOrderCancel mutation to cancel the fulfillment. Fulfillment orders that return this supported action have a requestStatus of SUBMITTED or CANCELLATION_REQUESTED.

If a fulfillmentOrder has a requestStatus of SUBMITTED, then a request for fulfillment has been sent to the third-party fulfillment service, but it has not been acknowledged. Calling FulfillmentOrderCancel cancels the fulfillmentOrder immediately since the fulfillment service has not yet accepted the fulfillment request. For example, a merchant submits a fulfillment request for a fulfillmentOrder. They then receive an email from the customer, who wants to confirm the size of an item. To stop the fulfillmentService from accepting the fulfillment order request, the merchant chooses to cancel the fulfillment order request in the app until the customer confirms the size.

Since the request from the previous example hasn't been accepted yet, we can cancel the request:

The call to cancel the fulfillment order returns the following:

  • fulfillmentOrder - This is the original fulfillment order, which now has a CLOSED status.
  • replacementFulfillmentOrder - This is the new fulfillment order, which is assigned to the same fulfillment service. This fulfillment order will be in an OPEN status, with a requestStatus of UNSUBMITTED.

If you call FulfillmentOrderCancel on a fulfillment order assigned to a legacy fulfillment service that is not opted into FulfillmentOrder-based fulfillment, then the request will fail.

REQUEST_CANCELLATION

After a fulfillment request is accepted for a fulfillment order, apps can also submit cancellation requests for it. For fulfillment orders that return REQUEST_CANCELLATION as a supported action, apps can call the fulfillmentOrderSubmitCancellationRequest mutation to request cancellation.

If the request is successful, then the fulfillmentOrder is updated to have a requestStatus of CANCELLATION_REQUESTED. If you call FulfillmentOrderSubmitCancellationRequest on a fulfillment order assigned to a legacy fulfillment service that is not opted into FulfillmentOrder-based fulfillment, then the request will fail.

If a fulfillmentOrder has a requestStatus of CANCELLATION_REQUESTED, then the cancellation request has been sent to the third-party fulfillment service but they have not yet accepted or rejected the request. If an app cancels the fulfillment order using FulfillmentOrderCancel without waiting for the third-party fulfillment service to respond, the fulfillment service may still complete the assigned work, and the order may still

Fulfillment outside of Shopify

Fulfillment orders that are assigned to an external fulfillment service return EXTERNAL as a supported action. When dealing with a fulfillment order that's assigned to an external fulfillment service, your app should redirect the merchant to the URL in the externalUrl field to initiate the fulfillment process outside of Shopify.

API references