Fulfill orders that contain digital items

Fulfill orders that contain digital items

wiredigital
Shopify Partner
14 1 0
I want to create a flow that fulfills all orders created from a specified time period to the present, as well as all future orders that have all items fulfilled except for one specific digital item.
I tried using the available Flow options, but the closest one, "Fulfill a digital item in an order," only works if the order contains only the digital item.
From forum discussions, it seems this can be achieved using an HTTP request, but I am unfamiliar with Shopify's API.
Could someone guide me through this process, including how to obtain the access token, which API endpoints to use, etc.?
From my research, it seems I need to start with the "Fulfillment order ready to fulfill" trigger and then use the HTTP request action to interact with the fulfillment API, fulfilling the digital product using its product variant ID. Once this flow runs, all orders that have all items except the digital product fulfilled should be completed, as the flow marks the digital product as fulfilled.
https://www.wiredigital.dev
Replies 18 (18)

wiredigital
Shopify Partner
14 1 0

This is my attempt so far at this:

dvmati_0-1721829376424.png

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:

            "message": "syntax error, unexpected STRING (\"query\") at [3, 5]",
I am not sure what is wrong with my query as its almost the exact same query from the docs.
- How do I scale this to go through all orders?
I've read in this thread the response of one of the Shopify Staff members making use of Liquid to mark all fulfillment orders as fulfilled:
https://community.shopify.com/c/shopify-flow-app/how-can-i-auto-fulfill-a-specific-product-with-an-h...
However, when I tried to use his example in combination with my case, the "order" inside for fo in order.fulFillmentOrders was marked as an error by Flow.
https://www.wiredigital.dev
wiredigital
Shopify Partner
14 1 0

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 - 

            "message": "syntax error, unexpected STRING (\"query\") at [1, 2]",
 
https://www.wiredigital.dev
paul_n
Shopify Staff
1584 171 364

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"
}
Paul_N | Flow Product Manager @ Shopify
- Finding Flow useful? Leave us a review
- Need Flow help? Check out our help docs.
- Building for Flow? Check out Flow's dev docs.
paul_n
Shopify Staff
1584 171 364

The workflow: 

Paul_N | Flow Product Manager @ Shopify
- Finding Flow useful? Leave us a review
- Need Flow help? Check out our help docs.
- Building for Flow? Check out Flow's dev docs.
paul_n
Shopify Staff
1584 171 364

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 | Flow Product Manager @ Shopify
- Finding Flow useful? Leave us a review
- Need Flow help? Check out our help docs.
- Building for Flow? Check out Flow's dev docs.
wiredigital
Shopify Partner
14 1 0

@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"

https://www.wiredigital.dev
paul_n
Shopify Staff
1584 171 364

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. 

Paul_N | Flow Product Manager @ Shopify
- Finding Flow useful? Leave us a review
- Need Flow help? Check out our help docs.
- Building for Flow? Check out Flow's dev docs.
wiredigital
Shopify Partner
14 1 0

I tried going for your second idea using the same trigger:

dvmati_0-1721862536622.png

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:

dvmati_1-1721862713897.png

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?

 

https://www.wiredigital.dev
paul_n
Shopify Staff
1584 171 364

Read the help right under that box. Or look at one of the queries already there for ideas.

Paul_N | Flow Product Manager @ Shopify
- Finding Flow useful? Leave us a review
- Need Flow help? Check out our help docs.
- Building for Flow? Check out Flow's dev docs.
wiredigital
Shopify Partner
14 1 0

I've tried this flow:

dvmati_0-1721932018541.png

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 .

dvmati_1-1721932257754.png

I am trying to use a hardcoded value for my specific item, that throws an error because my lineitem isnt recognized:

dvmati_2-1721932295039.png

dvmati_3-1721932332427.png

I tried with both the product_id and variant id and it threw the same error:

dvmati_4-1721932396709.png

 

https://www.wiredigital.dev
paul_n
Shopify Staff
1584 171 364

It's the ID at fulfillmentOrder / line Items / id

 

It's not a variant or product id or even an order/lineItems/id

Paul_N | Flow Product Manager @ Shopify
- Finding Flow useful? Leave us a review
- Need Flow help? Check out our help docs.
- Building for Flow? Check out Flow's dev docs.
wiredigital
Shopify Partner
14 1 0

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.

dvmati_0-1721948074313.png

 

https://www.wiredigital.dev
paul_n
Shopify Staff
1584 171 364

That lineItem field is not a single lineItem but a list of them. So you need to loop over them 

Paul_N | Flow Product Manager @ Shopify
- Finding Flow useful? Leave us a review
- Need Flow help? Check out our help docs.
- Building for Flow? Check out Flow's dev docs.
wiredigital
Shopify Partner
14 1 0

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"
}

 

 

 

 

https://www.wiredigital.dev
paul_n
Shopify Staff
1584 171 364

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`.

Paul_N | Flow Product Manager @ Shopify
- Finding Flow useful? Leave us a review
- Need Flow help? Check out our help docs.
- Building for Flow? Check out Flow's dev docs.

bryanhayes
Shopify Partner
2 0 0

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

 

bryanhayes_1-1727370730860.png

 

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:

bryanhayes_2-1727371366117.png

 

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.

RPiii
Shopify Staff
138 25 40

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.

bryanhayes
Shopify Partner
2 0 0

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.