Skip to main content

Build fulfillment solutions

Order management apps can automate the fulfillment process in Shopify on behalf of merchants.

This guide describes how to use the GraphQL Admin API to query orders and inventory levels, and manage orders using fulfillment order actions.



Anchor to Step 1: Retrieve an orderStep 1: Retrieve an order

You can use the GraphQL Admin API's order query to retrieve an order and its associated fulfillment orders. To retrieve fulfillment orders, provide the ID of the order in the request.

The response returns information about the associated fulfillment orders, including the fulfillment order ID, fulfillment order line items, supported actions, and assigned locations. Make note of the inventory item ID, as you'll use it to query inventory levels in the next step.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query getOrderByID {
order(id: "gid://shopify/Order/4057210552342") {
id
name
fulfillmentOrders(first: 10) {
edges {
node {
id
status
requestStatus
supportedActions {
action
}
destination {
address1
address2
city
countryCode
email
firstName
lastName
phone
province
zip
}
lineItems(first: 1) {
edges {
node {
id
totalQuantity
remainingQuantity
// Available as of 2023-04 API version
inventoryItemId
}
}
}
assignedLocation {
name
location {
address {
address1
address2
city
countryCode
phone
province
zip
}
id
}
}
merchantRequests(first: 1){
edges {
node {
message
}
}
}
}
}
}
}
}

JSON response

{
"data": {
"order": {
"id": "gid://shopify/Order/4057210552342",
"name": "#1007",
"fulfillmentOrders": {
"edges": [
{
"node": {
"id": "gid://shopify/FulfillmentOrder/5012074758166",
"status": "OPEN",
"requestStatus": "UNSUBMITTED",
"supportedActions": [
{
"action": "CREATE_FULFILLMENT"
},
{
"action": "MOVE"
}
],
"destination": {
"address1": "1318 Bloor St.",
"address2": "",
"city": "Innisfree",
"countryCode": "CA",
"email": "benedict.ankunding@testemail.com",
"firstName": "Benedict",
"lastName": "Ankunding",
"phone": null,
"province": "Alberta",
"zip": "T0B2G0"
},
"lineItems": {
"edges": [
{
"node": {
"id": "gid://shopify/FulfillmentOrderLineItem/10907791786006",
"totalQuantity": 3,
// The `remainingQuantity` is the most reliable way to determine the remaining quantity to be fulfilled for a line item that’s included in a fulfillment order.
"remainingQuantity": 3,
"inventoryItemId": "gid://shopify/InventoryItem/19848972992534",
}
}
}
]
},
"assignedLocation": {
"name": "Shopify Ottawa Office",
"location": {
"address": {
"address1": "151 O'Connor Street",
"address2": "1st Floor",
"city": "Ottawa",
"countryCode": "CA",
"phone": "+18887467439",
"province": "Ontario",
"zip": "K2P 2L8"
},
"id": "gid://shopify/Location/17285971990"
}
},
"merchantRequests": {
"edges": []
}
}
}
]
}
}
}
}

Anchor to Step 2: Retrieve inventory levelsStep 2: Retrieve inventory levels

You can use the GraphQL Admin API's inventoryItem query to retrieve the inventory levels for items and the locations where the items are stocked.

To retrieve inventory levels, provide the ID of the inventory item in your request:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query getInventoryItemByID {
inventoryItem(id: "gid://shopify/InventoryItem/49148385") {
id
inventoryLevels (first:6) {
edges {
node {
id
available
location {
name
id
}
}
}
}
}
}

JSON response

{
"data": {
"inventoryItem": {
"id": "gid://shopify/InventoryItem/19848972992534",
"inventoryLevels": {
"edges": [
{
"node": {
"id": "gid://shopify/InventoryLevel/17001775126?inventory_item_id=19848972992534",
"available": 7,
"location": {
"name": "Shopify Ottawa Office",
"id": "gid://shopify/Location/17285971990"
}
}
},
{
"node": {
"id": "gid://shopify/InventoryLevel/95549128726?inventory_item_id=19848972992534",
"available": 10,
"location": {
"name": "Shopify Montreal Office",
"id": "gid://shopify/Location/61205381142"
}
}
},
{
"node": {
"id": "gid://shopify/InventoryLevel/95549095958?inventory_item_id=19848972992534",
"available": 10,
"location": {
"name": "Shopify Toronto Office",
"id": "gid://shopify/Location/61205348374"
}
}
}
]
}
}
}
}

