Covers all questions related to inventory management, order fulfillment, and shipping.
I need to fulfill orders using the REST API given the order_id. I started to retrieve the fulfillments associated with the order_id where order_id is 2185365291143
https://.../admin/api/2020-04/orders/2185365291143/fulfillments.json
I receive status code 200, and but the json body response is empty. I have verified that the order is unfullfilled.
{'fulfillments': []}
In the attached image, I have visualized what I really want to achieve (basically fulfill all the line items in an order)
Hi @JianXiongWu ,
If you are using API version 2020-01 and higher, you can fulfill an order using the FulfillmentOrder and Fulfillment resources. Follow this document for more information on how to do this: https://shopify.dev/tutorials/manage-fulfillments-with-fulfillment-and-fulfillmentorder-resources
If you are using API version 2019-10 and below, you can fulfill an order using the Fulfillment and the Location resources. You can follow this guide: https://shopify.dev/tutorials/manage-fulfillments-with-fulfillment-and-fulfillmentservice-resources
(Note these documents use GraphQL for its examples, but everything listed here you can do in REST as well)
To learn more visit the Shopify Help Center or the Community Blog.
Here how you can Fulfill one complete order:
Fulfill all line items from 1 location:
POST /admin/api/2021-01/orders/{order_id}/fulfillments.json
{"fulfillment": { "location_id": 123456789 }}
This is the simplest way, you can also add various other options like tracking numbers. urls. etc ...
Hi,
Thanks for the answer, i have a doubt, the location_id is a mandatory field?
Does this work for you? I've been trying this exact request to fulfill all items on an order, and I always get a blank response. Other actions work, just not this one.
I've also tried to get a list of fulfillments, and it always comes up
{"fulfillment_orders":[]}
I think I need to create a fulfillment_order for the order first, but I cannot find out how to do this.
Any advice on how to create a fulfillment_order for an order?
I was looking for the same thing and finally figured it out. You need to query the fulfillment_orders endpoint [GET] for the order.
myshopify.com/admin/api/2021-07/orders/{order_id}/fulfillment_orders.json
Though, seemingly counter-intuitive, the FulfillmentOrder appears to already exist, with just the presence of an unfulfilled order.
note: nulled out values for obvious reasons
Please note that you must have the `read_merchant_managed_fulfillment_orders` permission to see the FulfillmentOrders.
I still don't get how to actually fulfill the fulfillment order.
In the documentation, I only found an option to send a fulfillment request to a third service party.
Could someone elaborate on which requests to make once you have the fulfillment order id?
We're also stuck on this. I feel like the documentation could do with a tutorial when it comes to order fulfillment and/or provide an easier alternative for apps that don't necessarily care about every aspect of the fulfillment process.
What we've been able to achieve so far:
We're also under the impression that the API documentation is in some kind of transition at the moment. Broken links, half written pages, links to the graphQL documentation from the REST documentation... Up until recently the migration guide to fulfillment orders API (away from the fulfillment API) page just returned a 404.
In other places we read comments about how we should be using REST and GraphQL together to get where we need to be, which I would like to avoid. That's like asking people to buy 2 cars if they also want to be able to drive uphill. We've been using the Shopify REST API for a couple of years now and would like to just expand our integration with Shopify to also include marking orders as fulfilled and updating tracking numbers on the orders. I figured that was going to be an quick and easy task. I couldn't have been more wrong.
Could you share how you've managed to acquire the fulfillment and accept it please? I've managed to get the list of fulfillment ID's, but if I try to just accept them it tells me they must be of API type and if I follow the Shopify guide of creating my own fulfillment service and then try to move the order to it I get told that the location doesn't stock these items?
@dj_gerbil
It seems that Shopify has two fulfillment mechanisms:
- an old one, which is documented on https://shopify.dev/api/admin-rest/2022-04/resources/fulfillment#top. This is the one we ended up using, at least for now. About half of the documentation of this api was missing until about a week ago, which was quite confusing to say the least.
- a new one, which is documented on https://shopify.dev/api/admin-rest/2022-04/resources/fulfillmentorder#top. We were unable to mark an order as "fulfilled" in the Shopify backend using this api endpoint however, we will have to come back to it later.
If you get the message "must be of API type" then I suppose you are trying to use the newer fulfillmentOrder mechanism. In that case you indeed need to create a fulfillment service using the api (as documented on https://shopify.dev/api/admin-rest/2022-04/resources/fulfillmentservice#post-fulfillment-services).
Note that each _fulfillment service_ automatically has a _location_ attached to it. This happens when you create the fulfillment service through the Shopify api.
Now, if you want to fulfill an order from a _fulfillment service_, and the products are **not** stocked at the location of your fulfillment service, you need to automatically _relocate_ the products. To do so, try to include the following keys in your api request:
{
relocate_if_necessary: true,
disconnect_if_necessary: true,
}
For example, to update the inventory at Shopify for a given product, do a POST to https://my-shop.myshopify.com/admin/api/2022-04/inventory_levels/set.json (as documented on https://shopify.dev/api/admin-rest/2022-04/resources/inventorylevel#post-inventory-levels-set) with the object:
{
relocate_if_necessary: true,
disconnect_if_necessary: true,
location_id: id_of_a_shopify_location, // get this from your fulfillment service object
inventory_item_id: inventoryItemId, // see https://shopify.dev/api/admin-rest/2022-04/resources/inventoryitem#top
available: qty
}
Hope this helps. Good luck 🙂
Hi, thanks for that. You're right, I'm trying to use the newer one as, like you, I can't see the point in writting code for a system they're discontinuing soon.
So, I just want to check I'm understaning you correctly, do I essentially need to send an inventory update to every product to add the relocate_if_necessary and disconnect_if_necessary keys?
Thanks
So, I just want to check I'm understanding you correctly, do I essentially need to send an inventory update to every product to add the relocate_if_necessary and disconnect_if_necessary keys?
I'm sorry for the confusion, my last example was not related to a fulfillment api call. I meant to say that, whenever you try to update a fulfillmentOrder, you might try to add those two records to your request body:
relocate_if_necessary: true,
disconnect_if_necessary: true,
If that does not work then we will not be able to assist further at this time - as I mentioned, we use the old fulfillment system for the time being.
No problem, it's confusing enough as they seem to make it as hard as possible to do.
Okay, so with the info you gave me and a lot of playing around I've managed to accept an order for fulfillment.
The :
relocate_if_necessary: true,
disconnect_if_necessary: true,
actually have to be done within an inventory update, so in my case I just wrote a script that got all the products from the API and just set them to the same amount but with the 2 parameters:
$set_variant = array(
"location_id" => wholesale_Location_ID,
"inventory_item_id" => $variant['inventory_item_id'],
"available" => $variant['inventory_quantity'],
"relocate_if_necessary" => true,
"disconnect_if_necessary" => true
);
Once I'd done that, I was able move the order to the Fulfillment service I'd created.
Then I had to do a call to:
api/2022-04/fulfillment_orders/".$fulillment_id."/fulfillment_request.json
and finally a call to:
api/2022-04/fulfillment_orders/".$fulillment_id."/fulfillment_request/accept.json
Now the only problem is I can't find anything anywhere in the documents to mark it as fulfilled.
I know you said you're using the old system for now, so I thought I'd share this here in the hope it helps yourselves or someone else to get to a similar point 🙂
How to retrieve the location id if we have multiple locations for a store.
There is a notice on Fulfillments API:
The Fulfillment resource will be deprecated. Use the FulfillmentOrder resource instead.
Hi @jam_chan
Have you got any solution on how to order mark as fulfilled using Rest API?
Can anyone please help to performance fulfill orders using by Rest API?
I struggle at the same point.
Either i'm too stupid, blind or it's too obvious 🙂
Workflow:
https://shopify.dev/docs/apps/fulfillment/order-management-apps#statuses
API-Reference From the fulfimmentService-Perspective:
I miss something like "Finish", "Complete" oder "Fulfill" here.
In Shopify-Admin there is a possibility to "Fulfill items".
The Buttons href-attribute is implicating, that there could be a "fulfill" API-endpoint with this REST-Schema:
/orders/{orderId}/fulfillment_orders/{orderFulfillmentId}/fulfill?locationId={serviceLocationid}
Where is the API-documentation for the "Fulfill Items"-Action? For me it would be logical, that like "accept" (https://shopify.dev/docs/api/admin-rest/2023-01/resources/fulfillmentrequest#post-fulfillment-orders...) i could just use a similar endpoint "complete" or something.
Hi @BfE 👋
I wouldn't recommend referring to the Admin dashboard fulfillment button to structure your API fulfillment workflow since your app will not have the same permissions as being logged into the Admin under a user.
We appreciate all the feedback, and to provide some context, our current workflow ensures we provide fulfillment services with only the data that is required to execute the service. Each fulfillment service can fetch all required data from their assigned fulfillment orders. These fulfillment orders can then be accepted, so that the service can create a fulfillment.
For apps managing fulfillments as an order management app, using GraphQL will help limit the number of requests required with a query like the below:
{
order (id: "gid://shopify/Order/123"){
fulfillmentOrders (first:10){
nodes { ... }
}
}
}
Hope that helps!
Developer Support @ Shopify
- Was this reply helpful? Click Like to let us know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog
Hi everybody!
Had the same issue with the official documentation and REST APIs examples. Found a solution to the generation of a fulfillment via API in the following way:
- Do a POST to /admin/api/2022-01/orders/{{order_id}}/fulfillments.json.
- Body params like:
{
"fulfillment": {
"notifyCustomer": false,
"location_id": 48685121239,
"tracking_company": "DHL",
"tracking_number": "123123",
"tracking_url": "trackingdhl.com/tracking/123123"
}
}
- "location_id" is a required field.
- If the line items in the fulfillment order are not specified, all items in the order will we fulfilled in the same way.
Hope this helps and everyone here can move forward with the shipping generation!
Regards
Awesome, thanks for finding that!
I had better luck using "notify_customer" rather than "notifyCustomer" here.
This helped us out a great deal!
We've also discovered that this only works with manually created locations (such as the location created when first creating the store). Fulfillment Service locations will not mark the order as fulfilled.
It pains me to have to use something deprecated but hey...
Hi @marianobotti ,
Many thanks for your example, that structure is very different from the example in the documentation, but it got us nearer to getting it working.
It complains that line_items_by_fulfillment_order is mandatory, but then using the example give in the documentation just results in {"errors":"Not Found"}. Do you have any further advice? Many thanks,
I think it's to do with the order ID. The GraphQL uses the gid but this API uses id. Grabbing the number from the end of the gid results in the not found error. Using the gid string results in {"errors":{"fulfillment_order_id":"expected String to be a Integer"}}. Using the short order number displayed with the order in Shopify also results in {"errors":"Not Found"}. What other numbers are there to try?!
Yes, I indeed found the same solution as @marianobotti. I am only passing the location_id parameter currently.
The next step which I am trying to figure out is to only fulfill certain items within an order. Haven't been able to find a solution for that yet.
@vboost This is the code that lets you pass along the line items & calculate backorders.
{
"fulfillment": {
"order_id": {Order: Id},
"tracking_company": "{Carrier}",
"location_id": 18014334875,
"line_items": [
{
"id": {Line Items: Id},
"variant_id": {Line Items: Variant Id},
"quantity": {Quantity Shipped}
}
],
"tracking_number": {Fulfillments: Tracking Number},
"tracking_url": "{Tracking URL}",
"notify_customer": true
}
}
So... has anyone figured this out? I've been scanning as many docs as I can to find out how to complete and fulfill FulfillmentOrders so I can migrate over, but I haven't found anything.
@zachsitka I've been searching but the references are not the same. It's frustrating.
Couldn't agree more. I think it's unhappy about one of the IDs, we've tried all sorts of IDs, including the ones from the GraphQL version which works just fine. It returns a 404 not found, but its unhelpful error message doesn't say what's not found. If we replace an ID with a string it is more helpful and returns a 200 with the error message that the parameter should have been an integer, so I believe the /admin/api/2022-10/fulfillments.json URL is correct. It's not finding something internally but without any debug information at all it's impossible to see what.
So here's what this whole thread has led me to..
If you keep your mouse pointer on the Fulfill Items, it shows a Fulfillment ID, which i have not been able to find with any request.
This (green circle) ID if used in the API POST request:
{"fulfillment":{"line_items_by_fulfillment_order":[{"fulfillment_order_id":5953813709030}]}}
You get what we all have been looking for!
Hopefully, someone here will be able to find out a way to find this ID
PS: I am not a coder, I use a zapier alternative Pabbly Connect to automate small stuff on my store.
Has anyone managed to find out where you get the Fulfillment Order ID?
Without having to mouse-hover over a piece of the Shopify UI like @technokul found out.
EDIT: Ok, I found it:
So what used to be a simple API call to send an order fulfilled and trigger an order shipped email has turned in to this? What happened to the principle of not dumping old APIs in a way that left current API users high and dry. Offer fulfilment IDs, if the order requires it, but why not offer either fulfilment IDs, or order ID if the entire order is fulfilled?
And presumably the fulfilments need to be created in the first place so the order can be queried for them so the order can then be marked as fulfilled? One step becomes three, unless I've missed some others!
@IGIT I've not changed anything about my app yet, or any settings in Shopify, and when I queried the API endpoint for getting the Fulfillment Orders for an Order (even an old Order), it returned a Fulfillment Order.
So I guess Shopify is creating them automatically? (I believe so, if I understand the docs correctly).
But yeah, now I have to make n+1 calls to the Shopify API to get Fulfillment Orders of a single Order before I can then POST a Fulfillment back. Definitely not an upgrade for me at least.
Hi @Robert_Tolton Many thanks for your reply. I can see that the object is created automatically, but until the order has been fulfilled it is empty. Currently for us this is a paradox, in order to see the fulfilment IDs we need first to fulfil the order, but having fulfilled the order we are barred from making further changes. At the moment we just use the old GraphQL fulfillmentCreate call, so for us it's going to be an n+2 situation + a fair amount of new app development in dealing with new message transmission, response handling and error correcting, three API calls is not very atomic!
The trouble with the old GraphQL call is that it's nearly impossible to get the correct carrier to the customer, the enum values are opaque and seem very incomplete. In the v2 call it seems that you can just add free-form text for the carrier and it's URL, which is great, except now they've switched to using these fulfillment IDs, presumably so the order can be dispatched in parts. However, for us, for whom this will probably never happen, it's a retrograde step. Why can't we just have a v2 version of a total order fulfilled? Maybe because the Shopify backend is not capable of creating fulfilment IDs and the fulfilling them? Furthermore, we've yet to see this free-form text approach actually work!
and all this will need to be done on a live shop!!
@IGIT I'm confused by what you mean when you say it's empty? I fetched the Fulfillment Orders of some Orders that haven't been touched yet, and all the data was there.
@Robert_Toltonno, I can't reproduce it any more either, maybe I landed on an order which had been cancelled or rejected, the trouble with trying to debug and develop against a live system. I should have tested with more than one order ID.
I have a problem. My JSON is structured properly, and it is going through, it is not failing. However, it is not adding the tracking info to the orders and it is not showing the items as shipped. It's pretty frustrating especially when the old API was so simple - and it was working.
Here is what I am doing:
GET
https://xxx.myshopify.com/admin/api/2022-10/orders/{Order: Id}/fulfillment_orders.json
Which is sending me:
api.id, api.shop_id, api.order_id, etc.
api.id appears to be the fulfillment_order_id as api.order_id is the order_id.
API: https://xxx.myshopify.com/admin/api/2022-10/fulfillments.json
JSON:
{ "fulfillment": { "line_items_by_fulfillment_order": [{ "fulfillment_order_id": {api.id}, "fulfillment_order_line_items": [{Line Items Fulfillment Array}] }], "tracking_info": { "company": "{Fulfillments: Tracking Company}", "number": {Fulfillments: Tracking Number}, "url": "{Tracking URL}" }, "notify_customer": false, "location_id": {api.assigned_location_id} } }
I have checked and refreshed the app permissions, so that does not seem to be the issue. I am quite frustrated by this. @IGIT is this what you meant as "empty"? That the data somehow isn't flowing through to the site?
No, I've not got that far yet, but l also need the tracking_info. The whole reason I stumbled across the v2 API and eventually to this thread was that the current GraphQL fulfillmentCreate is pretty random as to what tracking company actually gets stored against each order. Sadly it doesn't look like v2 is going to be any better and is more complicated to use. I used to work for a company that consumed and published APIs, they had two basic rules, first, never, EVER, develop or test on a live server and secondly, ALWAYS test the API to death before releasing it. Is there a sandbox API for Shopify so that we can develop and test against a non-live server? and did Shopify actually test this API or are we the beta testers?!
Hello my https://fragrance-lodge.myshopify.com/admin/api/2020-04/orders/{Order: Id}/fulfillments.json is not working/has been deprecated armageddon peeps!
With a lot of help from @Ralph-HA @IGIT @SCHLUE1 and others, it is solved.
THIS PROCESS IS WORKING!
Pay attention to the details, as a tiny detail can mess you up for days...ask me how I know...
Step One: GET Fulfillment_Orders API
This step creates the fulfillment and returns the fulfillment_order_id that you need.
GET
https://xxx.myshopify.com/admin/api/2022-10/orders/{Order : Id}/fulfillment_orders.json
Note: Make sure you use fulfillment_orders and not fulfillments.
This will send you: api.id, api.shop_id, api.order_id, etc., which you need for the next step. Use the api.id for the next step as it is the fulfillment_order_id, do not use api.order_id as it is the order_id.
Step two: POST to API (or use your method of choice)
API: https://xxx.myshopify.com/admin/api/2022-10/fulfillments.json
JSON:
{
"fulfillment": {
"line_items_by_fulfillment_order": [{
"fulfillment_order_id": {api.id},
"line_items": [{Line Items Fulfillment Array}]
}],
"tracking_info": {
"company": "{Fulfillments: Tracking Company}",
"number": {Fulfillments: Tracking Number},
"url": "{Tracking URL}"
},
"notify_customer": false,
"location_id": {api.assigned_location_id}
}
And that's it! You are done.
@colleencthat's fantastic work. I'll use this approach and hope it works for me. However, it shouldn't be down to us, the community and end users, to crack this. Surely we should expect better from Shopify's documentation, after all it is their API!
Sorry to be a little slow with this. You say "api.id, api.shop_id, api.order_id, etc., which you need for the next step", but only the api.id is used in your payload, is this correct? Do you know if you can miss the "line_items" field out? The "tracking_info" fields are all formatted as structures, "{....}", is this correct, or are they just strings? Thank you again for your help
Hi @IGIT happy to help - we are all in this together in a way.
The api returns a bunch of columns, you will need the api.id and maybe the location column which is closer to the end of the columns returned. Because I only have one location, I do not need to use that field, but if there is more than one fulfillment location, I expect that column may be required.
You do not need to add the line-items field if you are looking to fulfill the entire order. In my case, I may run into backorders. My client is pulling from the inventory in two disconnected ways - ask me how I feel about that!! So I have to do all kinds of crazy extra steps including refreshing inventory levels a few times a day.
The code works perfectly without the line_items field. I have already tested it that way when I though my line items array was causing my failures.
I would think you can remove the {...} around the tracking number - I think that was an oversight on my behalf.
Hi @IGIT I have finally sorted everything out and have it working properly.
Step One:
GET
https://xxx.myshopify.com/admin/api/2022-10/orders/{Order : Id}/fulfillment_orders.json
Step Two: Parse the return for the following details
In the return, look for the column with the array in it and parse out the array. There are several columns of data needed from this array. Some of them are also outside of the array, but some are not. The array is called "api.fulfillment_orders line_items". The columns you need, depending on your requirements, are:
api.fulfillment_orders id = fulfillment_orders_id
api.fulfillment_orders assigned_location_id = location_id
api.fulfillment_orders order_id = order_id
api.fulfillment_orders line_items variant_id = variant_id
api.fulfillment_orders line_items id = line item identifier needed for your ship quantity array
Admittedly, there may have been something with the way I pulled in this data that caused the array to occur. So you may not have to parse out the data, but in case you do, those are the details. After that, put together an array with your shipping info (only if you need it).
{"id": {api.fulfillment_orders line_items id}, "quantity": {Quantity Shipped}}
Step Three: POST to API (or use your method of choice)
API: https://xxx.myshopify.com/admin/api/2022-10/fulfillments.json
JSON:
{
"fulfillment": {
"line_items_by_fulfillment_order": [{
"fulfillment_order_id": {api.fulfillment_orders id},
"fulfillment_order_line_items": [{Line Items Fulfillment Array}]
}],
"tracking_info": {
"company": "{Fulfillments: Tracking Company}",
"number": {Fulfillments: Tracking Number},
"url": "{Tracking URL}"
},
"notify_customer": false,
"location_id": {api.fulfillment_orders assigned_location_id}
}
}
Hope this is helpful for you!
Many thanks @colleenc. This has now risen to the top of my tickets so I'll be using your data soon.
Hi @colleenc, many thanks again, I now have a response, but only an error as the orders have already been dispatched; testing this against a live store is just so much fun! Would you have an example of a valid response example you could post?
Hi @colleenc it worked, well kind of! Don't know if it's the same for you. If you set company to "foo" and url to "https://foo.bar", in the tracking info, then the customer's order details has the Carrier set to Other, not very useful, but the URL is correct. If you set company "DPD", i.e. one that's in Shopify's drop down the company is now correct, but there is no URL displayed, which in the case of DPD Germany is even less useful. We are a test department that can't raise tickets!
I was wrong, what the customer sees is correct, the above only applies to an admin attempting to edit the tracking info.