A Shopify unfulfilled orders daily digest is a scheduled n8n workflow that checks your store every morning, finds every order that has been sitting open for more than 24 hours, and emails you one clean summary. Instead of scrolling the Orders tab to see what slipped, you get a single message with the order name, customer, item count, value, and how long each one has been waiting. This guide builds it from scratch and gives you the ready-to-import template.
What it does
Every store leaks a few orders. A payment clears late, a shipment is half-picked, a note gets missed, and an order sits unfulfilled while the customer waits and wonders. By the time you notice, the order is two or three days old and the review risk is real. This workflow turns that from a thing you have to remember into a thing that arrives in your inbox.
Once a day at 8:00, the workflow asks Shopify for open orders that are still marked unshipped and were created more than 24 hours ago. It formats them into a short list and sends you an email digest. If everything is fulfilled, you get a one-line all-clear so you know the check actually ran. No dashboards to open, no app to babysit, just a daily line of sight into the orders that need a human.
Why it beats the default
Shopify already shows unfulfilled orders in the admin, and it can email you on every new order. Neither of those solves the real problem. The Orders tab needs you to remember to look, and per-order emails train you to ignore them because most orders are fine. What you actually want to know is the exception: which orders are late right now.
A daily digest is the right shape for that. It batches, so you get one message instead of fifty. It filters by age, so a brand-new order placed an hour ago never nags you. And it is a pull, not a push, so it keeps working even if a Shopify notification setting gets toggled off. The result is a report you will actually read, once a day, that maps exactly to the work sitting in your queue.
This is the digest counterpart to a per-order fulfillment delay alert. Use the alert when you want an instant ping the moment one order goes late; use this digest when you want a calm once-a-day roll-up of everything still open.
What you need
- An n8n instance (cloud or self-hosted, version 1.0 or newer).
- A Shopify custom app access token with
read_ordersscope. If you have not connected Shopify to n8n yet, follow the 2026 Dev Dashboard connection guide first, then come back. - A Gmail account (or any email node) for delivery. Swap in Telegram or Slack if you prefer.
Build time is about 20 minutes from scratch, or under 5 minutes if you import the template at the end and just attach your two credentials.
Node-by-node list
Five nodes, one straight line, no branches. Here is the whole thing before we configure it:
[Daily 08:00 schedule] -> [Cutoff timestamp (24h ago)] -> [Get unfulfilled orders]
-> [Build digest] -> [Send digest email]
- Daily 08:00 schedule (Schedule Trigger) fires the workflow once a day.
- Cutoff timestamp (24h ago) (Set) calculates the moment 24 hours in the past.
- Get unfulfilled orders (HTTP Request) pulls open, unshipped orders created before that cutoff from the Shopify Admin API.
- Build digest (Code) turns the order list into a readable summary and a count.
- Send digest email (Gmail) delivers the summary to your inbox.
Step-by-step build
1. Daily 08:00 schedule (Schedule Trigger)
Add a Schedule Trigger node. Set the rule to a Cron expression of 0 8 * * * so it runs at 08:00 in your instance timezone. Pick whatever hour lets you act on the list before you get busy. If you want two checks, add a second cron line such as 0 14 * * * for an afternoon sweep.
2. Cutoff timestamp (24h ago) (Set)
Add a Set node. Create one assignment named cutoff, type String, with this expression as the value:
={{ $now.minus({ hours: 24 }).toISO() }}
This produces an ISO 8601 timestamp for exactly one day ago, for example 2026-08-30T08:00:00.000-05:00. Shopify’s API accepts that format directly. To digest older orders, change 24 to 48; to catch same-day stragglers, drop it to 12.
3. Get unfulfilled orders (HTTP Request)
Add an HTTP Request node. Configure it like this:
- Method:
GET - URL:
https://YOUR_STORE.myshopify.com/admin/api/2026-04/orders.json - Authentication: Predefined Credential Type, then Shopify Access Token API, and select your credential.
- Send Query Parameters: on. Add these five:
| Parameter | Value | Why |
|---|---|---|
status |
open |
Ignore archived and cancelled orders |
fulfillment_status |
unshipped |
Only orders with nothing fulfilled yet |
created_at_max |
={{ $json.cutoff }} |
Only orders older than 24 hours |
limit |
250 |
Maximum orders per page |
fields |
id,name,created_at,total_price,currency,customer,line_items |
Return only the fields the digest uses |
The response is a JSON object with an orders array. A single unfulfilled order looks roughly like this:
{
"id": 5541203112,
"name": "#1042",
"created_at": "2026-08-29T15:22:10-05:00",
"total_price": "84.00",
"currency": "USD",
"customer": { "first_name": "Emily", "last_name": "Rodriguez" },
"line_items": [ { "quantity": 2 } ]
}
unshipped value means “nothing fulfilled yet”. Orders that are partially fulfilled use partial. If you want those flagged too, run the request twice or use fulfillment_status=any and filter in the next node.4. Build digest (Code)
Add a Code node in Run Once for All Items mode. It reads the orders array, keeps the unfulfilled ones, and builds one summary line per order plus a subject and a count.
const resp = $input.first().json;
const orders = Array.isArray(resp.orders) ? resp.orders : [];
// Keep only orders that are still unfulfilled (fulfillment_status null/empty).
const stale = orders.filter(o => !o.fulfillment_status);
const lines = stale.map(o => {
const created = new Date(o.created_at);
const hours = Math.floor((Date.now() - created.getTime()) / 3600000);
const name = o.name || ('#' + o.id);
const cust = o.customer
? ((o.customer.first_name || '') + ' ' + (o.customer.last_name || '')).trim() || 'Guest'
: 'Guest';
const units = Array.isArray(o.line_items)
? o.line_items.reduce((s, li) => s + (li.quantity || 0), 0)
: 0;
const money = (o.total_price || '0') + ' ' + (o.currency || '');
return `${name} | ${cust} | ${units} item(s) | ${money.trim()} | waiting ${hours}h`;
});
const count = stale.length;
const subject = count > 0
? `Shopify: ${count} unfulfilled order(s) over 24h`
: 'Shopify: all orders fulfilled, nothing waiting';
const digest = count > 0
? `You have ${count} order(s) unfulfilled for more than 24 hours:\n\n` + lines.join('\n')
: 'Good news: every open order is fulfilled. Nothing has been waiting more than 24 hours.';
return [{ json: { count, subject, digest } }];
Because the node always returns one item, the email always sends, even on a clean day. That is what gives you the all-clear message and confirms the check ran.
5. Send digest email (Gmail)
Add a Gmail node set to Message, Send. Fill in:
- To: your own address (or an ops alias).
- Subject:
={{ $json.subject }} - Email Type: Text.
- Message:
={{ $json.digest }}
Save the workflow and toggle it Active. A finished digest lands like this:
Subject: Shopify: 3 unfulfilled order(s) over 24h
You have 3 order(s) unfulfilled for more than 24 hours:
#1042 | Emily Rodriguez | 2 item(s) | 84.00 USD | waiting 27h
#1039 | Michael Chen | 1 item(s) | 49.00 USD | waiting 41h
#1035 | Sarah Thompson | 4 item(s) | 156.00 USD | waiting 63h
Common mistakes
- Wrong fulfillment value. The API uses
unshipped, notunfulfilled, in the query string. Using the wrong word returns everything and floods the digest. - Timezone drift on the cron. The Schedule Trigger runs in your instance timezone. Set the timezone in the workflow settings so 8:00 means 8:00 where you are, not UTC.
- Reading the array wrong in Code. The Shopify response wraps orders in an
orderskey. Referenceresp.orders, not the top-level object, or the loop finds nothing. - Missing read scope. If the request returns a 401 or 403, your access token lacks
read_orders. Regenerate it with that scope from the Shopify Dev Dashboard. - Assuming empty means broken. If your store genuinely has no late orders, you get the all-clear, not an error. That is success.
Cost at realistic volume
This workflow is effectively free to run. It executes once a day, which is 30 executions a month, well inside every n8n plan including self-hosted. Each run makes a single Shopify API call and one Gmail send, so you never approach Shopify’s rate limits or Gmail’s daily send cap. There is no AI model call, no paid third-party service, and no per-message fee anywhere in the chain. If you add a second daily run, you are still at 60 executions a month. The only real cost is the 20 minutes to build it, which the template removes.
Ready-to-import template
The guide above is free to follow, start to finish. If you would rather not build it by hand, the downloadable template is the exact five-node workflow, validated and ready to import. Attach your Shopify and Gmail credentials, set your email address, and switch it on. Want it done for you end to end? See our done-for-you setup service.
Instant download · Works on n8n Cloud and self-hosted
Frequently asked questions
How does the workflow know an order is unfulfilled for more than 24 hours?
The Set node computes a cutoff timestamp 24 hours before the run. The Shopify request then asks only for open orders with fulfillment_status unshipped that were created before that cutoff, so nothing newer than a day ever slips into the digest.
Will I get an email when there are no unfulfilled orders?
Yes. The Code node always returns one item, so the Gmail node always sends. When nothing is waiting you get a short all-clear message. If you prefer silence on quiet days, add an IF node after Build digest that only sends when the count is greater than zero.
Can I send the digest to Telegram or Slack instead of Gmail?
Yes. Replace the Gmail node with a Telegram or Slack node and map the same digest text into the message field. The Code node output does not change, so the rest of the workflow stays exactly as it is. Telegram is a good fit for a quick phone glance.
What if I have more than 250 unfulfilled orders?
The request caps at 250 orders per page, which covers almost every store for a 24-hour backlog. If you routinely exceed that, enable pagination on the HTTP Request node or shorten the age window so the digest stays readable and the page limit is never reached.
Does this change anything in my Shopify store?
No. The workflow only reads orders through the Admin API and sends you an email. It never edits, fulfills, or cancels anything, so it is safe to run daily. The single action it takes is delivering the summary to your inbox each morning.