Anchor to Step 3: Query supported fulfillment order actionsStep 3: Query supported fulfillment order actions

Each fulfillment order object includes a list of supported fulfillment order actions to determine which actions the app can take. This information is available in the JSON response received in the first step in this guide.

You can use the order query to independently request information about fulfillment order actions. Provide the order ID to retrieve supported actions:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query getOrderByID {
order(id: "gid://shopify/Order/4057210552342") {
id
name
fulfillmentOrders(first: 10) {
edges {
node {
id
status
requestStatus
supportedActions {
action
}
}
}
}
}
}

JSON response

{
"data": {
"order": {
"id": "gid://shopify/Order/4057210552342",
"name": "#1007",
"fulfillmentOrders": {
"edges": [
{
"node": {
"id": "gid://shopify/FulfillmentOrder/5012074758166",
"status": "OPEN",
"requestStatus": "UNSUBMITTED",
"supportedActions": [
{
"action": "CREATE_FULFILLMENT"
},
{
"action": "MOVE"
}
]
}
}
]
}
}
}
}

Anchor to Step 4: Determine which actions to takeStep 4: Determine which actions to take

If a fulfillment order is open, then apps can inspect the list of supported actions and decide what action to take.

The following table describes the list of supported fulfillment order actions. Each action is used at a different stage in the fulfillment process.

Fulfillment order actionDescription
MOVEMoves a fulfillment order between merchant-managed locations.
CREATE_FULFILLMENTCreates a fulfillment for an order that a merchant or third-party fulfillment service can manage.
REQUEST_FULFILLMENTSends a request to a third-party fulfillment service to fulfill an order.
CANCEL_FULFILLMENT_ORDERImmediately cancels a fulfillment that's being managed by a third-party fulfillment service.
REQUEST_CANCELLATIONSends a request to a third-party fulfillment service to cancel a fulfillment order.
EXTERNALIndicates fulfillment orders that are assigned to an external fulfillment service.
Note

Apps should filter out closed fulfillment orders. Closed fulfillment orders have no supported actions, and can't be changed.

Anchor to Move a fulfillment orderMove a fulfillment order

You can use the GraphQL Admin API's fulfillmentOrderMove mutation to move a fulfillment order from one location to another.

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 customer has already paid.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation {
fulfillmentOrderMove(
id: "gid://shopify/FulfillmentOrder/5862841122838",
newLocationId: "gid://shopify/Location/67794403350"
){
originalFulfillmentOrder {
status
assignedLocation {
location {
id
name
}
}
}
movedFulfillmentOrder {
status
assignedLocation {
location {
id
name
}
}
}
remainingFulfillmentOrder {
status
}
}
}

JSON response

{
"data": {
// The original `fulfillmentOrder` that was requested to be moved.
"fulfillmentOrderMove": {
"originalFulfillmentOrder": {
"status": "OPEN",
"assignedLocation": {
"location": {
"id": "gid://shopify/Location/67794403350",
"name": "test-created-via-api-1"
}
}
},
// The new `fulfillmentOrder` assigned to the new location, which includes all line items. The status of this `fulfillmentOrder` is `OPEN`.
"movedFulfillmentOrder": {
"status": "OPEN",
"assignedLocation": {
"location": {
"id": "gid://shopify/Location/67794403350",
"name": "test-created-via-api-1"
}
}
},
// In this case, `remainingFulfillmentOrder` is `null` because 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, then `remainingFulfillmentOrder` includes a new `fulfillmentOrder` assigned to the original location, consisting of only the remaining items that couldn't be moved. The status of this `fulfillmentOrder` is `OPEN`.
"remainingFulfillmentOrder": null
"remainingFulfillmentOrder": null
}
}
}

Anchor to Create a fulfillmentCreate a fulfillment

You can use the GraphQL Admin API's fulfillmentCreateV2 mutation to create a fulfillment.

