Fulfillment API problems--400

Topic summary

A developer is encountering a persistent 400 error when attempting to create fulfillments via Shopify’s Fulfillment API, despite reviewing numerous similar cases without finding a working solution.

Current Workflow:

  • Successfully retrieves fulfillment order IDs from the endpoint
  • Builds fulfillment payload with line items, tracking info, and location ID
  • Receives 400 error when POSTing to the fulfillments endpoint (2023-04 API version)
  • Note: Using the older orders/{order_id}/fulfillments endpoint returns 404 instead

Technical Details:

  • App has all necessary permissions (write_fulfillments)
  • Single warehouse setup, fulfilling complete orders at once
  • PHP implementation with proper JSON encoding
  • Location ID is correctly retrieved from fulfillment orders array

Troubleshooting Attempted:

  • Added debugging/logging (shows normal execution until the 400 error)
  • Verified placeholder values are replaced with actual data in production code
  • Confirmed location_id uses the correct variable ($loc_id)

Status: Issue remains unresolved. A community member suggested adding cURL debugging with detailed error logging and response inspection, but the root cause of the 400 error has not been identified.

Summarized with AI on November 13. AI used: claude-sonnet-4-5-20250929.

I have waded through dozens (or hundreds) of similar cases here. Some are ‘solved’ and some are not, but none of the solutions work for me.

We have a very simple setup, with just one in-house warehouse. We receive the orders, ship them out, then we want to update tracking data and close the order.

Once I have the shipping/tracking data, first we retrieve the fulfillment ids using the endpoint https://{store name}.myshopify.com/admin/api/2023-04/orders/{order id}/fulfillment_orders.json

This works fine. Since we’re going to fulfill the entire order at once I take the first ID supplied, before the line_items.

I then build the outgoing data with the following php code:

$post = array(
‘fulfillment’ => array(
‘line_items_by_fulfillment_order’ => array(
“fulfillment_order_id” => {fill_id}
),
“tracking_info” => array(
“company” => $carrier,
“number” => $tracking
),
‘notify_customer’ => false,
‘location_id’ => {location id from the fulfillment orders array}
)
);

$post=json_encode($post);

This is sent out to the endpoint https://{store name}.myshopify.com/admin/api/2023-04/fulfillments.json and I get a 400 error. (Note that if I try to do it the old way, with orders/{order id}, as suggested in some of the posts, I get a 404 instead of 400.

My app has all the permissions it needs, and when I fetch the fulfillmlent IDs it always gives create_fulfillment as a support action, so I’m at a loss. Any help is appreciated.

I would try adding some debugging and see what the logs show:

$post = array(
    'fulfillment' => array(
        'line_items_by_fulfillment_order' => array(
            "fulfillment_order_id" => {fill_id}
        ),
        "tracking_info" => array(
            "company" => $carrier,
            "number" => $tracking
        ),
        'notify_customer' => false,
        'location_id' => {location id from the fulfillment orders array}
    )
);

$post = json_encode($post);

// Initialize cURL
$ch = curl_init();

// Set cURL options
curl_setopt($ch, CURLOPT_URL, "https://{store name}.myshopify.com/admin/api/2023-04/fulfillments.json");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
    // Include your access token here
));

// Execute cURL request
$response = curl_exec($ch);

// Check for errors and log them
if(curl_errno($ch)) {
    error_log('cURL error: ' . curl_error($ch));
}

// Log response
error_log('API response: ' . $response);

// Close cURL session
curl_close($ch);
'location_id' => {location id from the fulfillment orders array}

seems to be a placeholder rather than actual code. In your PHP script, you should replace {location id from the fulfillment orders array} with the actual location ID obtained from your fulfillment orders. This should be a specific numeric or string identifier, like $locationId, which you would have defined earlier in your script based on the data retrieved from Shopify’s API.

Ensure that you’re assigning the correct location ID as retrieved from the fulfillment orders to the location_id field in your $postData array.

I have done debugging but removed those statements for this script. It shows everything proceeding normally up until the 400 error, which has no details.

Sorry, I put it in this way here as a placeholder to clarify..in the actual script it’s $loc_id, which is the location I retrieved in the fulfillment ids call.