Softechinfra
Productivity

n8n + WhatsApp Cloud: Auto-Send GSTR-3B Reminders to 240 Clients on the 18th of Every Month

A monthly n8n cron flow that pulls a CA firm's 240-client list and sends GSTR-3B reminders with one-click reply handlers (READY / EXTEND / SKIP). Real WhatsApp template, a lean all-in monthly cost, and the deduplication trick we use.

Hrishikesh BaidyaHrishikesh Baidya
December 18, 202514 min read
n8n + WhatsApp Cloud: Auto-Send GSTR-3B Reminders to 240 Clients on the 18th of Every Month

A 4-partner CA firm in Indore handles GSTR-3B filing for 240 clients every month. Until October 2025, two associates spent 11 hours each on the 17th-18th of every month sending individual WhatsApp reminders, chasing acknowledgments, marking spreadsheets. We replaced that with a 19-node n8n flow that fires at 09:00 IST on the 18th, sends a personalised WhatsApp utility template to all 240 clients, and routes replies (READY / EXTEND / SKIP) into the firm's Notion board automatically. The associates now spend 90 minutes total handling exceptions instead of 22 hours sending messages. This is the build.

240
Clients reminded each month
22 hrs
Manual time saved per month
63%
Replies via one-click button on day 1

TL;DR — what this GSTR-3B reminder flow does

On the 18th of every month at 09:00 IST, n8n reads the firm's "GSTR_clients" Notion database (240 active clients with GSTIN, name, partner-incharge, default reminder template). For each client, it sends a personalised WhatsApp utility template with three quick-reply buttons: READY (filing data sent), EXTEND (need extra time), SKIP (NIL return). A separate webhook flow listens for replies, updates the client's row in Notion with status + timestamp, and DMs the partner-incharge if a reply is overdue by 24 hours.

Why this matters now — December 2025

Two reasons. The GSTR-3B due date moved permanently to the 20th of each month under the GST 2.0 rollout, and the non-editable Tables 3.1/3.2 from April 2025 mean filings now require validated supplier data days in advance — not the night before. CA firms can no longer leave reminders to the 19th. They need disciplined cadence on the 18th. Combined with the WhatsApp Business Cloud API quick-reply button feature (rolled out for India in mid-2025), we now have the components to make this fully self-serve.

A r/IndiaCAs thread from October 2025 on automation tools for filing reminders has 80+ practitioners debating exactly this — the consensus view is that WhatsApp templates with quick-reply buttons beat email by 4-5x on response rate.

The 4-block workflow

CRON
Block 1: Monthly cron + read clients
Schedule trigger fires at 09:00 IST on day 18 of every month. Notion query returns 240 active clients with GSTIN, partner, contact.
SEND
Block 2: Loop + WhatsApp send
SplitInBatches loops over 240 clients in batches of 10. WhatsApp Cloud API send-template node fires personalised template per row with 3 quick-reply buttons.
WB
Block 3: Reply webhook + status update
Separate workflow listens to webhook from Meta. Parses button reply, looks up client by phone, updates Notion row with status + timestamp.
CHASE
Block 4: 24h chaser sub-flow
On day 19 at 11:00 IST, queries Notion for clients with no reply, DMs the partner-incharge with the missing list. Manual triage from there.

The actual n8n node JSON (3 critical nodes)

Node 1 — Schedule Trigger (monthly on 18th)

json
{
    "parameters": {
      "rule": {
        "interval": [
          {
            "field": "cronExpression",
            "expression": "0 9 18 * *"
          }
        ]
      },
      "timezone": "Asia/Kolkata"
    },
    "name": "18th 9am IST",
    "type": "n8n-nodes-base.scheduleTrigger",
    "typeVersion": 1.2,
    "position": [240, 300]
  }

The cron expression 0 9 18 reads as: minute 0, hour 9, day-of-month 18, every month, every day-of-week. The timezone field is mandatory — without it, the cron runs at 14:30 IST instead of 09:00.

Node 2 — WhatsApp Cloud API send template with buttons

