{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "50 7 1"
}
]
},
"timezone": "Asia/Kolkata"
},
"name": "Monday 7:50 IST",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [240, 300]
}50 7 1 — minute 50, hour 7, every month, every day, Monday only. The timezone field is critical — without it, n8n uses the server timezone (UTC if you are on Hetzner) and your Monday 7:50 IST run goes off at 1:20 pm. We made that mistake on the second client and had to dig through the logs to figure out why nothing fired on Monday.
### Node 2 — Google Sheets read
{
"parameters": {
"authentication": "serviceAccount",
"operation": "read",
"documentId": {
"__rl": true,
"mode": "id",
"value": "1xJ_REDACTED_SHEET_ID"
},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Daily_Sales"
},
"options": {
"rangeDefinition": "specifyRange",
"range": "A2:H8"
}
},
"name": "Read last 7 days",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [460, 300]
}yourbot@yourproject.iam.gserviceaccount.com). The range A2:H8 reads exactly 7 rows after the header — keeps the payload tiny.
### Node 3 — Code node (the 6-reduction)
{
"parameters": {
"jsCode": "const rows = items.map(i => i.json);\nconst weekend = rows.filter(r => {\n const d = new Date(r.date).getDay();\n return d === 0 || d === 6;\n});\nconst gross = weekend.reduce((s, r) => s + Number(r.amount || 0), 0);\nconst returns = weekend.reduce((s, r) => s + Number(r.returns || 0), 0);\nconst byDay = weekend.reduce((m, r) => { m[r.date] = (m[r.date]||0) + Number(r.amount); return m; }, {});\nconst worstDay = Object.entries(byDay).sort((a,b) => a[1]-b[1])[0];\nconst skuCount = weekend.reduce((m, r) => { m[r.top_sku] = (m[r.top_sku]||0) + Number(r.qty); return m; }, {});\nconst topSku = Object.entries(skuCount).sort((a,b) => b[1]-a[1])[0];\nconst tickets = weekend.reduce((s, r) => s + Number(r.tickets||0), 0);\nconst avgTicket = tickets ? Math.round(gross / tickets) : 0;\nconst upi = weekend.reduce((s, r) => s + Number(r.upi_pct||0), 0) / weekend.length;\nreturn [{\n json: {\n gross: gross.toLocaleString('en-IN'),\n net: (gross - returns).toLocaleString('en-IN'),\n returns: returns.toLocaleString('en-IN'),\n worstDay: worstDay ? worstDay[0] + ' (₹' + worstDay[1].toLocaleString('en-IN') + ')' : 'n/a',\n topSku: topSku ? topSku[0] + ' (' + topSku[1] + ' units)' : 'n/a',\n avgTicket: avgTicket.toLocaleString('en-IN'),\n upi: Math.round(upi) + '%'\n }\n}];"
},
"name": "Compute 6 numbers",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [680, 300]
}Header (TEXT): Weekend roll-up — Indore store
Body:
Hi {{1}},
Sat-Sun gross: ₹{{2}}
Net (after returns): ₹{{3}}
Returns: ₹{{4}}
Worst day: {{5}}
Top SKU: {{6}}
Avg ticket: ₹{{7}}
UPI share: {{8}}
— Auto from n8n at 7:50 IST. Reply STOP to mute.
Footer: Softechinfra workflow{{1}} is the recipient first name, {{2}} is gross, etc. Keep the template under 1,024 chars or it will fail Meta review.
## The cost — actual numbers from one month of running
The Hetzner box runs 7 other client workflows in parallel — we apportion 1/8th. WhatsApp sends are cheap because Utility templates in India sit at ~₹0.115 per delivered message. If you bumped this to a Marketing template (e.g., a sales push to customers), you would pay ~₹0.78 per message — 7x more. Categorise correctly.
## The pre-flight checklist (we run this every time)
- Cron expression timezone is set to Asia/Kolkata, not server-default
- Google service-account JSON pasted into n8n, sheet shared with bot email
- WhatsApp template approved with category Utility (not Marketing)
- Recipient phone numbers verified opted-in (test message returns code 131000)
- Code node returns valid JSON for an empty weekend (we test by clearing rows)
- Error workflow set on the main workflow's Settings panel — points to a "send-to-ops" sub-flow
- Log sheet has columns date, recipient, status, message_id, error
- Workflow is saved, activated, and a manual test run completed in the last 24 hours
- Backup of workflow JSON exported to git the day of go-live
- Founders briefed on STOP keyword and how to mute
"", not as zero. Number("") is 0 but Number(undefined) is NaN. Fix: the || 0 fallback in the Code node above guards both cases. We left it in because we got bitten on a public-holiday weekend.
Symptom: "founders complain that Monday after a public holiday is wrong." Cause: long weekends include Monday in the dates we should have summed. Fix: change the filter to "last 3 days" if today is Tuesday after a Monday holiday, or maintain a simple holidays sheet and exclude business days. We added a 3-row "exceptions" tab in the same Google Sheet.
## The mini case study — Indore retail SMB, 11 weeks live
The first install ran at the Indore client from August 11, 2025. Eleven Mondays later: zero missed runs, two template-rejection incidents (both fixed within 4 hours), one opt-in bounce we caught in the log sheet on day 4. Founders dropped the Monday standup permanently in week 3 — recouped 4 hours per week of senior team time. We extended the same workflow to a Coimbatore D2C client in October; their version has 17 nodes because they wanted a separate breakdown by sales channel (Shopify vs. retail vs. WhatsApp orders).
For more context on how we structure these flows, see our [n8n receptionist build](/blog/n8n-receptionist-twilio-whisper-claude-call-routing) and the broader [AI automation services](/services/ai-automation) page. The Indore project is one of several recent SMB engagements where the [Softechinfra team](/team/rishikesh-baidya) has replaced a recurring meeting with a workflow.
## When not to build this
Skip this workflow if (a) your weekend sales data is not already in a structured Sheet — building the data-entry hygiene takes 4x longer than the n8n piece, (b) your team is under 5 people and standups already take under 10 minutes — there is no time-saving to capture, or (c) your founders prefer face-to-face — automation does not fix a meeting that exists for political reasons. We turned down two clients in 2025 for reason (c). The workflow was technically easy; the human change wasn't.
For a similar small-team setup we shipped, see the [Radiant Finance lead pipeline](/projects/radiant-finance) — same n8n stack, different use case.
## FAQ
### How long to build and deploy this n8n workflow end-to-end?
A first-time install takes 2 working days for us — half a day on Meta Business Manager template approval, half a day on the n8n flow, one day on testing across edge cases (empty weekend, public holiday, founder opt-out). For a self-build, budget 5-7 days including time spent on first-time WhatsApp Cloud API setup.
### Can I run this on n8n Cloud instead of self-hosted?
Yes. n8n Cloud Starter at $24/month (~₹2,000) handles 2,500 executions, well above what this single weekly workflow needs. The trade-off: cloud counts every WhatsApp status webhook (delivered, read) as an execution, so a busier installation can hit limits fast. Self-host if you plan to run more than 3-4 chatty workflows.
### What if WhatsApp template review is rejected?
Most rejections come from words flagged as marketing — "discount", "free", "limited time". Strip those, add the explicit phrase "transactional update", resubmit. Approval is usually within 4 hours on the second submission. We have a checklist of safe phrases shared on request.
### How do I test the workflow without spamming founders?
Use Meta's test phone number (provided in your WhatsApp Cloud API setup) for the first 5 runs. It accepts any inbound and shows the rendered message in your developer console. Switch to real numbers only after the template renders correctly twice in a row.
### What is the right error-handling pattern for a Monday-only workflow?
Set an Error Workflow on the main workflow's settings — point it to a 3-node sub-flow that posts to a Slack channel and writes a row to an "errors" sheet. We do not retry the main flow automatically — Monday-only flows that fail benefit from human triage; an unattended retry could send the wrong week's numbers if the Sheet was being edited mid-run.
### Why Google Sheets and not Airtable or Postgres?
Because the shop floor already filled the Sheet daily. Whatever data lives where the team naturally enters it — that is what your workflow reads. Migrating data entry to a "better" tool is a separate, much harder project. For a small retail team, Sheets wins because it is the path of least resistance.
### Can this run on the Indian-region n8n cloud?
n8n Cloud regions are EU and US. Latency from India is 200-280 ms — fine for a once-a-week workflow. For latency-sensitive flows (real-time receptionists, live payment alerts) we self-host on Hetzner Singapore or AWS Mumbai instead.
Want this exact n8n workflow built and deployed?
We ship the weekend roll-up flow — n8n on your Hetzner box, Sheets read, WhatsApp template approval, founder onboarding — in 7 working days for ₹38,000. Suitable for any retail or D2C SMB with 5+ founders or department heads in a recurring Monday meeting.
Book a 20-min Call
