Build for fulfillment services
Fulfillment service apps can provide third-party warehousing, print on demand, or fulfillment services that prepare and ship orders on behalf of merchants.
This guide describes how to use the GraphQL Admin API to manage fulfillments in the following ways:
- Receive fulfillment requests and cancellations
- Accept and reject fulfillment requests and cancellations
- Exchange fulfillment requests notes with merchants
- Create and perform fulfillments for assigned work
- Optionally close fulfillment orders
Anchor to RequirementsRequirements
- Your app can make authenticated requests to the GraphQL Admin API.
- Your app has the
write_fulfillments
,read_assigned_fulfillment_orders
andwrite_assigned_fulfillment_orders
access scopes. Learn how to configure your access scopes using Shopify CLI. - Your store has existing orders that are unfulfilled. If you need to make changes to an unfulfilled item, then you can edit an order.
- You've met Shopify's protected customer data requirements.
Anchor to Step 1: Create a fulfillment serviceStep 1: Create a fulfillment service
To begin managing fulfillments as a fulfillment service app, you need to create a fulfillment service in Shopify with a callback URL that will listen for fulfillment requests.
You can use the GraphQL Admin API's fulfillmentServiceCreate
mutation to create a fulfillment service for each of your locations where you fulfill orders. Each fulfillment service corresponds to an inventory location in Shopify and allows you to manage fulfillment orders assigned to this location. Each physical fulfillment centre or warehouse should have its own fulfillment service.
To register a fulfillment service, your app requires the write_fulfillments
access scope.
All fulfillment services are created with fulfillment orders opted in now.
POST https://{shop}.myshopify.com/api/{api_version}/graphql.json
GraphQL mutation
JSON response
Anchor to Step 2: Edit locationsStep 2: Edit locations
After creating a fulfillment service for each of your fulfillment locations, you need to update the associated location address. This ensures that Shopify can have an accurate representation of your fulfillment network.
You can use the GraphQL Admin API's locationEdit
mutation to update the address information of each location associated with your fulfillment services.
To update a location, your app requires either the write_locations
or write_fulfillments
access scope.
-
write_locations
- This is required to modify merchant-managed locations -
write_fulfillments
- The app that created the fulfillment service can edit locations associated with it
POST https://{shop}.myshopify.com/api/{api_version}/graphql.json
GraphQL mutation
JSON response
Repeat step 1 and step 2 for each fulfillment location you operate from until all fulfillment locations have their own fulfillment services. Ensure that you provide a unique name
for each fulfillment service to distinguish between your different locations. A callbackUrl
can be shared by multiple fulfillment services.
Anchor to Step 3: Receive fulfillment requests and cancellationsStep 3: Receive fulfillment requests and cancellations
Apps must configure an endpoint on a callback URL where Shopify will send the notifications for fulfillment requests. This is a requirement to receive requests from Shopify. The endpoint should be structured in the following format: <callback_url>/fulfillment_order_notification
.
The payloads that the endpoint receives include the kind
property or field, which can have one of the following values:
Value | Description |
---|---|
FULFILLMENT_REQUEST | When a merchant requests a fulfillment in their Shopify admin |
CANCELLATION_REQUEST | When a merchant submits a cancellation request in their Shopify admin |
The following examples show payloads for each type of request:
Payloads
Fulfillment request payload
Cancellation request payload
Anchor to Step 4: Act on fulfillment requestsStep 4: Act on fulfillment requests
Each fulfillment order has a status and a request status, both of which indicate the state of the fulfillment order.
A fulfillment order notification of FULFILLMENT_REQUEST
indicates that there’s a new fulfillment order assigned to the service, and it’s ready to be acted on because the merchant has requested a fulfillment.
Fulfillment services can retrieve assigned fulfillment order requests, and specify the fields that are required to determine whether it should accept or reject a fulfillment request. Requests can be validated with the same HMAC validation method that's used when Shopify sends webhooks to apps.
Anchor to Retrieve assigned fulfillment order requestsRetrieve assigned fulfillment order requests
The following example shows how to use the assignedFulfillmentOrders
connection to request the first 10 assigned fulfillment orders with the FULFILLMENT_REQUESTED
status.
The request includes:
- Fulfillment order destination, line items, and line item SKUs.
- Merchant request notes from the
merchantRequests
field.
POST https://{shop}.myshopify.com/api/{api_version}/graphql.json
GraphQL query
JSON response
Anchor to Accept a fulfillment requestAccept a fulfillment request
After retrieving the assigned fulfillment orders, the fulfillment service can review each fulfillment order, and determine whether it can be fulfilled.
To accept a fulfillment order, the fulfillment service must send an accepted request using the GraphQL Admin API's fulfillmentOrderAcceptFulfillmentRequest
mutation:
POST https://{shop}.myshopify.com/api/{api_version}/graphql.json
GraphQL mutation
JSON response
After the fulfillment service sends an accepted request, the fulfillment order card in the Shopify admin indicates to the merchant that the request was accepted:

