It is 3 AM. You are awake thinking about that invoice that is 23 days overdue. The client said "next week" two weeks ago. You know you need to follow up, but you also know that awkward phone call is coming.
There is a better way.
I built an n8n workflow that handles invoice follow-ups automatically. It sends polite reminders at 7, 14, and 21 days overdue. At 30 days, it escalates to a firmer tone and flags the account in your CRM. At 45 days, it drafts a collections letter for your review.
Here is the complete workflow with importable JSON.
What the System Does
Day 7 overdue: Friendly reminder
"Hi [Name], just checking if you saw invoice #[1234] for $[amount]. Due [date]. Let me know if you need anything!"
Day 14 overdue: Polite nudge with attachment
"Hi [Name], following up on invoice #[1234] for $[amount]. Attached another copy. When can we expect payment?"
Day 21 overdue: Direct ask
"Hi [Name], invoice #[1234] is now 3 weeks overdue. Please confirm payment date by end of week."
Day 30 overdue: Escalation + CRM flag
"Hi [Name], invoice #[1234] is 30 days overdue. This account is flagged for review. Payment required within 5 business days."
Day 45 overdue: Collections draft
Generates a formal collections letter for your review before sending.
All of this happens automatically. You get a daily digest email at 9 AM showing what was sent.
The n8n Workflow
Import this JSON directly into n8n (Settings → Import from JSON):
{
"name": "Automated Invoice Dunning System",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9 * * *"
}
]
}
},
"id": "daily-trigger",
"name": "Every Day 9AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [0, 0]
},
{
"parameters": {
"operation": "read",
"documentId": {
"__rl": true,
"value": "YOUR_INVOICE_TRACKER_ID",
"mode": "id"
},
"range": "Invoices!A:Z"
},
"id": "read-invoices",
"name": "Read Invoice Tracker",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [250, 0]
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.Status }}",
"value2": "Overdue"
}
]
}
},
"id": "filter-overdue",
"name": "Filter Overdue Invoices",
"type": "n8n-nodes-base.filter",
"typeVersion": 1,
"position": [500, 0]
},
{
"parameters": {
"mode": "combine",
"combinationMode": "multiplex"
},
"id": "calculate-days",
"name": "Calculate Days Overdue",
"type": "n8n-nodes-base.merge",
"typeVersion": 3,
"position": [750, 0]
},
{
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.daysOverdue }}",
"operation": "smallerEqual",
"value2": "7"
},
{
"value1": "={{ $json.daysOverdue }}",
"operation": "greater",
"value2": "6"
}
]
}
},
"id": "check-7-days",
"name": "7 Days Overdue?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [1000, -200]
},
{
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.daysOverdue }}",
"operation": "smallerEqual",
"value2": "14"
},
{
"value1": "={{ $json.daysOverdue }}",
"operation": "greater",
"value2": "13"
}
]
}
},
"id": "check-14-days",
"name": "14 Days Overdue?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [1000, -100]
},
{
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.daysOverdue }}",
"operation": "smallerEqual",
"value2": "21"
},
{
"value1": "={{ $json.daysOverdue }}",
"operation": "greater",
"value2": "20"
}
]
}
},
"id": "check-21-days",
"name": "21 Days Overdue?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [1000, 0]
},
{
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.daysOverdue }}",
"operation": "smallerEqual",
"value2": "30"
},
{
"value1": "={{ $json.daysOverdue }}",
"operation": "greater",
"value2": "29"
}
]
}
},
"id": "check-30-days",
"name": "30 Days Overdue?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [1000, 100]
},
{
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.daysOverdue }}",
"operation": "smallerEqual",
"value2": "45"
},
{
"value1": "={{ $json.daysOverdue }}",
"operation": "greater",
"value2": "44"
}
]
}
},
"id": "check-45-days",
"name": "45 Days Overdue?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [1000, 200]
},
{
"parameters": {
"model": "gpt-4o-mini",
"messages": {
"values": [
{
"content": "Write a friendly invoice reminder email."
},
{
"content": "=Write a friendly 7-day overdue invoice reminder.\n\nClient: {{ $json.ClientName }}\nInvoice: #{{ $json.InvoiceNumber }}\nAmount: ${{ $json.Amount }}\nDue Date: {{ $json.DueDate }}\n\nKeep it casual. One paragraph. No guilt."
}
]
}
},
"id": "email-7day",
"name": "Generate 7-Day Email",
"type": "n8n-nodes-base.openAi",
"typeVersion": 1.8,
"position": [1250, -200]
},
{
"parameters": {
"model": "gpt-4o-mini",
"messages": {
"values": [
{
"content": "Write a polite invoice follow-up with attachment mention."
},
{
"content": "=Write a polite 14-day overdue invoice follow-up.\n\nClient: {{ $json.ClientName }}\nInvoice: #{{ $json.InvoiceNumber }}\nAmount: ${{ $json.Amount }}\nDue Date: {{ $json.DueDate }}\n\nMention you are attaching another copy. Ask for expected payment date. One paragraph."
}
]
}
},
"id": "email-14day",
"name": "Generate 14-Day Email",
"type": "n8n-nodes-base.openAi",
"typeVersion": 1.8,
"position": [1250, -100]
},
{
"parameters": {
"model": "gpt-4o-mini",
"messages": {
"values": [
{
"content": "Write a direct invoice reminder."
},
{
"content": "=Write a direct 21-day overdue invoice reminder.\n\nClient: {{ $json.ClientName }}\nInvoice: #{{ $json.InvoiceNumber }}\nAmount: ${{ $json.Amount }}\nDue Date: {{ $json.DueDate }}\n\nBe direct. Ask for payment confirmation by end of week. One paragraph."
}
]
}
},
"id": "email-21day",
"name": "Generate 21-Day Email",
"type": "n8n-nodes-base.openAi",
"typeVersion": 1.8,
"position": [1250, 0]
},
{
"parameters": {
"model": "gpt-4o-mini",
"messages": {
"values": [
{
"content": "Write an escalated invoice notice."
},
{
"content": "=Write a 30-day overdue escalated invoice notice.\n\nClient: {{ $json.ClientName }}\nInvoice: #{{ $json.InvoiceNumber }}\nAmount: ${{ $json.Amount }}\nDue Date: {{ $json.DueDate }}\n\nMention account is flagged for review. Require payment within 5 business days. Professional but firm."
}
]
}
},
"id": "email-30day",
"name": "Generate 30-Day Email",
"type": "n8n-nodes-base.openAi",
"typeVersion": 1.8,
"position": [1250, 100]
},
{
"parameters": {
"model": "gpt-4o-mini",
"messages": {
"values": [
{
"content": "Draft a collections letter."
},
{
"content": "=Draft a formal collections letter for 45-day overdue invoice.\n\nClient: {{ $json.ClientName }}\nInvoice: #{{ $json.InvoiceNumber }}\nAmount: ${{ $json.Amount }}\nDue Date: {{ $json.DueDate }}\n\nFormal tone. Mention potential next steps. This is for internal review before sending."
}
]
}
},
"id": "email-45day",
"name": "Generate Collections Draft",
"type": "n8n-nodes-base.openAi",
"typeVersion": 1.8,
"position": [1250, 200]
},
{
"parameters": {
"fromEmail": "your-email@domain.com",
"toEmail": "={{ $json.ClientEmail }}",
"subject": "=Invoice #{{ $json.InvoiceNumber }} Reminder",
"text": "={{ $node['Generate 7-Day Email'].json.message.content }}"
},
"id": "send-7day",
"name": "Send 7-Day Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [1500, -200]
},
{
"parameters": {
"fromEmail": "your-email@domain.com",
"toEmail": "={{ $json.ClientEmail }}",
"subject": "=Invoice #{{ $json.InvoiceNumber }} Follow-Up",
"text": "={{ $node['Generate 14-Day Email'].json.message.content }}"
},
"id": "send-14day",
"name": "Send 14-Day Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [1500, -100]
},
{
"parameters": {
"fromEmail": "your-email@domain.com",
"toEmail": "={{ $json.ClientEmail }}",
"subject": "=Invoice #{{ $json.InvoiceNumber }} - Payment Required",
"text": "={{ $node['Generate 21-Day Email'].json.message.content }}"
},
"id": "send-21day",
"name": "Send 21-Day Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [1500, 0]
},
{
"parameters": {
"fromEmail": "your-email@domain.com",
"toEmail": "={{ $json.ClientEmail }}",
"subject": "=Invoice #{{ $json.InvoiceNumber }} - Account Flagged",
"text": "={{ $node['Generate 30-Day Email'].json.message.content }}"
},
"id": "send-30day",
"name": "Send 30-Day Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [1500, 100]
},
{
"parameters": {
"operation": "update",
"documentId": {
"__rl": true,
"value": "YOUR_CRM_ID",
"mode": "id"
},
"range": "Clients!A:Z",
"options": {
"valueInputMode": "RAW"
}
},
"id": "flag-crm",
"name": "Flag Account in CRM",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [1750, 100]
},
{
"parameters": {
"fromEmail": "your-email@domain.com",
"toEmail": "your-email@domain.com",
"subject": "=Daily Invoice Follow-Up Digest — {{ $today.toFormat('MMM d, y') }}",
"text": "={{ $nodes.all.map(n => n.json).filter(i => i.sent).map(i => '- Sent ' + i.emailType + ' to ' + i.ClientName + ' for Invoice #' + i.InvoiceNumber).join('\n') }}"
},
"id": "daily-digest",
"name": "Send Daily Digest",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [2000, 0]
}
],
"connections": {
"Every Day 9AM": {
"main": [[{ "node": "Read Invoice Tracker", "type": "main", "index": 0 }]]
},
"Read Invoice Tracker": {
"main": [[{ "node": "Filter Overdue Invoices", "type": "main", "index": 0 }]]
},
"Filter Overdue Invoices": {
"main": [[{ "node": "Calculate Days Overdue", "type": "main", "index": 0 }]]
},
"Calculate Days Overdue": {
"main": [
[{ "node": "7 Days Overdue?", "type": "main", "index": 0 }],
[{ "node": "14 Days Overdue?", "type": "main", "index": 0 }],
[{ "node": "21 Days Overdue?", "type": "main", "index": 0 }],
[{ "node": "30 Days Overdue?", "type": "main", "index": 0 }],
[{ "node": "45 Days Overdue?", "type": "main", "index": 0 }]
]
}
}
}
Setup Instructions
1. Create Your Invoice Tracker
Google Sheet with these columns:
| InvoiceNumber | ClientName | ClientEmail | Amount | DueDate | Status | LastFollowUp |
|---|---|---|---|---|---|---|
| 1234 | ACME Corp | jane@acme.com | 4200 | 2026-06-01 | Overdue | 2026-06-08 |
2. Add a Status Formula
In the Status column, use:
=IF(TODAY()>DueDate, IF(PaidDate<>"", "Paid", "Overdue"), "Current")
3. Connect Credentials
- Gmail OAuth2 — for sending emails
- Google Sheets OAuth2 — for invoice tracker and CRM
- OpenAI API — for email generation
4. Customize Email Templates
The AI prompts in the workflow generate emails automatically. You can adjust the tone by editing the prompts:
- More casual: "Keep it friendly, like you are texting a colleague"
- More formal: "Use professional business letter tone"
- Add your brand voice: "Write like [your company] — helpful but direct"
What This Costs
| Component | Monthly Cost |
|---|---|
| n8n (self-hosted) | $0 |
| OpenAI API (~20 emails × 200 tokens) | ~$0.08/mo |
| Google Workspace | Already paying |
| Total additional | $0.08/mo |
Why This Works
Removes the awkwardness. You are not the bad guy chasing money. The system is. Clients respond to consistent, professional follow-ups.
Catches everything. No invoice slips through because you forgot. The workflow runs every day at 9 AM.
Escalates appropriately. A 7-day reminder is friendly. A 30-day notice is firm. The tone matches the situation.
Creates a paper trail. Every email is logged. If you need to escalate to collections or small claims, you have documentation.
Frees your mental RAM. You are not thinking about overdue invoices at 3 AM. The system handles it.
Common Questions
"What if the client replies?" Set up a Gmail filter to label replies to invoice reminders. Add a node that checks for replies and pauses the workflow for that invoice.
"Can I use this with QuickBooks or Xero?" Yes. Replace the Google Sheets nodes with QuickBooks/Xero API nodes. The workflow logic stays the same.
"What about different currencies?" Add a currency column to your tracker. Update the email template to include the currency symbol: ={{ $json.Currency }}{{ $json.Amount }}.
"Should I automate the 45-day collections letter?" No. That one should always be manual review. The workflow drafts it and emails it to you. You decide whether to send.
What to Do Next
If you want more n8n workflows like this, there is a Boring Automation Pack with 5 complete workflows (including invoice follow-ups, weekly reviews, and lead scoring) at smbscaleup.gumroad.com/l/boring-automation-pack.
It is $15 CAD and includes all the n8n JSON files plus setup checklists.
There is also a free AI Automation Cheat Sheet with 15 copy-paste prompts at ai-automation-cheat-sheet.vercel.app.
We build these tools for our own business and share what works. If you set this up, drop a comment below — I would love to hear how it goes.











