5 n8n Automations Every WooCommerce Store Needs (Save 10+ Hours/Week)
Running a WooCommerce store means drowning in repetitive tasks: checking inventory, logging orders to spreadsheets, chasing abandoned carts, sending follow-up emails, compiling daily sales reports. Most store owners spend hours each week on work that should be automatic.
This guide shows you 5 n8n workflows that eliminate the most time-consuming WooCommerce admin tasks — with full workflow JSON you can import and use today.
Setup: Connect WooCommerce to n8n (5 minutes)
- In your WordPress admin, go to WooCommerce → Settings → Advanced → REST API
- Click Add key → set description "n8n", user (admin), permissions Read/Write
- Copy the Consumer Key and Consumer Secret
- In n8n, create a WooCommerce credential: add your store URL + those keys
That's it. All 5 workflows below use this credential.
Workflow 1: Low Stock Alert → Slack + Email
The problem: You run out of stock without knowing, losing sales. WooCommerce's built-in alerts are easy to miss.
What this does: Every hour, checks all products with stock below your threshold and sends a Slack message + email listing which products need restocking — with current stock count and a direct admin link.
Workflow (6 nodes):
{
"name": "WooCommerce Low Stock Alert",
"nodes": [
{
"name": "Every Hour",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": { "interval": [{ "field": "hours", "hoursInterval": 1 }] }
},
"position": [250, 300]
},
{
"name": "Get Products",
"type": "n8n-nodes-base.wooCommerce",
"parameters": {
"resource": "product",
"operation": "getAll",
"returnAll": true,
"filters": {
"stock_status": "instock",
"manage_stock": true
}
},
"position": [450, 300]
},
{
"name": "Filter Low Stock",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "const LOW_STOCK_THRESHOLD = 5;\nconst lowStock = items.filter(item => {\n const stock = item.json.stock_quantity;\n return stock !== null && stock <= LOW_STOCK_THRESHOLD;\n});\nif (lowStock.length === 0) return [];\nconst report = lowStock.map(p =>\n `• ${p.json.name} — ${p.json.stock_quantity} left (${process.env.WP_URL}/wp-admin/post.php?post=${p.json.id}&action=edit)`\n).join('\\n');\nreturn [{ json: { report, count: lowStock.length } }];"
},
"position": [650, 300]
},
{
"name": "IF Has Low Stock",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": {
"number": [{ "value1": "={{$json.count}}", "operation": "larger", "value2": 0 }]
}
},
"position": [850, 300]
},
{
"name": "Slack Alert",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#inventory-alerts",
"text": "⚠️ Low stock alert ({{$json.count}} products):\n\n{{$json.report}}"
},
"position": [1050, 200]
},
{
"name": "Email Alert",
"type": "n8n-nodes-base.gmail",
"parameters": {
"toEmail": "store@yourdomain.com",
"subject": "⚠️ WooCommerce Low Stock Alert — {{$json.count}} products",
"message": "The following products are running low:\n\n{{$json.report}}"
},
"position": [1050, 400]
}
]
}
Customise: Change LOW_STOCK_THRESHOLD to any number. Add a Google Sheets log node to track restock history.
Workflow 2: New Order → Google Sheets Log + Slack Notification
The problem: Your order data lives in WooCommerce only. You can't easily analyse trends, share data with your team, or build custom reports without exporting manually.
What this does: Every new WooCommerce order automatically gets logged to a Google Sheets master order table AND posts a Slack notification — so your team knows about every sale in real time.
Workflow (4 nodes):
{
"name": "WooCommerce Order Logger",
"nodes": [
{
"name": "WooCommerce Webhook",
"type": "n8n-nodes-base.wooCommerceTrigger",
"parameters": {
"event": "order.created"
},
"position": [250, 300]
},
{
"name": "Format Order",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "const order = items[0].json;\nconst customer = `${order.billing.first_name} ${order.billing.last_name}`;\nconst items_list = order.line_items.map(i => `${i.name} x${i.quantity}`).join(', ');\nreturn [{\n json: {\n order_id: order.id,\n date: order.date_created,\n customer,\n email: order.billing.email,\n total: order.total,\n items: items_list,\n status: order.status,\n payment: order.payment_method_title\n }\n}];"
},
"position": [450, 300]
},
{
"name": "Log to Sheets",
"type": "n8n-nodes-base.googleSheets",
"parameters": {
"operation": "appendOrUpdate",
"sheetId": "YOUR_SHEET_ID",
"range": "Orders!A:I",
"values": {
"Order ID": "={{$json.order_id}}",
"Date": "={{$json.date}}",
"Customer": "={{$json.customer}}",
"Email": "={{$json.email}}",
"Total": "={{$json.total}}",
"Items": "={{$json.items}}",
"Status": "={{$json.status}}",
"Payment": "={{$json.payment}}"
}
},
"position": [650, 200]
},
{
"name": "Slack Notification",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#sales",
"text": "🛒 New order #{{$json.order_id}} — {{$json.customer}} — ${{$json.total}}\n{{$json.items}}"
},
"position": [650, 400]
}
]
}
Customise: Replace the WooCommerce Trigger with a Schedule Trigger that polls every 15 min if webhooks are hard to set up on your host. Add a Discord node instead of Slack.
Workflow 3: Abandoned Cart Recovery Email
The problem: 70%+ of WooCommerce carts are abandoned. The built-in recovery email (only in WooCommerce Pro) costs $199/year. n8n does the same thing free.
What this does: Every 30 minutes, checks for carts abandoned in the last 1 hour and sends a personalised recovery email with the exact items left behind and a direct checkout link.
Workflow (5 nodes):
{
"name": "Abandoned Cart Recovery",
"nodes": [
{
"name": "Every 30 Min",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": { "interval": [{ "field": "minutes", "minutesInterval": 30 }] }
},
"position": [250, 300]
},
{
"name": "Get Pending Orders (1h)",
"type": "n8n-nodes-base.wooCommerce",
"parameters": {
"resource": "order",
"operation": "getAll",
"returnAll": false,
"limit": 50,
"filters": {
"status": "pending",
"after": "={{ DateTime.now().minus({hours:2}).toISO() }}",
"before": "={{ DateTime.now().minus({hours:1}).toISO() }}"
}
},
"position": [450, 300]
},
{
"name": "Build Recovery Email",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "return items.map(item => {\n const order = item.json;\n const cartItems = order.line_items.map(i =>\n `<li>${i.name} x${i.quantity} — $${i.total}</li>`\n ).join('');\n const html = `<p>Hi ${order.billing.first_name},</p>\n<p>You left some great items in your cart:</p>\n<ul>${cartItems}</ul>\n<p><strong>Total: $${order.total}</strong></p>\n<p><a href=\"${order.checkout_payment_url}\">Complete your order →</a></p>`;\n return { json: { email: order.billing.email, name: order.billing.first_name, html, order_id: order.id } };\n});"
},
"position": [650, 300]
},
{
"name": "IF Has Email",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": {
"string": [{ "value1": "={{$json.email}}", "operation": "isNotEmpty" }]
}
},
"position": [850, 300]
},
{
"name": "Send Recovery Email",
"type": "n8n-nodes-base.gmail",
"parameters": {
"toEmail": "={{$json.email}}",
"subject": "Hey {{$json.name}}, you left something behind...",
"message": "={{$json.html}}",
"options": { "emailType": "html" }
},
"position": [1050, 300]
}
]
}
Note: WooCommerce creates an order in "pending" status when a customer starts checkout and doesn't complete it. This workflow targets those orders. Check your host's cron scheduling for the WooCommerce sessions plugin if you want to catch earlier-stage abandonment.
Workflow 4: AI Customer Review Tagger
The problem: Reading every customer review manually to understand what's working and what isn't takes hours. You need a way to automatically categorise feedback.
What this does: When a new WooCommerce review is submitted, Claude AI reads it and tags it as Positive/Negative/Feature Request — then logs it to Google Sheets with the AI's 1-line summary. You get a searchable database of every piece of customer feedback, automatically categorised.
Workflow (4 nodes):
{
"name": "WooCommerce AI Review Tagger",
"nodes": [
{
"name": "WooCommerce Review Webhook",
"type": "n8n-nodes-base.wooCommerceTrigger",
"parameters": {
"event": "review.created"
},
"position": [250, 300]
},
{
"name": "Claude AI Tagger",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"method": "POST",
"url": "https://api.anthropic.com/v1/messages",
"headers": {
"x-api-key": "YOUR_CLAUDE_API_KEY",
"anthropic-version": "2023-06-01",
"content-type": "application/json"
},
"body": {
"model": "claude-haiku-4-5-20251001",
"max_tokens": 100,
"messages": [{
"role": "user",
"content": "Categorise this WooCommerce review. Reply with JSON only: {tag: 'Positive'|'Negative'|'Feature Request', summary: '10 words max'}\n\nReview: {{$json.review}}"
}]
}
},
"position": [450, 300]
},
{
"name": "Parse AI Response",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "const text = items[0].json.content[0].text;\nconst parsed = JSON.parse(text);\nreturn [{ json: { ...parsed, review: $('WooCommerce Review Webhook').first().json.review, product: $('WooCommerce Review Webhook').first().json.product_id, rating: $('WooCommerce Review Webhook').first().json.rating } }];"
},
"position": [650, 300]
},
{
"name": "Log to Sheets",
"type": "n8n-nodes-base.googleSheets",
"parameters": {
"operation": "appendOrUpdate",
"sheetId": "YOUR_SHEET_ID",
"range": "Reviews!A:E",
"values": {
"Product ID": "={{$json.product}}",
"Rating": "={{$json.rating}}",
"Tag": "={{$json.tag}}",
"AI Summary": "={{$json.summary}}",
"Review": "={{$json.review}}"
}
},
"position": [850, 300]
}
]
}
Customise: Swap Claude Haiku for OpenAI GPT-4o-mini to cut cost further. Add a Slack alert when a "Negative" tag is detected so you can respond quickly.
Workflow 5: Daily WooCommerce Sales Report
The problem: Checking WooCommerce stats every morning means logging in, waiting for the dashboard to load, and mentally compiling numbers. Total waste of 10 minutes every day.
What this does: Every morning at 7 AM, this workflow pulls the previous day's orders, calculates revenue, order count, AOV, top product, and week-over-week change — then emails you a clean summary and posts it to Slack.
Workflow (5 nodes):
{
"name": "WooCommerce Daily Sales Report",
"nodes": [
{
"name": "7 AM Daily",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": { "interval": [{ "field": "cronExpression", "expression": "0 7 * * *" }] }
},
"position": [250, 300]
},
{
"name": "Get Yesterday's Orders",
"type": "n8n-nodes-base.wooCommerce",
"parameters": {
"resource": "order",
"operation": "getAll",
"returnAll": true,
"filters": {
"status": "completed,processing",
"after": "={{ DateTime.now().minus({days:1}).startOf('day').toISO() }}",
"before": "={{ DateTime.now().startOf('day').toISO() }}"
}
},
"position": [450, 300]
},
{
"name": "Calculate Stats",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "const orders = items.map(i => i.json);\nif (orders.length === 0) return [{ json: { revenue: 0, orders: 0, aov: 0, top_product: 'N/A', date: new Date().toDateString() } }];\nconst revenue = orders.reduce((s, o) => s + parseFloat(o.total), 0);\nconst productCount = {};\norders.forEach(o => o.line_items.forEach(item => {\n productCount[item.name] = (productCount[item.name] || 0) + item.quantity;\n}));\nconst top_product = Object.entries(productCount).sort((a,b) => b[1]-a[1])[0]?.[0] || 'N/A';\nreturn [{ json: {\n date: new Date().toDateString(),\n revenue: revenue.toFixed(2),\n orders: orders.length,\n aov: (revenue / orders.length).toFixed(2),\n top_product\n}}];"
},
"position": [650, 300]
},
{
"name": "Email Report",
"type": "n8n-nodes-base.gmail",
"parameters": {
"toEmail": "owner@yourdomain.com",
"subject": "📊 WooCommerce Daily Report — {{$json.date}}",
"message": "Yesterday's numbers:\n\n💰 Revenue: ${{$json.revenue}}\n🛒 Orders: {{$json.orders}}\n📈 AOV: ${{$json.aov}}\n🏆 Top Product: {{$json.top_product}}",
"options": {}
},
"position": [850, 200]
},
{
"name": "Slack Report",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#sales",
"text": "📊 *Yesterday's WooCommerce Report* ({{$json.date}})\n💰 Revenue: ${{$json.revenue}} | 🛒 Orders: {{$json.orders}} | 📈 AOV: ${{$json.aov}}\n🏆 Top product: {{$json.top_product}}"
},
"position": [850, 400]
}
]
}
Customise: Add a Sheets log to track trends over time. Add week-over-week comparison by also fetching orders from 7 days ago. Add top 3 products instead of just 1.
The Real Cost of Manual Store Management
Let me put some numbers on this:
| Task | Manual time | With n8n |
|---|---|---|
| Check inventory daily | 10 min | 0 min |
| Log orders to spreadsheet | 15 min/day | 0 min |
| Send abandoned cart emails | 20 min/day | 0 min |
| Read and tag reviews | 30 min/week | 0 min |
| Morning sales check | 10 min/day | 0 min |
That's over 3 hours per week handed back to you. For a store doing even $5,000/month, that's time better spent on marketing, product development, or — honestly — not working.
What's Next: Ready-Made Templates
If you want these workflows pre-built and ready to import — no JSON wrangling, no setup debugging — I packaged all 15 of my most-used n8n workflows into the FlowKit n8n Template Pack at stripeai.gumroad.com.
Each template includes the workflow JSON, a setup checklist, and a short video showing exactly how to configure the credentials. The Price Monitor ($29) and Daily Report Generator ($19) in the pack work directly with WooCommerce's REST API.
Final Tips for WooCommerce + n8n
- Use webhooks over polling where possible — webhooks fire instantly, polling adds latency
- WooCommerce REST API rate limits: default is 200 requests/minute — more than enough for most stores
-
Test with order status
pendingbefore going live withcompletedorders - Store your API keys in n8n credentials (never hardcode them in Code nodes)
- Schedule triggers use server time — make sure your n8n instance timezone matches your store timezone
Questions? Drop them in the comments — I read every one.
Top comments (0)