In the request, provide the line items, destination, and order information you retrieved in the first step of this guide.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation fulfillmentCreateV2 {
fulfillmentCreateV2(fulfillment: {
notifyCustomer: false,
trackingInfo: {
company: "my-shipping-company",
number: "1562678",
url: "https://www.my-shipping-company.com"
},
# The `fulfillmentCreateV2` mutation accepts an array of `FulfillmentOrderLineItemsInputs`, which lets apps create a single fulfillment with one or many fulfillment orders.
# All of the fulfillment orders need to be on the same order, and assigned to the same location.
lineItemsByFulfillmentOrder: [
{
fulfillmentOrderId: "gid://shopify/FulfillmentOrder/5012131971094",
# The array of `lineItemsByFulfillmentOrder.fulfillmentOrderLineItems` lets apps partially fulfill fulfillment orders.
# If `fulfillmentOrderLineItems` aren't provided, then the app creates a fulfillment for all remaining line items.
fulfillmentOrderLineItems: [
{
id: "gid://shopify/FulfillmentOrderLineItem/10907890253846",
quantity: 3
}
]
}
]
})
{
fulfillment {
id
status
trackingInfo {
company
number
url
}
}
userErrors {
field
message
}
}
}

JSON response

{
"data": {
"fulfillmentCreateV2": {
"fulfillment": {
"id": "gid://shopify/Fulfillment/3286409904150",
// If successful, then the fulfillment order transitions to a `CLOSED` status because there's no more remaining work. If this is a partial fulfillment (for example, only fulfilling `fulfillmentOrderLineItem` with a `quantity` of 1), then the fulfillment order moves to an `IN_PROGRESS` status.
"status": "SUCCESS",
"trackingInfo": [
{
"company": "my-shipping-company",
"number": "1562678",
"url": "https://www.my-shipping-company.com"
}
]
},
"userErrors": []
}
}
}
Caution

As of API version 2024-10, you can only create fulfillments for orders that are assigned to a merchant-managed location or for orders that are assigned to a third-party fulfillment service that you own. If you use the GraphQL Admin API's fulfillmentCreateV2 for an order that's assigned to another third-party fulfillment service, then the request will fail.

Anchor to Request a fulfillmentRequest a fulfillment

If a merchant is using a third-party fulfillment service, then the service can support the REQUEST_FULFILLMENT action.

You can use the GraphQL Admin API's fulfillmentOrderSubmitFulfillmentRequest mutation to request a fulfillment with the assigned fulfillment service. Apps can also provide an optional message to facilitate communication with the fulfillment service.

To request a fulfillment for all the line items, you only need to pass the fulfillment order ID in the request:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation submitFulfillmentRequest{
fulfillmentOrderSubmitFulfillmentRequest(
id: "gid://shopify/FulfillmentOrder/5014438150166",
message: "Please gift wrap."
) {
originalFulfillmentOrder {
id
status
requestStatus
}
submittedFulfillmentOrder {
id
status
requestStatus
merchantRequests(first: 10) {
edges {
node {
message
}
}
}
}
unsubmittedFulfillmentOrder {
id
status
requestStatus
}
}
}

JSON response

{
"data": {
"fulfillmentOrderSubmitFulfillmentRequest": {
"originalFulfillmentOrder": {
"id": "gid://shopify/FulfillmentOrder/5014438150166",
"status": "OPEN",
"requestStatus": "SUBMITTED"
},
// The submitted fulfillment order is always returned in the `submittedFulfillmentOrder` field. In this case, the `requestStatus` is `SUBMITTED`, and the status is `OPEN`.
"submittedFulfillmentOrder": {
"id": "gid://shopify/FulfillmentOrder/5014438150166",
"status": "OPEN",
"requestStatus": "SUBMITTED",
"merchantRequests": {
"edges": [
{
"node": {
"message": "Please gift wrap."
}
}
]
}
},
"unsubmittedFulfillmentOrder": null
}
}
}

Anchor to Request a partial fulfillmentRequest a partial fulfillment

You can also use the GraphQL Admin API's fulfillmentOrderSubmitFulfillmentRequest mutation to submit only one of the line items on the fulfillmentOrder for fulfillment.

The following example shows a request to fulfill one fulfillment order line item:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation submitFulfillmentRequest{
fulfillmentOrderSubmitFulfillmentRequest(
id: "gid://shopify/FulfillmentOrder/5014440017942",
message: "Please gift wrap.",
fulfillmentOrderLineItems: {
id: "gid://shopify/FulfillmentOrderLineItem/10912786186262",
quantity: 1
}) {
originalFulfillmentOrder {
id
status
requestStatus
}
submittedFulfillmentOrder {
id
status
requestStatus
merchantRequests(first: 10) {
edges {
node {
message
}
}
}
}
unsubmittedFulfillmentOrder {
id
status
requestStatus
}
}
}