json
{
    "parameters": {
      "method": "POST",
      "url": "https://graph.facebook.com/v21.0/{{ $env.WA_PHONE_ID }}/messages",
      "sendHeaders": true,
      "headerParameters": {
        "parameters": [
          { "name": "Authorization", "value": "Bearer {{ $env.WA_TOKEN }}" },
          { "name": "Content-Type", "value": "application/json" }
        ]
      },
      "sendBody": true,
      "bodyContentType": "json",
      "jsonBody": "={\n  \"messaging_product\": \"whatsapp\",\n  \"to\": \"{{ $json.phone }}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"gstr3b_reminder_v2\",\n    \"language\": { \"code\": \"en\" },\n    \"components\": [\n      {\n        \"type\": \"body\",\n        \"parameters\": [\n          { \"type\": \"text\", \"text\": \"{{ $json.client_name }}\" },\n          { \"type\": \"text\", \"text\": \"{{ $json.gstin }}\" },\n          { \"type\": \"text\", \"text\": \"{{ $json.due_date }}\" }\n        ]\n      },\n      {\n        \"type\": \"button\",\n        \"sub_type\": \"quick_reply\",\n        \"index\": \"0\",\n        \"parameters\": [\n          { \"type\": \"payload\", \"payload\": \"READY_{{ $json.gstin }}\" }\n        ]\n      },\n      {\n        \"type\": \"button\",\n        \"sub_type\": \"quick_reply\",\n        \"index\": \"1\",\n        \"parameters\": [\n          { \"type\": \"payload\", \"payload\": \"EXTEND_{{ $json.gstin }}\" }\n        ]\n      },\n      {\n        \"type\": \"button\",\n        \"sub_type\": \"quick_reply\",\n        \"index\": \"2\",\n        \"parameters\": [\n          { \"type\": \"payload\", \"payload\": \"SKIP_{{ $json.gstin }}\" }\n        ]\n      }\n    ]\n  }\n}",
      "options": { "timeout": 15000 }
    },
    "name": "Send GSTR-3B reminder",
    "type": "n8n-nodes-base.httpRequest",
    "typeVersion": 4.2,
    "position": [680, 300]
  }

Note the GSTIN baked into each button payload. When the client taps a button, Meta's webhook returns the payload (e.g., READY_27ABCDE1234F1Z5). Our reply-handler workflow parses that payload and finds the client without ambiguity — even if two clients share a phone number (it happens with family-run businesses).

Node 3 — Reply webhook handler (separate workflow)

json
{
    "parameters": {
      "httpMethod": "POST",
      "path": "wa-reply",
      "responseMode": "lastNode",
      "options": {}
    },
    "name": "WA reply webhook",
    "type": "n8n-nodes-base.webhook",
    "typeVersion": 2,
    "position": [240, 300],
    "webhookId": "REDACTED"
  }

Followed by a Code node that parses the Meta webhook payload:

javascript
const body = $input.item.json.body;
  const messages = body?.entry?.[0]?.changes?.[0]?.value?.messages || [];
  if (messages.length === 0) return []; // status update, not a message
  
  const msg = messages[0];
  if (msg.type !== 'button') return [{ json: { skip: true, reason: 'not_a_button' } }];
  
  const payload = msg.button.payload;
  const [action, gstin] = payload.split('_');
  if (!['READY', 'EXTEND', 'SKIP'].includes(action)) return [];
  
  return [{
    json: {
      action,
      gstin,
      phone: msg.from,
      timestamp: new Date(parseInt(msg.timestamp) * 1000).toISOString(),
      message_id: msg.id
    }
  }];

The WhatsApp template (the one to submit to Meta)

Submit this in Meta Business Manager under Templates > Create Template > Category: Utility:

code
Name: gstr3b_reminder_v2
  Category: Utility
  Language: English
  
  Body:
  Hi {{1}},
  GSTR-3B for {{2}} is due {{3}}.
  
  Please confirm:
  - READY: filing data ready, share via portal
  - EXTEND: need extra time (we will reach out)
  - SKIP: NIL return for this period
  
  — [CA firm name] auto-reminder
  
  Buttons:
  [Quick reply] READY
  [Quick reply] EXTEND
  [Quick reply] SKIP

Three variables: {{1}} = client name, {{2}} = GSTIN, {{3}} = due date. Three quick-reply buttons. Meta approves utility templates with quick-reply buttons in 4-12 hours typically. Template approval is per business account, so once approved, you can use it across all clients of that account.