Anchor to Reject a fulfillment requestReject a fulfillment request
If the fulfillment service determines that it can’t fulfill the order, then it can reject the fulfillment request using the fulfillmentOrderRejectFulfillmentRequest mutation:
POST https://{shop}.myshopify.com/api/{api_version}/graphql.json
GraphQL mutation
JSON response
After the fulfillment service sends a rejected request, the fulfillment order card in the Shopify admin indicates to the merchant that the request was rejected:

Anchor to Step 5: Create fulfillmentsStep 5: Create fulfillments
After accepting a fulfillment request, the fulfillment service can begin creating fulfillments using the GraphQL Admin API's fulfillmentCreateV2
mutation. The fulfillment service can create multiple fulfillments for a given fulfillment order if required, representing multiple packages to be shipped.
When an app accepts a fulfillment request, the fulfillment order status transitions to in progress. After all line items on the fulfillment order are entirely fulfilled, the fulfillment order status transitions to a closed state.
Fulfillments can’t be created for a fulfillment order before the fulfillment service accepts the fulfillment request.
The following examples show how to create a fulfillment for a single line item, with a quantity of three. The response provides a success status, which indicates that the fulfillment has been successfully created.
POST https://{shop}.myshopify.com/api/{api_version}/graphql.json
GraphQL mutation
JSON response
Anchor to Step 6: Act on cancellation requestsStep 6: Act on cancellation requests
A fulfillment order notification of CANCELLATION_REQUEST
indicates that a merchant has requested for a fulfillment order to be cancelled. Similar to retrieving assigned fulfillment order requests, the fulfillment service can retrieve cancellation requests.
After retrieving cancellation requests, the fulfillment service can either accept or reject the cancellation request. If the fulfillment service accepts the cancellation request, then it can cancel the fulfillment.
It might take a few minutes for cancellation requests to show up as a part of your assigned fulfillment orders. We recommend polling your assigned fulfillment orders until you receive the cancellation request.
Anchor to Merchants cancelling fulfillment ordersMerchants cancelling fulfillment orders
Merchants can cancel a fulfillment order before the fulfillment service responds to the cancellation request. This option is provided immediately after requesting cancellation. A warning message is also displayed to the merchant, indicating that forcibly cancelling a fulfillment order doesn’t guarantee that the fulfillment service won't ship the items.
After the fulfillment service responds by either rejecting or accepting the cancellation request, the option is no longer provided to the merchant. The fulfillment service can’t create fulfillments against a fulfillment order that was cancelled by a merchant.
Anchor to Retrieve cancellation requestsRetrieve cancellation requests
The following example shows how to use the assignedFulfillmentOrders
connection to query cancellation requests. Pass in the assignmentStatus
argument and set it to CANCELLATION_REQUEST
to retrieve assigned fulfillment orders with the CANCELLATION_REQUEST
status.
A fulfillment service can’t create fulfillments for the fulfillment orders that are returned in assigned fulfillment orders until it accepts or rejects the cancellation requests.
POST https://{shop}.myshopify.com/api/{api_version}/graphql.json
GraphQL query
JSON response
Anchor to Accept a cancellation requestAccept a cancellation request
After retrieving the assigned fulfillment order cancellation requests, the fulfillment service can review each fulfillment order, and determine whether it can be cancelled.
If the fulfillment order can be cancelled, then the fulfillment service can use the GraphQL Admin API's fulfillmentOrderAcceptCancellationRequest
mutation to accept the cancellation of the fulfillment order and send an optional message to the merchant.
The response returns a fulfillment order that has a request status of cancellation accepted:
POST https://{shop}.myshopify.com/api/{api_version}/graphql.json
GraphQL mutation
JSON response
After the fulfillment service accepts a cancellation request, the fulfillment order card in the Shopify admin indicates to the merchant that the cancellation request was accepted:

Anchor to Reject a cancellation requestReject a cancellation request
If the fulfillment service determines that it can’t accept the cancellation of the fulfillment order, then it can reject the cancellation request using the the GraphQL Admin API's fulfillmentOrderRejectCancellationRequest
mutation.
The response returns a fulfillment order that has a request status of cancellation rejected. The fulfillment service can continue to create fulfillments after rejecting a cancellation.
POST https://{shop}.myshopify.com/api/{api_version}/graphql.json
GraphQL mutation
JSON response
After the fulfillment service rejects a cancellation request, the fulfillment order card in the Shopify admin indicates to the merchant that the cancellation request was rejected:

Anchor to Step 7: Cancel a fulfillmentStep 7: Cancel a fulfillment
You can cancel a fulfillment using the GraphQL Admin API's fulfillmentCancel
mutation. However, any fulfillment orders that the fulfillment was created against will be affected in the following ways:
- If the underlying fulfillment order was entirely fulfilled, then it will be automatically closed.
- After cancelling a fulfillment, a new fulfillment order is created consisting of the line items from the cancelled fulfillment. This new fulfillment order is assigned to the same location as the original fulfillment order if the items are still stocked there.
- If all items from the cancelled fulfillment can't be sourced from the same location, then the new fulfillment order is assigned to a location where the items are stocked, considering the shop’s fulfillment priority settings. This might result in multiple newly opened fulfillment orders for different locations.
- If the fulfillment order was partially fulfilled, then the fulfillment order line item and remaining quantity properties / fields are adjusted back based on the cancelled fulfillment’s line items.
POST https://{shop}.myshopify.com/api/{api_version}/graphql.json
GraphQL mutation
JSON response
After a fulfillment service cancels a fulfillment, the fulfillment order card in the Shopify admin notifies the merchant that the fulfillment request has been cancelled:

Anchor to Step 8 (Optional): Close a fulfillment orderStep 8 (Optional): Close a fulfillment order
In some cases, a fulfillment service might realize that it can’t fulfill the requested items only after it’s already accepted a fulfillment order request.
The fulfillment service can use the GraphQL Admin API's fulfillmentOrderClose
mutation to close the fulfillment order and indicate to the merchant that they won't be fulfilling the order.
Closing a fulfillment results in a fulfillment order where the status is incomplete and the request status is closed.
If a merchant makes a new fulfillment request on an order that had a fulfillment order marked as incomplete, then the original incomplete fulfillment order will have a closed status and a new fulfillment order will be created.
POST https://{shop}.myshopify.com/api/{api_version}/graphql.json
GraphQL mutation
JSON response
Anchor to Step 9 (Optional): Enable tracking supportStep 9 (Optional): Enable tracking support
Your app can optionally act on Shopify requests for tracking numbers by exposing a /fetch_tracking_numbers
endpoint that's prefixed with the callback_url
that you set in step 3.
If tracking_support
is set to true
, then once each hour, Shopify makes a request to the /fetch_tracking_numbers
endpoint to determine if there are any completed fulfillments awaiting tracking numbers from the remote fulfillment service.
Your app can optionally act on Shopify requests for inventory levels by exposing a /fetch_stock
endpoint that's prefixed with the callback_url
that you set in step 3.
If fulfillment_service.inventory_management
is set to true
, then Shopify makes a request to the /fetch_stock
endpoint for the inventory of an individual SKU when the product is set up initially, when its SKU is changed in the Shopify admin, or when its inventory management is set to use the fulfillment service. Please note that SKU strings are case-sensitive at Shopify, and the SKUs returned from the /fetch_stock
endpoint will be matched to the Shopify product SKUs respecting the case.
A request for all inventory data happens once every hour to keep Shopify up to date with the remote fulfillment service.
The format for the payload is a JSON object where the keys are the SKUs and the values are the on_hand
inventory state levels.
Fetch stock payload
Anchor to WebhooksWebhooks
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.",
}
Anchor to Next stepsNext steps
- Learn about the recommended workflow for using Shopify APIs to track orders placed through third-party marketplaces.
- Learn how to manage inventory using the GraphQL Admin API by referring to the
inventoryItem
query andinventoryActivate
mutation in the reference documentation.