JSON response

{
"data": {
"fulfillmentOrderSubmitFulfillmentRequest": {
// Because this is a partial fulfillment request, Shopify closes the `originalFulfillmentOrder` for which a fulfillment request was submitted, and opens two new fulfillment orders.
// The `originalFulfillmentOrder` contains all of the line items. The status is `CLOSED`, and the `requestStatus` is `UNSUBMITTED`.
"originalFulfillmentOrder": {
"id": "gid://shopify/FulfillmentOrder/5014440017942",
"status": "CLOSED",
"requestStatus": "UNSUBMITTED"
},
// The `submittedFulfillmentOrder` is the new `fulfillmentOrder`, which contains a `FulfillmentOrderLineItem` with a quantity of 1 that's submitted to the fulfillment service. The status is `OPEN`, and the `requestStatus` is `SUBMITTED`.
"submittedFulfillmentOrder": {
"id": "gid://shopify/FulfillmentOrder/5014440902678",
"status": "OPEN",
"requestStatus": "SUBMITTED",
"merchantRequests": {
"edges": [
{
"node": {
"message": "Please gift wrap."
}
}
]
}
},
// The `unsubmittedFulfillmentOrder` is the new `fulfillmentOrder`, which contains the remaining line items that weren't submitted. The status is `OPEN`, and the `requestStatus` is `UNSUBMITTED`.
"unsubmittedFulfillmentOrder": {
"id": "gid://shopify/FulfillmentOrder/5014440935446",
"status": "OPEN",
"requestStatus": "UNSUBMITTED"
}
}
}
}

Anchor to Cancel a fulfillment orderCancel a fulfillment order

If a merchant is using a third-party fulfillment service, then the service can support the CANCEL_FULFILLMENT_ORDER action.

You can use the GraphQL Admin API's fulfillmentOrderCancel mutation to immediately cancel the fulfillment order.

Fulfillment orders that return the CANCEL_FULFILLMENT_ORDER action have one of the following request statuses:

Request statusDescriptionBehavior
SUBMITTEDA request for fulfillment has been sent to the third-party fulfillment service, but it hasn't been acknowledged.The fulfillment order is immediately cancelled because the fulfillment service hasn't yet accepted the fulfillment request.
CANCELLATION_REQUESTEDA request to cancel the fulfillment order has been submitted to the third-party fulfillment service.If an app cancels the fulfillment order and doesn't wait for the third-party fulfillment service to respond, then the fulfillment service might still complete the assigned work, and the order might still succeed.

A merchant might submit a fulfillment request for a fulfillment order. They then receive an email from the customer, who wants to confirm the size of an item. To stop the third-party fulfillment service from accepting the fulfillment order request, the app can cancel the fulfillment order request until the customer confirms the size.

The following example shows how to cancel a fulfillment that's being managed by a third-party fulfillment service:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation fulfillmentOrderCancel {
fulfillmentOrderCancel(id: "gid://shopify/FulfillmentOrder/5014440902678") {
fulfillmentOrder {
id
}
replacementFulfillmentOrder {
id
}
}
}

JSON response

{
"data": {
"fulfillmentOrderCancel": {
// The original fulfillment order, which now has a `CLOSED` status.
"fulfillmentOrder": {
"id": "gid://shopify/FulfillmentOrder/5014440902678"
},
// 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`.
"replacementFulfillmentOrder": {
"id": "gid://shopify/FulfillmentOrder/5014442508310"
}
}
}
}
Caution

If you use the GraphQL Admin API's fulfillmentOrderCancel mutation on a fulfillment order assigned to a legacy fulfillment service that hasn't opted into FulfillmentOrder-based fulfillment, then the request will fail. For more information, refer to Migrate to fulfillment orders.

Anchor to Request to cancel a fulfillment orderRequest to cancel a fulfillment order

If a merchant is using a third-party fulfillment service, then the service can support the REQUEST_CANCELLATION action.

You can use the GraphQL Admin API's fulfillmentOrderSubmitCancellationRequest mutation to send a request to the third-party fulfillment service to cancel the fulfillment order.