The credentials checklist

1
Meta Business — WhatsApp Cloud API
Create Meta Business app, add WhatsApp product, generate permanent system-user access token (not the 24-hour debug one). Note Phone Number ID. Verify the firm's business display name to avoid the generic "Business Account" label on client phones.
2
Notion — internal integration + 2 databases
Build "GSTR_clients" (name, gstin, phone, partner_incharge, active) and "GSTR_replies" (gstin, period, action, replied_at, reminder_sent_at) databases. Create internal integration. Share both databases with the integration.
3
Meta webhook subscription for incoming messages
In Meta Business app settings, configure the webhook URL to point at n8n's reply-handler webhook. Subscribe to "messages" field. Verify token matches what n8n expects. Test with a real button tap from your own phone first.
4
Client opt-in evidence — keep an audit log
Every WhatsApp recipient must have opted in. The CA firm signs an engagement letter that includes WhatsApp comms consent — store the signed PDF link in the Notion client row. If Meta audits, you have proof.
WhatsApp utility templates are NOT marketing templates. If you submit this template under category Marketing, India delivery cost per message jumps 7x. Always submit as Utility for filing reminders. Meta categorisation rules: utility = transaction-related to a service the user already uses. Filing reminders qualify as utility.

The pre-flight checklist (we run this on the 17th of every month)

  • WhatsApp template still approved (Meta sometimes silently expires templates; check status weekly)
  • Notion GSTR_clients database has zero stale rows (clients off-boarded marked inactive)
  • All client phone numbers in E.164 format (+91 prefix, no spaces, no dashes)
  • Reply webhook subscription confirmed working with a test button tap from your own phone
  • Permanent system-user access token verified (not the 24-hour debug token that expires)
  • Partner-incharge column populated for every client (chaser DMs need it)
  • Notion replies database has the right schema (action, gstin, phone, replied_at)
  • Error workflow set — failed sends should not block the rest of the loop
  • Manual test send to a known phone scheduled for the 17th evening
  • Backup notification path (email) for the 19th chaser if WhatsApp send fails

Common mistakes — symptoms first, fixes second

Symptom: "Template send returns 132001 invalid template." Cause: template name in API call does not match the approved template name (case-sensitive, includes version suffix). Fix: copy the template name from Meta Business Manager exactly. We use gstr3b_reminder_v2 after the v1 had a typo we fixed.

Symptom: "Client gets the message but the buttons are missing on iOS." Cause: iOS WhatsApp version below 23.10 has known button-rendering bugs. Fix: nothing you can do server-side. Most users are on current versions; for the few stragglers, the message body still works (READY / EXTEND / SKIP can be typed as plain text replies and parsed by a regex fallback).

Symptom: "Reply webhook fires but client name lookup returns nothing." Cause: phone number in Meta webhook is in different format from what we stored (e.g., 919876543210 vs +919876543210). Fix: normalise both ends to E.164 with explicit + sign. The reply handler should strip the + and prepend it consistently.

Symptom: "Same client received the reminder twice in 5 minutes." Cause: Meta retried the API call after a network blip and we did not idempotency-protect. Fix: add a message_id deduplication table — for each unique (client_phone, period) pair, only allow one send per day.

Symptom: "Partners complain the chaser DMs are too noisy." Cause: chaser fires 24 hours after the original send for any non-reply, including clients who replied via email outside the WhatsApp flow. Fix: add an "alternative_response" column to Notion that the partner can tick — chaser skips clients with that ticked.

Symptom: "WhatsApp limit of 1,000 unique users in 24 hours hit on month-end." Cause: tier-1 WhatsApp Business accounts have a 1,000-recipient daily cap. Fix: spread sends over 2 hours via the SplitInBatches node with a 5-second delay, or apply for tier-2 (10,000/day) once you have 1+ months of clean send history.

The reply handler in detail

