A shopify inventory reorder point forecast google sheets n8n workflow turns 60 days of paid orders into a sales velocity for every SKU, compares that against stock on hand, and tells you which products to reorder this week and how many units to buy. Eight nodes, one Google Sheet, one Monday morning email. No forecasting app and no per-SKU fee. Below: the node map, the validated build, the mistakes that quietly produce wrong numbers, and the real cost.
Prefer to skip the setup? Grab the ready-made template and be running in under 10 minutes.
What it does
Every Monday at 7am the workflow pulls your paid orders from the last 60 days, counts the units sold per SKU, and divides by the window to get a daily sales velocity. It then reads current stock for every tracked variant and asks one question per SKU: at this rate of sale, will the shelf empty before a new delivery could arrive?
The answer comes from the reorder point, which is the velocity multiplied by your supplier lead time plus a safety buffer. If stock has fallen to or below that number, the SKU gets flagged with a suggested order quantity. Everything lands in a Google Sheet, one row per SKU, and the flagged rows arrive in your inbox as a table you can forward to a supplier.
Take a black tee that sold 60 units in 60 days, so one a day. Your supplier takes 14 days and you want a 7 day buffer, putting the reorder point at 21 units. Stock reads 14, already past the line, so the workflow tells you to order 14 units.
Why it beats the default
Shopify tells you how many units you have. It does not tell you how long they will last, and that is the number a purchase order actually needs.
The common substitute is a low stock alert on a fixed threshold, set once and never revisited. A fixed number means different things for different products. Ten units of something selling five a day is a two day emergency. Ten units of something selling five a year is nine years of dead capital. One threshold cannot describe both, so alerts cry wolf on slow movers while fast movers sell out in silence. The Shopify low stock alert is the right tool when you just want to know a shelf is thin. This is the other job: knowing when to buy.
Forecasting apps solve it for 30 to 200 dollars a month, often billed per SKU, and hide the arithmetic behind a dashboard. The arithmetic is division. Once your lead time and buffer are two numbers in one node, you can change the assumptions in ten seconds and watch the whole catalog rerank.
What you need
- An n8n instance, cloud or self-hosted. A free self-hosted install handles this comfortably.
- A Shopify store connected to n8n the 2026 way, with read access to orders and products. See the 2026 connection guide.
- A Google account for Sheets and Gmail, connected to n8n by OAuth.
- One empty Google Sheet with a header row, described in step 2 below.
- Inventory tracking switched on in Shopify for the products you care about. Untracked variants are skipped, because a variant with no stock number cannot have a reorder point.
- Your real supplier lead time in days. The workflow cannot derive this, and it decides whether the output is useful.
- Roughly 25 minutes.
No AI model is involved. A trailing average is arithmetic, and arithmetic does not need a language model. Anything selling you an LLM to divide units by days is selling you a calculator at a markup.
Node-by-node list
Eight nodes in one straight line, no branches.
Every Monday 7am (Schedule Trigger)
↓
Set planning inputs (Edit Fields, v3.4) lookback, lead time, review, safety, email
↓
Get paid orders (Shopify, order:getAll) returnAll, financialStatus=paid
↓
Get products (Shopify, product:getAll) returnAll, Execute Once = ON
↓
Compute reorder points (Code, run once for all items)
↓
Upsert forecast sheet (Google Sheets, appendOrUpdate, match on sku)
↓
Build reorder summary (Code, run once for all items)
↓
Email the reorder list (Gmail, send)
| Node | Type | Job |
|---|---|---|
| Every Monday 7am | Schedule Trigger | Fires weekly. The only entry point. |
| Set planning inputs | Edit Fields v3.4 | Holds the five settings you tune, so you never edit an expression to change a policy. |
| Get paid orders | Shopify | Pulls every paid order created since the lookback date. |
| Get products | Shopify | Pulls the catalog with current stock per variant. |
| Compute reorder points | Code | Counts units per SKU, derives velocity, reorder point, days of cover and order quantity. |
| Upsert forecast sheet | Google Sheets v4.5 | Writes one row per SKU, matched on sku so reruns update in place. |
| Build reorder summary | Code | Keeps only the flagged SKUs and builds the email table. |
| Email the reorder list | Gmail | Sends you the buy list. |
Step-by-step build
1 Add the schedule trigger
Create a new workflow and add a Schedule Trigger. Set the interval to Weeks, trigger day Monday, hour 7, minute 0. Weekly matches how purchasing actually works: you place orders in batches, not continuously, and the review period is part of the math in step 5.
2 Create the Google Sheet
Make a new sheet, name the tab Reorder, and put these headers in row 1, spelled exactly like this because the mapping matches on them:
sku | product | variant | stock | units_sold | velocity_per_day |
days_of_cover | reorder_point | suggested_qty | status | updated_at
Keep the sheet ID from the URL. It is the long string between /d/ and /edit.
3 Add the Set planning inputs node
Add an Edit Fields node and create five assignments: lookbackDays as a Number set to 60, leadTimeDays as a Number set to your supplier’s real turnaround, reviewPeriodDays as a Number set to 7 to match the weekly schedule, safetyDays as a Number set to 7, and reportEmail as a String set to your address.
Tip: Keep the buffer in days rather than units. Seven days of safety stock means seven days for every product, whether that is 70 units of a bestseller or one unit of a slow mover. A buffer in units would have to be retuned per SKU forever.
4 Pull the orders and the catalog
Add a Shopify node, resource Order, operation Get Many, Return All on. Under Options add Status any, Financial Status paid, and Created At Min set to this expression:
{{ new Date(Date.now() - $json.lookbackDays * 86400000).toISOString() }}
Then add a second Shopify node, resource Product, operation Get Many, Return All on. Open its Settings tab and turn on Execute Once. This matters more than it looks: an n8n node runs once per incoming item, and the orders node hands it thousands. Without Execute Once you would fetch the entire catalog thousands of times and hit the rate limit in seconds.
Filtering to paid orders at the source is what keeps the velocity honest. Abandoned checkouts and unpaid drafts are not sales, and letting them into the count inflates demand for products nobody bought.
5 Compute the reorder points
Add a Code node named exactly Compute reorder points, because two later nodes reference it by name. Set mode to Run Once for All Items. It counts units per SKU from the orders, then walks the catalog and joins the two.
const cfg = $('Set planning inputs').first().json;
const { lookbackDays, leadTimeDays, reviewPeriodDays, safetyDays } = cfg;
// 1. Units sold per SKU over the lookback window
const sold = {};
for (const o of $('Get paid orders').all()) {
for (const li of (o.json.line_items || [])) {
const sku = (li.sku || '').trim();
if (!sku) continue;
sold[sku] = (sold[sku] || 0) + (li.quantity || 0);
}
}
// 2. Join to current stock, one row per variant
const rows = [];
const stamp = new Date().toISOString().slice(0, 10);
for (const p of $input.all()) {
for (const v of (p.json.variants || [])) {
const sku = (v.sku || '').trim();
if (!sku) continue;
if (v.inventory_management !== 'shopify') continue;
const stock = v.inventory_quantity || 0;
const units = sold[sku] || 0;
const velocity = units / lookbackDays;
const reorderPoint = Math.ceil(velocity * (leadTimeDays + safetyDays));
const daysOfCover = velocity > 0 ? Math.round(stock / velocity) : null;
const target = velocity * (leadTimeDays + reviewPeriodDays + safetyDays);
const suggestedQty = Math.max(0, Math.ceil(target - stock));
const needsReorder = velocity > 0 && stock <= reorderPoint;
rows.push({ json: {
sku,
product: p.json.title,
variant: v.title === 'Default Title' ? '' : v.title,
stock,
units_sold: units,
velocity_per_day: Number(velocity.toFixed(3)),
days_of_cover: daysOfCover,
reorder_point: reorderPoint,
suggested_qty: needsReorder ? suggestedQty : 0,
status: velocity === 0 ? 'no sales' : (needsReorder ? 'REORDER NOW' : 'ok'),
updated_at: stamp
}});
}
}
rows.sort((a, b) => (a.json.days_of_cover ?? 99999) - (b.json.days_of_cover ?? 99999));
return rows;
Three decisions are worth naming. Variants without inventory_management set to shopify are skipped, because a reorder point on an untracked quantity is fiction. Blank SKUs are skipped, since there is nothing to join them to. And the suggested quantity covers lead time plus review period plus safety, not just lead time, because the next chance to order is seven days away.
Here is what the black tee looks like coming out of this node:
{
"sku": "TEE-BLK-M",
"product": "Black Tee",
"variant": "M",
"stock": 14,
"units_sold": 60,
"velocity_per_day": 1,
"days_of_cover": 14,
"reorder_point": 21,
"suggested_qty": 14,
"status": "REORDER NOW",
"updated_at": "2026-07-17"
}
6 Upsert the forecast sheet
Add a Google Sheets node, operation Append or Update Row. Pick your document by ID, sheet Reorder, mapping mode Map Automatically, and set Column to Match On to sku. Matching on SKU is what makes the workflow idempotent: every Monday the same row updates in place instead of appending a duplicate, so the sheet stays one row per SKU and sorts by days_of_cover into a catalog ranked by urgency.
7 Build the summary and email it
Add a second Code node, Run Once for All Items, that filters to the flagged rows and builds an HTML table:
const rows = $('Compute reorder points').all().map(i => i.json);
const flagged = rows.filter(r => r.status === 'REORDER NOW');
let html;
if (flagged.length === 0) {
html = '<p>Nothing crosses its reorder point this week. ' + rows.length + ' SKUs checked.</p>';
} else {
const cells = flagged.map(r =>
'<tr><td>' + r.sku + '</td><td>' + r.product + ' ' + r.variant + '</td><td>' + r.stock +
'</td><td>' + r.days_of_cover + '</td><td>' + r.reorder_point + '</td><td><b>' + r.suggested_qty + '</b></td></tr>'
).join('');
html = '<p>' + flagged.length + ' of ' + rows.length + ' SKUs need a purchase order.</p>' +
'<table border="1" cellpadding="6" cellspacing="0">' +
'<tr><th>SKU</th><th>Product</th><th>Stock</th><th>Days left</th><th>Reorder at</th><th>Order</th></tr>' +
cells + '</table>';
}
const label = flagged.length === 1 ? ' SKU' : ' SKUs';
return [{ json: {
subject: flagged.length ? 'Reorder ' + flagged.length + label : 'Reorder check: all clear',
html,
count: flagged.length
}}];
Then add a Gmail node, operation Send. Set To to {{ $('Set planning inputs').first().json.reportEmail }}, Subject to {{ $json.subject }}, and Message to {{ $json.html }} with Email Type set to HTML. The all clear email is deliberate. A workflow that only writes when something is wrong is indistinguishable from one that has quietly stopped running.
Save, run it once by hand, and check the sheet fills before you switch Active on. To have the list land in a channel instead of an inbox, swap the Gmail node for Telegram or Slack; the summary node already hands over a finished string, so nothing upstream changes. See Shopify alerts on Telegram.
Common mistakes
| Mistake | What happens | Fix |
|---|---|---|
| Leaving Execute Once off on the products node | The catalog is fetched once per order, thousands of times, and Shopify rate limits you | Turn on Execute Once in the node’s Settings tab |
| A wholesale order inside the window | One 500 unit order makes a trailing average think you sell 8 a day forever | Sanity check the velocity column; exclude the order or shorten the lookback |
| Guessing the lead time | Every reorder point is wrong by the size of the guess, in the same direction, for every SKU | Use the real number from your last three purchase orders, including customs |
| Products launched mid-window | Units get divided by 60 days even though the product existed for 10, so velocity reads low | Check anything with a recent first sale by eye until it has a full window |
| Renaming the Compute reorder points node | The summary node and the Gmail node break, since both reference it by name | Keep the name, or update every reference |
| Duplicate SKUs across variants | Two variants sharing a SKU collide on the sheet and overwrite each other | Keep SKUs unique per variant, which Shopify does not enforce for you |
The wholesale spike is the one that catches people. A mean has no idea that one of its inputs was unusual, and it will happily tell you to buy 500 more of something you sold once. If bulk orders are normal for you, the honest fix is a median instead of a mean, which is a two line change in the Code node.
Cost at realistic volume
Nothing here bills per SKU, which is the whole argument against the app version.
| Piece | Cost | Notes |
|---|---|---|
| n8n (self-hosted) | $0 | Four executions a month |
| Shopify Admin API | $0 | Order and product reads are included in your plan |
| Google Sheets | $0 | Free, well inside the write limits |
| Gmail | $0 | Four emails a month to yourself |
The only real resource is the two API pulls. A store doing 500 orders a month has about 1,000 orders in a 60 day window and maybe 300 variants, which the Shopify nodes page through in well under a minute. At 5,000 orders a month and 2,000 variants it takes a few minutes, which is unremarkable for something that runs while you sleep.
On n8n Cloud this is four executions a month against your plan allowance. Against 30 to 200 dollars a month for a forecasting app, the sheet wins on cost before you count the part that matters more: your lead time and your buffer are yours to change, and the numbers recompute on the next run.
🚀 Get the Shopify reorder point forecast template
The guide above is free to follow. If you would rather skip the build, download the ready-to-import workflow, then just add your credentials. Prefer it installed and tuned for you? See our done-for-you services.
Instant download · Works on n8n Cloud and self-hosted
FAQ
What is a reorder point in ecommerce?
A reorder point is the stock level at which you place a purchase order. It equals your daily sales velocity multiplied by your supplier lead time, plus a safety buffer. Hit that level and you order, because waiting any longer means the shelf empties before the delivery lands.
How is this different from a low stock alert?
A low stock alert fires at a fixed number you picked by hand, so 10 units means the same thing for a product selling 5 a day and one selling 5 a year. This workflow derives the threshold from actual sales velocity and your lead time, so it answers when to reorder rather than announcing that stock is low.
How much order history does the forecast need?
Sixty days is the default and works for steady sellers. Products launched inside the window show an inflated velocity because the units sold get divided by the full 60 days. Treat anything with less than a few weeks of history as directional and check it by eye before ordering.
Does this workflow change my Shopify inventory?
No. It only reads orders and products. Every number lands in your Google Sheet and in one email, so a wrong lead time cannot corrupt stock records or push a purchase order to a supplier. You stay the approval step between the forecast and the money.
Can it handle seasonal products?
Not on its own. A trailing average assumes next month looks like the last two, which is wrong going into a peak. Shorten the lookback to 30 days when demand climbs so the average reacts faster, and override the suggested quantity by hand for known seasonal spikes.
Related guides
The sheet this workflow produces is a planning input. These are the automations around it.
- The full guide to n8n Shopify automation
- How to connect Shopify to n8n in 2026
- Shopify inventory low stock alert, the fixed threshold version for when you just want to know a shelf is thin
- Auto email your supplier on low stock, the step after you approve the buy list
- Auto hide out of stock products, for the SKUs that get away from you
- Shopify weekly best sellers report
- More Shopify automations