Shopify Flow is an ecommerce automation platform that enables you to automate tasks and processes within your store and across your apps.
This is my attempt so far at this:
I've created a custom app that has all the access scopes required for https://shopify.dev/docs/api/admin-graphql/2024-07/mutations/fulfillmentCreateV2,
used the access token inside the Shopify Flow http request and I tried to test this on a particular order where I fulfill the digital product.
Here's the body of the request:
{
"query": `mutation fulfillmentCreateV2($fulfillment: FulfillmentV2Input!) {
fulfillmentCreateV2(fulfillment: $fulfillment) {
fulfillment {
id
status
}
userErrors {
field
message
}
}
}`,
"variables": {
"fulfillment": {
"lineItemsByFulfillmentOrder": {
"fulfillmentOrderId": "gid://shopify/FulfillmentOrder/5779802587373",
"fulfillmentOrderLineItems": {
"id": "gid://shopify/FulfillmentOrderLineItem/44034857402605",
"quantity": 1
}
}
}
},
}
Now I am facing 2 problems:
- I tested this in Postman and it gives me a syntax error:
I also tried removing whitespace from my request as @paul_n suggested in this thread: https://community.shopify.com/c/shopify-flow-app/fulfilling-a-specific-product-using-a-http-request-...
And I get the same error for this request:
{"query":"mutation fulfillmentCreateV2($fulfillment: FulfillmentV2Input!) {fulfillmentCreateV2(fulfillment: $fulfillment) {fulfillment {id status} userErrors {field message}}}","variables":{"fulfillment":{"lineItemsByFulfillmentOrder":{"fulfillmentOrderId":"gid://shopify/FulfillmentOrder/5779802587373","fulfillmentOrderLineItems":{"id":"gid://shopify/FulfillmentOrderLineItem/44034857402605","quantity":1}}}}}
Another thing I tried was to adapt his response to my needs resulting into this query:
{"query":"mutation fulfillmentCreateV2($fulfillment: FulfillmentV2Input!) { fulfillmentCreateV2(fulfillment: $fulfillment) { fulfillment { id } userErrors { field message } } }","variables":{"fulfillment":{"lineItemsByFulfillmentOrder":[{% for fo in order.fulfillmentOrders %}{"fulfillmentOrderId":"{{fo.id}}","fulfillmentOrderLineItems":[{"id":"44034857402605","quantity":1}]}{% unless forloop.last %},{% endunless %}{% endfor %}]}}}
and in Shopify flow it still throws an error that "order" cannot be recognized(even though I have added read_orders and write_orders to my access scope) and in Postman it gives me the same syntax error -
Don't use that action to call the Shopify API. Use "Send Admin API request" instead. It's WAY easier.
I'm making a template similar to this right now. Here's the mutation JSON for the action:
{
"fulfillment": {
"notifyCustomer": false,
"lineItemsByFulfillmentOrder": [
{
"fulfillmentOrderId": "{{fulfillmentOrder.id}}",
"fulfillmentOrderLineItems": [
{
"id": "{{lineItemsForeachitem.id}}",
"quantity": {{lineItemsForeachitem.totalQuantity}}
}
]
}
]
},
"message": "Digital item to be auto-fulfilled"
}
The workflow:
The main idea - if the whole fulfillment order doesn't require shipping -> mark that as fulfilled
Otherwise, check if any fulfillment orders have lineItems that could be auto-fulfilled (usually this means that "this is a physical product" is checked for some reason). It will then fulfill each of those lineItems separately.
@paul_n
And If I want to modify orders from the past as well(because there are like 3months of orders that have accumulated), I guess I should change the initial trigger. Any suggestions of what I could use instead to trigger the same flow for past orders?
Also, it seems I don't need a custom app anymore if I use Shopify's "Send Admin API request"
You could potentially use Order created and then run that manually, but you would need to change it up a bit. There is also a "Get fulfillment order data" action where you can query by "updated_at" and "status". But you would need to loop over those fulfillment orders using foreach and then in the action, you could loop over the lineItems to find those that are fulfillable and output the JSON for that action.
I tried going for your second idea using the same trigger:
However, I am not sure how I can query by status and updated_at at the same time. If I use a custom query and by updatedAt and status, it adds these 2 query as you would add them in Liquid:
Also, I can't seem to be chaining from for each item in getfulfillmentOrderData to for each lineItem in item? Any ideas of how I can get around this?
Read the help right under that box. Or look at one of the queries already there for ideas.
I've tried this flow:
It's not as efficient because it obviously takes time to go through the orders considering the trigger and my filter conditions, but it should eventually get the job done.
I basically check if the fulfillment orders with open status contain an item with "eh-pkgins" sku, then I send a request to fulfillmentCreateV2
However, think I am misunderstanding how I should build my request body @paul_n .
I am trying to use a hardcoded value for my specific item, that throws an error because my lineitem isnt recognized:
I tried with both the product_id and variant id and it threw the same error:
It's the ID at fulfillmentOrder / line Items / id
It's not a variant or product id or even an order/lineItems/id
Ok, I understand, but how exactly do I fulfill only the item that I need?
I've looked into the docs for examples https://help.shopify.com/en/manual/shopify-flow/reference/actions/send-admin-api-request#examples on how I can achieve this. Turns out I can use liquid in combination with the json filter so I tried this:
{% assign sku_to_fulfill = "ek-pkgins" %}
{% assign line_item = fulfillmentOrder.lineItems | where: "sku", sku_to_fulfill | first %}
{
"fulfillment": {
"notifyCustomer": false,
"lineItemsByFulfillmentOrder": [
{
"fulfillmentOrderId": "{{fulfillmentOrder.id}}",
"fulfillmentOrderLineItems": [
{
"id": "{{ line_item.id | json }}",
"quantity": 1
}
]
}
]
},
"message": "Digital item to be auto-fulfilled"
}
However, this returns null resulting into an error when sending the request.
That lineItem field is not a single lineItem but a list of them. So you need to loop over them
which one is a list of lineItems @paul_n ?
{% assign line_item = fulfillmentOrder.lineItems | where: "sku", sku_to_fulfill | first %} this?
This should get the first element, however I tried a classic loop and it has the same error:
{% assign sku_to_fulfill = "ek-pkgins" %}
{% for item in fulfillmentOrder.lineItems %}
{% if item.sku == sku_to_fulfill %}
{% assign line_item = item %}
{% endif %}
{% endfor %}
{
"fulfillment": {
"notifyCustomer": false,
"lineItemsByFulfillmentOrder": [
{
"fulfillmentOrderId": "{{ fulfillmentOrder.id }}",
"fulfillmentOrderLineItems": [
{
"id": "{{ line_item.id | json }}",
"quantity": 1
}
]
}
]
},
"message": "Digital item to be auto-fulfilled"
}
Here you assign a list of lineItems to the variable "line_item", but I missed that you had a "first" in there unless you edited it since then.
{% assign line_item = fulfillmentOrder.lineItems | where: "sku", sku_to_fulfill | first %}
Anyway, I suspect that your sku is incorrect, making the result of this null. In one place you have `ek-pkgins` and another you have `eh-pkgins`.
In case anyone is still looking for a solution to this, I have a working flow that others might find useful. This applies to all items that are not physical, aka do not require shipping
Since some customers order both physical and digital items from our store at the same time, Shopify automatically splits the order into separate fulfillment orders (1 for shippable and 1 for not-shippable). First we check if at least one of fulfillment order / line items requires.Shipping is equal to false:
If that condition is not met, we move to the Otherwise track and log the line item product titles just for debugging purposes.
{% for lineItems_item in fulfillmentOrder.lineItems %}
{{lineItems_item.productTitle}}
{% endfor %}
Requires shipping
If the non-shippable fulfillment order is in the workflow, the check will be true and move on to this API request using the fulfillmentCreateV2 mutation:
{% assign non_shippable_items = "" %}
{% assign non_shippable_quantity = "" %}
{% for lineItems_item in fulfillmentOrder.lineItems %}
{% if lineItems_item.requiresShipping == false %}
{% assign non_shippable_items = non_shippable_items | append: lineItems_item.id | append: "," %}
{% assign non_shippable_quantity = non_shippable_quantity | append: lineItems_item.totalQuantity | append: "," %}
{% endif %}
{% endfor %}
{% assign non_shippable_items = non_shippable_items | strip | split: "," %}
{% assign non_shippable_quantity = non_shippable_quantity | strip | split: "," %}
{% assign last_index = non_shippable_items.size | minus: 1 %}
{
"fulfillment": {
"notifyCustomer": false,
"lineItemsByFulfillmentOrder": [
{
"fulfillmentOrderId": "{{fulfillmentOrder.id}}",
"fulfillmentOrderLineItems": [{% for item_index in (0..last_index) %}
{
"id": "{{ non_shippable_items[item_index] }}",
"quantity": {{ non_shippable_quantity[item_index] | strip }}
}{% if item_index < last_index %},{% endif %}
{% endfor %}
]
}
]
}
}
This will store all of the digital items in a variable and their quantity in a separate variable. Then when we get to the fulfillmentOrderLineItems, we loop through the values in those variables based on their index, and add a comma depending on whether there are more items in the index.
This creates a single fulfillment for all digital items in the fulfillment order. I think this method is a lot more hands off than using sku or collection or tag conditions. All digital items will have a false value for lineitems_item.requiresShipping, so might as well leverage that instead of maintaining and updating a list.
Thanks for sharing! Be advised that fulfillmentCreateV2 is deprecated and will likely no longer be supported in the future. The fulfillmentCreate mutation is currently available in the 2024-10 release candidate but Flow is still using 2024-01 and may not support that until later next year.
Hi RPiii, thanks for the heads up. Ideally this fulfillmentCreate support within Flow comes before fulfillmentCreateV2 is depreciated. Being left without that will be very disruptive for our workflow.
2m ago Learn the essential skills to navigate the Shopify admin with confidence. T...
By Shopify Feb 12, 2025Learn how to expand your operations internationally with Shopify Academy’s learning path...
By Shopify Feb 4, 2025Hey Community, happy February! Looking back to January, we kicked off the year with 8....
By JasonH Feb 3, 2025