When a client taps READY, the webhook handler updates Notion in 3 steps:

  • 1. Parse the payload (READY_27ABCDE1234F1Z5 → action=READY, gstin=27ABCDE1234F1Z5).
  • 2. Query Notion GSTR_clients for the row with that gstin.
  • 3. Update the GSTR_replies database with a new row: client (relation), period (current month), action, replied_at, message_id.
  • A separate Slack DM goes to the partner-incharge: "Pinky Textiles confirmed READY for Nov 2025 GSTR-3B at 09:14 IST." Partners report this single change has improved their internal tracking more than anything else they have tried.

    For more on how we wire similar Notion + Slack patterns, see our client-onboarding workflow. The 22-node onboarding flow uses the same Notion + WhatsApp pattern with different triggers.

    Mini case study — 4-partner CA firm in Indore, 3 months live

    The first install ran from October 18, 2025. Three monthly runs since. October numbers: 240 clients sent, 211 replies via button (88%), 17 EXTEND requests handled, 12 SKIP for NIL returns. November numbers: 240 sent, 217 replies (90%). December (today, 18th): 240 sent at 09:00, 152 replies in the first 4 hours. The two associates who used to spend the day on this now handle filing prep — one of them mentioned "I had time to read the GST council notification properly for the first time in two years."

    For another high-volume Indian SMB workflow we built, see the BFCM cart-recovery flows — same WhatsApp Cloud API pattern with different triggers.

    When not to build this

    Skip this if (a) your client list is under 50 — the build cost is hard to amortise against ~5 hours of associate time saved per month, (b) your clients are not on WhatsApp — for older or extremely traditional client bases, this fails entirely; SMS is a worse fallback because two-way SMS in India is expensive and slow, or (c) you have ongoing trust issues with clients who suspect "auto-messages" — sometimes a text from the partner's personal number is what wins. We turned down a Mumbai chartered firm in 2025 for reason (c) — their senior partners did not want the firm to look automated.

    For more on the AI automation services we offer to professional service firms, see the Softechinfra services overview for the full lineup.

    FAQ

    Do clients need to opt in to receive WhatsApp from the firm?

    Yes, every recipient needs documented consent. We add a clause to the firm's engagement letter: "I consent to receiving filing reminders, document requests, and other transactional updates via WhatsApp at the registered mobile." The signed PDF lives in the client's Notion row.

    What happens if a client replies with free text instead of a button?

    The reply still arrives at the webhook. A fallback Code node looks for keywords (READY, EXTEND, SKIP, ready, "kar diya", "ho gaya") and tries to match. If no match, the message is forwarded to the partner-incharge via Slack with the raw text — they handle it manually.

    How does the firm bill clients for SKIP/NIL returns?

    Separately. The SKIP button just changes the workflow downstream — no filing data requested, just a one-line Notion entry. The firm's own billing system (we have not touched that) handles the NIL filing fee.

    What if a client wants to switch off these reminders?

    A "active" boolean column in the Notion GSTR_clients database. Set to false → client skipped on the next month's send. We also support a STOP keyword in the reply handler that auto-unsubscribes for 90 days.

    Does this work for GSTR-1 too?

    Yes, by changing the cron to day 11 (GSTR-1 due date is the 11th for monthly filers). The same template structure works — just rename to gstr1_reminder_v1. We have shipped the GSTR-1 variant for two firms.

    How does the firm handle TDS / TCS reminders?

    The same pattern, different cron (7th of each month for TDS). We are building a unified "compliance_reminder" workflow that reads a master Notion calendar and fires the right template per due date. Should ship in Q1 2026.

    What about client GSTIN changes (mergers, demergers)?

    The Notion row is the source of truth. The CA firm's account manager updates the GSTIN there; the next month's send picks up the new value. We log GSTIN history in a separate "client_history" Notion database for audit.

    Want this GSTR reminder system built for your CA practice?

    We ship the full 19-node flow — n8n on your infra, Meta Business + Notion setup, WhatsApp template approval, partner training — in 7 working days. Suitable for any CA or compliance firm with 50+ active clients on monthly GST cycles.

    Book a 20-min Call

    For more on Hrishikesh's perspective on automating professional services workflows, see the team page.

    Tags:
    n8nWhatsApp Cloud APIGSTCA FirmGSTR-3BNotionWorkflow AutomationCompliance
    Share this post:
    Hrishikesh Baidya

    Hrishikesh Baidya

    CTO at Softechinfra specializing in Python, system architecture, and building secure, scalable software solutions.