- n8n v1.6x with the timezone set to Asia/Kolkata (Settings → check
GENERIC_TIMEZONE) - A Google Cloud service account with the Sheets API enabled, shared on the sales sheet
- An Anthropic API key for Claude (we used Claude 3.5 Sonnet; Haiku works and is cheaper)
- A WhatsApp Business account with the Cloud API and one approved utility template
- A clean Google Sheet: one row per order, columns for rep, amount, product, date
- About 75 minutes
.iam.gserviceaccount.com) as a Viewer.
Claude: n8n has an Anthropic credential type. Paste your API key. We call Claude through the HTTP Request node for full control over the system prompt and max tokens, but the native Anthropic node works too.
WhatsApp Cloud API: n8n's WhatsApp Business Cloud node takes an access token and your phone number ID from Meta's developer dashboard. Generate a permanent token via a Meta System User — the temporary 24-hour token will silently break your cron next week.
0 19 0 means 7:00pm every Sunday.
{
"parameters": {
"rule": {
"interval": [
{ "field": "cronExpression", "expression": "0 19 0" }
]
}
},
"id": "schedule-sunday-eod",
"name": "Every Sunday 7pm IST",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [240, 300]
}
GENERIC_TIMEZONE is UTC, 0 19 * * 0 fires at 12:30am Monday IST. Set the env var, restart n8n, and trigger a manual test run before trusting the cron.
{
"parameters": {
"operation": "read",
"documentId": "={{ $env.SALES_SHEET_ID }}",
"sheetName": "Orders",
"options": {
"dataStartRow": 2
}
},
"name": "Google Sheets — Read Orders",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [460, 300],
"credentials": {
"googleApi": { "id": "4", "name": "Sheets service account" }
}
}
The Code node filters to the last 7 days and rolls up per rep:
{
"parameters": {
"jsLanguage": "javaScript",
"jsCode": "const weekAgo = new Date(Date.now() - 72460601000);\nconst rows = $input.all().map(i => i.json);\nconst recent = rows.filter(r => new Date(r.date) >= weekAgo);\nconst byRep = {};\nfor (const r of recent) {\n const rep = r.rep || 'Unknown';\n byRep[rep] = byRep[rep] || { rep, total: 0, orders: 0 };\n byRep[rep].total += Number(r.amount) || 0;\n byRep[rep].orders += 1;\n}\nreturn Object.values(byRep).sort((a,b) => b.total - a.total).map(json => ({ json }));"
},
"name": "Code — Roll Up Per Rep",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [680, 300]
}
## How Do You Get Claude to Write a WhatsApp-Ready Summary?
The point of Claude here isn't analysis — the maths is already done. It's tone and language. The reps read a mix of Hindi and English, so a raw table lands flat. Claude turns the rollup into a short, readable message with the top three reps called out and a one-line nudge for anyone at zero.
The system prompt that worked after a few iterations: "You are writing a short weekly sales summary for a field-sales WhatsApp group in India. Input is JSON of reps with totals in rupees. Write under 90 words, mix natural Hindi and English the way Indian sales teams talk, lead with this week's total, name the top 3 reps, and end with one motivating line. No markdown, no emojis beyond one trophy."
It took three iterations to get the tone right. The first version read like a press release — "We are pleased to report total weekly sales of..." — and the group ignored it exactly the way they ignored the spreadsheet. The second was too casual and buried the numbers. The third, the one above, leads with the figure, names names, and closes with a single line of push. The lesson generalises: the maths was never the hard part. Getting 22 busy people on bikes to actually read the message was, and that's a writing problem, not a data problem. We spent more time on the prompt than on every other node combined, and it was the right place to spend it.
{
"parameters": {
"method": "POST",
"url": "https://api.anthropic.com/v1/messages",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{ "name": "anthropic-version", "value": "2023-06-01" }
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"model\": \"claude-3-5-sonnet-20241022\",\n \"max_tokens\": 400,\n \"system\": \"You write a short weekly field-sales summary for an Indian WhatsApp group. Under 90 words, natural Hindi-English mix, lead with the weekly total, name the top 3 reps, one motivating closing line. No markdown.\",\n \"messages\": [{ \"role\": \"user\", \"content\": \"{{ JSON.stringify($json) }}\" }]\n}"
},
"name": "Claude — Format Summary",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [900, 300],
"credentials": {
"httpHeaderAuth": { "id": "5", "name": "Anthropic API" }
}
}
{{1}}.
The WhatsApp node loops over the recipient list (reps + manager) and sends the template:
{
"parameters": {
"resource": "message",
"operation": "send",
"phoneNumberId": "={{ $env.WA_PHONE_ID }}",
"recipientPhoneNumber": "={{ $json.phone }}",
"messageType": "template",
"template": "weekly_sales_rollup",
"templateLanguage": "en",
"components": {
"componentValues": [
{ "type": "body", "parameter": "={{ $json.summary }}" }
]
}
},
"name": "WhatsApp — Send Template",
"type": "n8n-nodes-base.whatsApp",
"typeVersion": 1,
"position": [1120, 300],
"credentials": {
"whatsAppApi": { "id": "6", "name": "WhatsApp Cloud" }
}
}
## How Do You Handle Errors in a Weekly Cron Like This?
A cron that runs once a week is easy to forget until it's been silently broken for a month. Three guardrails.
1. Heartbeat to the manager. If zero rows match the week, send the manager a "no orders logged this week — is the sheet being updated?" message instead of failing quietly. Silence and success look identical otherwise.
2. Retry WhatsApp sends. Per-recipient sends get Retry On Fail (3 tries). One bad number shouldn't kill the broadcast to the other 22. We also wrap the send loop so a single failure routes to a "failed_sends" log, not a stopped workflow.
3. Token-expiry alarm. A monthly check-call to the WhatsApp API; if it 401s, n8n alerts. Permanent tokens can still be revoked when a Meta admin changes.
"Pehle sheet bhar dete the, koi dekhta nahi tha. Ab Sunday ko message aata hai aur Monday meeting already half done hoti hai."
{{1}} body parameter.
### Do I need Claude at all, or can n8n format the message?
You don't strictly need it. A Code node can build a clean text table for free. We use Claude because the Nagpur team reads Hindi-English better than a formal table, and the conversational tone drove replies. If your team reads English fine, skip the API call.
### How do I make sure the cron fires at 7pm IST and not UTC?
Set n8n's GENERIC_TIMEZONE to Asia/Kolkata and restart. Then the cron 0 19 0 means 7pm Sunday IST. If you leave it UTC, that expression fires at 12:30am Monday IST. Always do one manual test run to confirm.
### What happens if no orders were logged that week?
The flow detects zero matching rows and sends the manager a heartbeat message asking whether the sheet is being updated, rather than failing silently. Silence and success look the same otherwise, which is how weekly crons rot unnoticed.
### Why did my WhatsApp broadcast stop working after a day?
Almost always the temporary 24-hour access token from Meta's quick-start. Create a System User and generate a permanent token, then update the n8n credential. A monthly heartbeat call catches token revocation too.
### How much does the WhatsApp portion cost in India?
Very little. After WhatsApp's July 1, 2025 move to per-message pricing, India utility templates run a fraction of a rupee each, and 23 messages a week comes to roughly ₹18. Utility templates inside a 24-hour service window are free.
Want a weekly WhatsApp rollup for your sales team?
We build the Google Sheets → Claude → WhatsApp pipeline, register your templates, and self-host it on your infra — for Indian SMBs in 5 working days. Typical project: ₹40k–₹70k. Suitable if you have a team logging numbers nobody reads. No slides — just your sheet and our honest take.
Book a 20-min CallWritten by Hrishikesh Baidya, CTO at Softechinfra. Want this on your stack? Email contact@softechinfra.com.