The following example shows how to send a request to the third-party fulfillment service to cancel the fulfillment order. The response returns that the cancellation request is in progress.

Tip

Even after a fulfillment request is accepted for a fulfillment order, apps can also submit cancellation requests for the fulfillment order.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation fulfillmentOrderSubmitCancellationRequest {
fulfillmentOrderSubmitCancellationRequest(id: "gid://shopify/FulfillmentOrder/5014442508310") {
fulfillmentOrder {
id
status
}
userErrors {
field
message
}
}
}

JSON response

{
"data": {
"fulfillmentOrderSubmitCancellationRequest": {
"fulfillmentOrder": {
"id": "gid://shopify/FulfillmentOrder/5014442508310",
"status": "IN_PROGRESS"
},
"userErrors": []
}
}
}
Caution

If you use the GraphQL Admin API's fulfillmentOrderSubmitCancellationRequest mutation on a fulfillment order assigned to a legacy fulfillment service that hasn't opted into FulfillmentOrder-based fulfillment, then the request will fail. For more information, refer to Migrate to fulfillment orders.

Anchor to Fulfillment outside of ShopifyFulfillment 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.


In API version 2023-01 and higher, your app can subscribe to webhooks for event notifications related to fulfillment orders. The following examples show the JSON responses from each of the available webhooks.

To learn how to set up and consume webhooks, refer to Webhooks configuration overview.

Example webhook responses

// Occurs when a fulfillment order is created and the order routing has been completed for the order
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "open",
}
}
// Occurs when a merchant requests one or more fulfilment order line items be fulfilled by a fulfilment service
{
"original_fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "in_progress",
"request_status": "submitted"
},
"submitted_fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "in_progress",
"request_status": "submitted"
},
"unsubmitted_fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/2",
"status": "in_progress",
"request_status": "submitted"
},
"fulfillment_order_merchant_request": {
"id": "gid://shopify/FulfillmentOrderMerchantRequest/1",
"message": "Fragile"
}
}
// Occurs when a fulfillment service accepts a merchant's request to fulfill one or more fulfillment order line items
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "in_progress",
"request_status": "accepted"
},
"message": "Reminder that tomorrow is a holiday. We won't be able to ship this until Monday.",
}
// Occurs when a fulfillment service rejects a merchant's request to fulfill an order
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "open",
"request_status": "rejected"
},
"message": "We weren't able to find this product in the warehouse. Sorry!",
}
// Occurs when a fulfillment order is placed on hold
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/5862841122838",
"status": "on_hold",
"fulfillment_holds": [
{
"reason": "inventory_out_of_stock",
"reason_notes": "Sorry, we ran out of stock."
}
]
}
}
// Occurs when a fulfillment order hold is released
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "open"
}
}
// Occurs when a scheduled fulfillment order is ready to be fulfilled
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "open",
"risk_assessment": "low"
}
}
// Occurs when a fulfillment order is rescheduled
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"fulfill_at": "2023-09-11T13:54:00-04:00",
"status": "cancelled"
}
}
// Occurs when a merchant requests to cancel a fulfillment order after the request was accepted by a fulfillment service
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "in_progress",
"request_status": "cancellation_requested"
},
"fulfillment_order_merchant_request": {
"id": "gid://shopify/FulfillmentOrderMerchantRequest/1",
"message": "The customer cancelled their order."
}
}
// Occurs when a fulfillment service accepts a fulfillment cancellation request from a merchant
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "in_progress",
},
"message": "The item was not picked and packed yet. As a result, cancelling as requested.",
}
// Occurs when a fulfillment service rejects a fulfillment cancellation request from a merchant
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "in_progress",
},
"message": "This was already picked up by the courier."
}
// Occurs when a fulfillment service or merchant cancels a fulfillment order
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "cancelled"
},
"replacement_fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/2",
"status": "open"
}
}
// Occurs when a fulfillment service intends to close an assigned fulfillment order that was previously accepted for fulfillment. After this action, the fulfillment order transitions to an incomplete status. To continue the fulfillment process, the merchant or app needs to determine the next course of action.
{
"fulfillment_order": {
"id": "gid://shopify/FulfillmentOrder/1",
"status": "incomplete"
},
"message": "Apologies, but it appears we are out of stock.",
}


Was this page helpful?