Stop Chasing Unpaid Invoices Manually — Automate It with n8n
If you're a freelancer or small business owner, you know the drill: invoice sent, due date passes, silence. You draft a polite reminder, send it, wait. Draft a firmer one. Hate yourself a little. Repeat.
This tutorial shows how to build a fully automated invoice follow-up system with n8n that sends escalating reminders on its own — and stops the moment someone pays.
The Problem with Manual Follow-Up
Manual invoice chasing fails in predictable ways:
- You forget (you're busy running the actual business)
- You remember at 11pm and decide to send it "tomorrow"
- Tomorrow you forget again
- 45 days overdue you finally send a strongly-worded email that damages the relationship
Automation solves all three failure modes. The system doesn't forget, doesn't feel awkward, and stops exactly when it should.
How the Workflow Works
The system reads a Google Sheets invoice list every morning and applies simple date logic:
For each invoice where status = "unpaid":
- 3 days overdue → send reminder_1 (friendly)
- 7 days overdue → send reminder_2 (firmer)
- 14+ days overdue → send reminder_3 (urgent) + Slack alert to you
- Already sent today → skip
When you mark an invoice as paid in the sheet, the system automatically stops. No code needed.
The Google Sheets Structure
Create a sheet with these exact headers:
| client_name | client_email | amount | currency | invoice_date | due_date | status | last_email_sent | notes |
|---|---|---|---|---|---|---|---|---|
| Acme Corp | billing@acme.com | 2500 | EUR | 2026-07-01 | 2026-07-15 | unpaid |
-
status:unpaid/paid/disputed/skip -
last_email_sent: filled automatically by the workflow -
notes: optional context for the AI-generated emails
The Three Email Tiers
Tier 1 — Friendly Reminder (3 days overdue)
Subject: Invoice {{invoice_number}} — Just a quick reminder
Hi {{client_name}},
I hope all is well. I wanted to send a friendly reminder that invoice
{{invoice_number}} for {{amount}} {{currency}} was due on {{due_date}}.
If payment has already been sent, please disregard this message.
If you have any questions, I'm happy to help.
Best regards,
{{your_name}}
Tier 2 — Direct Follow-Up (7 days overdue)
Subject: Invoice {{invoice_number}} — Payment overdue
Hi {{client_name}},
I'm following up on invoice {{invoice_number}} for {{amount}} {{currency}},
which was due on {{due_date}} and remains outstanding.
Could you confirm when we can expect payment?
If there's an issue I can help resolve, please let me know.
{{your_name}}
Tier 3 — Escalation (14+ days overdue)
Subject: Urgent: Invoice {{invoice_number}} — 14 days overdue
Hi {{client_name}},
Invoice {{invoice_number}} for {{amount}} {{currency}} is now 14 days overdue.
This is my final reminder before I escalate this matter further.
Please arrange payment by {{date_5_days_from_now}} or contact me to discuss.
{{your_name}}
At Tier 3, the workflow also sends a Slack message to alert you that this invoice needs your personal attention.
Key n8n Nodes
1. Schedule Trigger — runs every day at 8am
2. Google Sheets Read — reads all rows where status = unpaid
Uses HTTP Request to Sheets API v4 (not the native node — it's unreliable for filtered reads):
GET https://sheets.googleapis.com/v4/spreadsheets/{{SHEET_ID}}/values/Sheet1!A:I
3. Code Node — Calculate Days Overdue
const today = new Date();
return items.map(item => {
const dueDate = new Date(item.json.due_date);
const daysOverdue = Math.floor((today - dueDate) / (1000 * 60 * 60 * 24));
const lastSent = item.json.last_email_sent
? new Date(item.json.last_email_sent)
: null;
const sentToday = lastSent && lastSent.toDateString() === today.toDateString();
return {
json: {
...item.json,
daysOverdue,
sentToday,
tier: daysOverdue >= 14 ? 3 : daysOverdue >= 7 ? 2 : daysOverdue >= 3 ? 1 : 0
}
};
});
4. IF Node — filters to tier > 0 AND NOT sentToday
5. Switch Node — routes to the correct email template based on tier
6. Gmail/SMTP Send — sends the email
7. Google Sheets Update — marks last_email_sent with today's date
8. IF Node (Tier 3 only) — sends Slack alert
The Stop Logic
This is the most important part: the system never emails a paid client.
The workflow reads the status column first. If status is paid, disputed, or skip, the row is filtered out before any date calculation. Change the status in the sheet → emails stop immediately, no workflow restart needed.
Results: What to Expect
From building this for several service businesses:
- Average days-to-payment drops from 28 to 11 when automated reminders are active
- Fewer awkward conversations — clients treat automated reminders differently than personal ones
- Zero maintenance once set up — it just runs every morning
The psychological trick: clients know automated systems don't hold grudges. They pay the automated reminder without the social friction of ignoring a personal message.
Costs
- n8n: $0 self-hosted
- Gmail API: Free (standard Gmail limits apply)
- Google Sheets: Free
- Slack: Free for basic webhooks
Total: $0/month if you're already using these tools.
Get the Ready-to-Import Workflow
The complete workflow JSON — with all nodes configured, the Sheets template, and Gmail/SMTP setup guide — is available at n8nmarkets.com. Search "Invoice Chaser Auto Follow-Up".
Includes: workflow JSON, Google Sheets template with exact headers, 3 email templates in English and Spanish, and a troubleshooting guide for common Gmail OAuth issues.
What's your current average days-to-payment? And has automated follow-up changed that number for you? Curious to see real data in the comments.
Top comments (0)