Cannot fulfill order via API: legacy fulfillments return 406, fulfillment_orders is empty, all scopes granted

Hi everyone,

I’m building a Laravel app that integrates with the Shopify Admin API for a dev store:

  • Store: shopiify-dev-store.myshopify.com

  • App is installed with the following scopes (from granted_scopes log):

json

Copy

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

[

“write_products”,

“read_products”,

“write_orders”,

“read_orders”,

“write_fulfillments”,

“read_fulfillments”,

“write_assigned_fulfillment_orders”,

“read_assigned_fulfillment_orders”,

“write_shipping”,

“read_shipping”,

“write_draft_orders”,

“read_draft_orders”,

“write_inventory”,

“read_inventory”,

“write_locations”,

“read_locations”,

“write_themes”,

“read_themes”,

“write_price_rules”,

“read_price_rules”,

“write_discounts”,

“read_discounts”,

“write_checkouts”,

“read_checkouts”,

“write_marketing_events”,

“read_marketing_events”,

“write_resource_feedbacks”,

“read_resource_feedbacks”,

“read_analytics”,

“read_shopify_payments_payouts”

]

So the app has write_fulfillments, read_fulfillments, write_assigned_fulfillment_orders, and read_assigned_fulfillment_orders.


Problem 1: Cannot create a fulfillment for an unfulfilled order

I have an order like this (simplified):

json

Copy

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

{

“id”: 6870850764975,

“admin_graphql_api_id”: “gid://shopify/Order/6870850764975”,

“financial_status”: “paid”,

“fulfillment_status”: null,

“line_items”: [

{

“id”: 15746809725103,

“quantity”: 1,

“requires_shipping”: true,

“fulfillment_service”: “manual”

}

],

“fulfillments”:

}

I tried using the legacy REST endpoint to create a fulfillment:

php

Copy

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

$payload = [

'fulfillment' => \[

    'message'         => 'Order #1136 shipped',

    'notify_customer' => true,

    'tracking_info'   => \[

        'company' => 'FedEx',

        'number'  => '102096900',

        'url'     => 'https://FedEx.com/102096900',

    \],

    'line_items'      => \[

        \[

            'id'       => 15746809725103,

            'quantity' => 1,

        \],

    \],

\],

];

$response = $shop->api()->rest(

'POST',

'/admin/api/2024-10/orders/6870850764975/fulfillments.json',

$payload

);

But this always returns:

  • HTTP status: 406 Not Acceptable

The exception is:

text

Copy

1

2

Client error: `POST https://shopiify-dev-store.myshopify.com/admin/api/2024-10/orders/6870850764975/fulfillments.json`

resulted in a `406 Not Acceptable` response

So it looks like the store is on the new fulfillment orders workflow and the old line_items API is not allowed.

Then I tried the new fulfillment orders REST endpoint:

php

Copy

1

2

3

4

5

6

7

8

9

$foResponse = $shop->api()->rest(

'GET',

"/admin/api/2025-10/orders/6870850764975/fulfillment_orders.json"

);

Log::info(‘FULFILLMENT_ORDERS RAW’, [

'status' => $foResponse\['status'\],

'body'   => $foResponse\['body'\],

]);

Response:

json

Copy

1

2

3

{

“fulfillment_orders”:

}

So there are no FulfillmentOrder objects for this order, even though:

  • The order is paid

  • It has a shipping address

  • It has a shippable line item (requires_shipping: true)

  • It is unfulfilled

Because fulfillment_orders is empty, I also cannot use:

http

Copy

1

POST /admin/api/2025-10/fulfillments.json

with line_items_by_fulfillment_order, since I have no fulfillment_order_id or fulfillment_order_line_item_id.

So from the REST side:

  • Legacy endpoint → 406

  • New fulfillment_orders endpoint → empty array

Which means there’s currently no way for my app to fulfill this order via API.


Problem 2: Cannot update tracking for an existing fulfillment

I have another order which is already fulfilled (fulfilled from the Admin):

json

Copy

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

{

“id”: 6875231518895,

“order_number”: 1139,

“fulfillment_status”: “fulfilled”,

“fulfillments”: [

{

“id”: 6324579893423,

“status”: “success”,

“tracking_company”: null,

“tracking_number”: null,

“line_items”: [

{

“id”: 15755415584943,

“quantity”: 1,

“fulfillment_status”: “fulfilled”

}

]

}

]

}

I tried to update the tracking info using the REST helper:

php

Copy

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

$trackingPayload = [

'fulfillment' => \[

    'tracking_info' => \[

        'number'  => '63777665870001',

        'company' => 'UPS',

    \],

    'notify_customer' => true,

\],

];

$updateResponse = $shop->api()->rest(

'POST',

'/admin/api/2024-01/fulfillments/6324579893423/update_tracking.json',

$trackingPayload

);

This returns:

  • HTTP status: 422

  • Body: "Tracking information update failed."

Then I tried the general update endpoint:

php

Copy

1

2

3

4

5

6

7

8

9

10

11

12

13

14

$payload = [

'fulfillment' => \[

    'tracking_number'  => '63777665870001',

    'tracking_company' => 'UPS',

    'tracking_url'     => 'https://www.ups.com/track?loc=en_US&tracknum=63777665870001',

    'notify_customer'  => true,

\],

];

$updateResponse = $shop->api()->rest(

'PUT',

'/admin/api/2024-01/fulfillments/6324579893423.json',

$payload

);

But this also returns:

  • HTTP status: 406 Not Acceptable

So even for an existing fulfillment, the legacy REST Fulfillment endpoints are not usable on this store (406).


What I’ve already verified

  • The app has all relevant scopes, including:

    • write_fulfillments

    • read_fulfillments

    • write_assigned_fulfillment_orders

    • read_assigned_fulfillment_orders

    • write_orders

    • read_orders

    • etc.

  • The order 6870850764975 is:

    • financial_status: "paid"

    • fulfillment_status: null

    • Has a normal shipping address

    • A single line item requires_shipping: true, fulfillment_service: "manual".

  • There are no existing fulfillments or refunds on that order.


My questions

  1. Why is GET /admin/api/{version}/orders/{order_id}/fulfillment_orders.json returning an empty fulfillment_orders array for this valid, unfulfilled, paid order?

    Is there any store setting, fulfillment app, or test‑store limitation that would prevent FulfillmentOrder objects from being created or exposed for this order?

  2. Is it expected that all legacy Fulfillment REST endpoints return 406 on a store that uses the fulfillment orders workflow?

    • POST /orders/{id}/fulfillments.json → 406

    • PUT /fulfillments/{id}.json → 406

    If yes, is the only supported way to create fulfillments and update tracking now via the Admin GraphQL API (e.g., fulfillmentCreateV2)?

  3. What is the officially recommended approach to:

    • Fulfill an unfulfilled order from an app on a store like this?

    • Update tracking on an existing fulfillment?

I’m happy to switch to GraphQL for fulfillment if that’s the only supported way, but right now I’m blocked because:

  • REST legacy Fulfillment endpoints are rejected (406), and

  • REST FulfillmentOrder endpoint returns an empty list for this particular order.

Any clarification from Shopify staff or anyone who has dealt with this migration before would be really appreciated.

Thanks!