Unpaid invoices collections AI agent
Every morning, the agent reviews overdue invoices, drafts polite reminders in the right tone, and flags the ones that need a call.
How it works
- The agent reads your Invoices sheet.
- For each unpaid invoice past its due date, it picks the next step based on how late it is.
- It drafts a reminder email in Gmail (friendly, then firmer) and notes it in the sheet.
- Invoices over 30 days late are posted to Slack for a person to call.
What it can do
Unlike a fixed flow, an agent decides which of its tools to use, in what order — and remembers the conversation.
- Chooses the tone by days overdue and reminders already sent
- Drafts emails in the client's language with invoice number, amount and payment link
- Keeps the sheet updated with the last reminder date
- Sends the team a short daily summary
System prompt
The instructions that shape the agent. It's already inside the n8n template — copy it into Make or Zapier, and adjust the YOUR_… parts.
You are the accounts receivable assistant for YOUR_COMPANY. Today is {{ $now.toFormat('cccc d LLLL yyyy') }}.
You get a list of overdue invoices. For EACH invoice, decide the next step:
- 1–7 days late, no reminder in the last 5 days: a friendly reminder ("in case it slipped through").
- 8–30 days late, no reminder in the last 5 days: a clear, polite reminder asking for a payment date.
- Over 30 days late: draft a firm but polite reminder AND include it in the team alert so someone calls the client.
- A reminder sent in the last 5 days: do nothing for that invoice.
Tools:
- "Draft reminder": creates a Gmail draft (never sends). Write in the client's language if it's clear from their name or company; otherwise English. Include the invoice number, amount, days overdue and the payment link YOUR_PAYMENT_LINK. Sign as YOUR_NAME.
- "Update invoice": records that a reminder was drafted. Call it once per invoice you drafted for, with the new reminder count.
- "Alert team": posts one message to Slack. Call it once at the end with a short summary: how many drafts, and the invoices over 30 days that need a call.
Rules:
- Never threaten, mention legal action, add late fees or offer discounts.
- Never invent amounts, dates or invoice numbers; use only the data you were given.
- Finish with a one-line summary of what you did.Safety rules
Built into the prompt and the tools:
- Only drafts emails, never sends them
- Never threatens, adds fees or offers discounts
- Skips invoices marked as disputed
Uses your own AI key (Claude, OpenAI or another model). Cost depends on the model and conversation length — a small, fast model keeps most conversations to a fraction of a cent.
Diagram
Code
JavaScript used in the Code step — already included in the template below.
// Invoices sheet columns: invoice | client | email | amount | currency | due_date | status | reminders | last_reminder
// Gathers every unpaid, undisputed invoice past its due date into one list for the agent.
const DAY = 24 * 60 * 60 * 1000;
const today = new Date();
today.setHours(0, 0, 0, 0);
const overdue = $input.all()
.map((item) => item.json)
.filter((r) => !["paid", "disputed"].includes(String(r.status).toLowerCase()))
.filter((r) => r.email && new Date(r.due_date) < today)
.map((r) => ({
invoice: r.invoice,
client: r.client,
email: r.email,
amount: `${r.amount} ${r.currency || ""}`.trim(),
days_late: Math.floor((today - new Date(r.due_date)) / DAY),
reminders_sent: Number(r.reminders) || 0,
last_reminder: r.last_reminder || "never",
}));
return [{ json: { count: overdue.length, invoices: overdue } }];Build it
Pick your tool — same automation, step by step.
Copy and paste straight into an n8n canvas (Ctrl+V), or download and use Import from File. Then connect your own credentials and replace every YOUR_… placeholder. AI steps use a Claude model node — swap it for OpenAI, Gemini or Ollama if you prefer.
Show code (322 lines)
{
"name": "Unpaid invoices collections AI agent (Flowngn)",
"nodes": [
{
"parameters": {
"content": "## Unpaid invoices collections AI agent\nEvery morning, the agent reviews overdue invoices, drafts polite reminders in the right tone, and flags the ones that need a call.\n\n**Setup**\n1. Open each node and connect your own credentials.\n2. Replace every YOUR_… placeholder.\n3. Test once, then activate.\n\nFree template from Flowngn",
"height": 260,
"width": 340
},
"id": "2c527986-e838-40f5-a594-fbb4654a5a4e",
"name": "Setup notes",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-420,
-60
]
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9 * * 1-5"
}
]
}
},
"id": "2c527986-e838-40f5-a594-fbb4654a5a4e",
"name": "Weekdays 9:00",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
0,
0
]
},
{
"parameters": {
"operation": "read",
"documentId": {
"__rl": true,
"value": "YOUR_SPREADSHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "Invoices",
"mode": "name"
},
"options": {}
},
"id": "2c527986-e838-40f5-a594-fbb4654a5a4e",
"name": "Read invoices",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
300,
0
]
},
{
"parameters": {
"jsCode": "// Invoices sheet columns: invoice | client | email | amount | currency | due_date | status | reminders | last_reminder\n// Gathers every unpaid, undisputed invoice past its due date into one list for the agent.\nconst DAY = 24 * 60 * 60 * 1000;\nconst today = new Date();\ntoday.setHours(0, 0, 0, 0);\n\nconst overdue = $input.all()\n .map((item) => item.json)\n .filter((r) => ![\"paid\", \"disputed\"].includes(String(r.status).toLowerCase()))\n .filter((r) => r.email && new Date(r.due_date) < today)\n .map((r) => ({\n invoice: r.invoice,\n client: r.client,\n email: r.email,\n amount: `${r.amount} ${r.currency || \"\"}`.trim(),\n days_late: Math.floor((today - new Date(r.due_date)) / DAY),\n reminders_sent: Number(r.reminders) || 0,\n last_reminder: r.last_reminder || \"never\",\n }));\n\nreturn [{ json: { count: overdue.length, invoices: overdue } }];"
},
"id": "2c527986-e838-40f5-a594-fbb4654a5a4e",
"name": "Collect overdue",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
600,
0
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "c1",
"leftValue": "={{ $json.count }}",
"rightValue": 0,
"operator": {
"type": "number",
"operation": "gt"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "2c527986-e838-40f5-a594-fbb4654a5a4e",
"name": "Any overdue?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
900,
0
]
},
{
"parameters": {
"promptType": "define",
"text": "=Overdue invoices:\n{{ JSON.stringify($json.invoices, null, 2) }}",
"options": {
"systemMessage": "=You are the accounts receivable assistant for YOUR_COMPANY. Today is {{ $now.toFormat('cccc d LLLL yyyy') }}.\n\nYou get a list of overdue invoices. For EACH invoice, decide the next step:\n- 1–7 days late, no reminder in the last 5 days: a friendly reminder (\"in case it slipped through\").\n- 8–30 days late, no reminder in the last 5 days: a clear, polite reminder asking for a payment date.\n- Over 30 days late: draft a firm but polite reminder AND include it in the team alert so someone calls the client.\n- A reminder sent in the last 5 days: do nothing for that invoice.\n\nTools:\n- \"Draft reminder\": creates a Gmail draft (never sends). Write in the client's language if it's clear from their name or company; otherwise English. Include the invoice number, amount, days overdue and the payment link YOUR_PAYMENT_LINK. Sign as YOUR_NAME.\n- \"Update invoice\": records that a reminder was drafted. Call it once per invoice you drafted for, with the new reminder count.\n- \"Alert team\": posts one message to Slack. Call it once at the end with a short summary: how many drafts, and the invoices over 30 days that need a call.\n\nRules:\n- Never threaten, mention legal action, add late fees or offer discounts.\n- Never invent amounts, dates or invoice numbers; use only the data you were given.\n- Finish with a one-line summary of what you did."
}
},
"id": "2c527986-e838-40f5-a594-fbb4654a5a4e",
"name": "Collections agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 1.9,
"position": [
1200,
0
]
},
{
"parameters": {
"model": {
"__rl": true,
"value": "claude-sonnet-5",
"mode": "list"
},
"options": {}
},
"id": "2c527986-e838-40f5-a594-fbb4654a5a4e",
"name": "Claude model",
"type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
"typeVersion": 1.3,
"position": [
945,
220
]
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Creates a Gmail draft reminder to a client (never sends).",
"resource": "draft",
"operation": "create",
"subject": "={{ $fromAI('subject', \"Email subject, including the invoice number\", 'string') }}",
"message": "={{ $fromAI('body', \"Email body\", 'string') }}",
"options": {
"sendTo": "={{ $fromAI('to', \"The client's email address\", 'string') }}"
}
},
"id": "2c527986-e838-40f5-a594-fbb4654a5a4e",
"name": "Draft reminder",
"type": "n8n-nodes-base.gmailTool",
"typeVersion": 2.1,
"position": [
1115,
220
]
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Records that a reminder was drafted for an invoice.",
"operation": "update",
"documentId": {
"__rl": true,
"value": "YOUR_SPREADSHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "Invoices",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"invoice": "={{ $fromAI('invoice', \"The invoice number\", 'string') }}",
"reminders": "={{ $fromAI('reminders', \"New number of reminders sent\", 'number') }}",
"last_reminder": "={{ $now.toISODate() }}"
},
"matchingColumns": [
"invoice"
]
},
"options": {}
},
"id": "2c527986-e838-40f5-a594-fbb4654a5a4e",
"name": "Update invoice",
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.5,
"position": [
1285,
220
]
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Posts the daily collections summary to the team.",
"select": "channel",
"channelId": {
"__rl": true,
"value": "#finance",
"mode": "name"
},
"text": "={{ $fromAI('message', \"Drafts created, and invoices over 30 days that need a call\", 'string') }}",
"otherOptions": {}
},
"id": "2c527986-e838-40f5-a594-fbb4654a5a4e",
"name": "Alert team",
"type": "n8n-nodes-base.slackTool",
"typeVersion": 2.2,
"position": [
1455,
220
]
}
],
"connections": {
"Weekdays 9:00": {
"main": [
[
{
"node": "Read invoices",
"type": "main",
"index": 0
}
]
]
},
"Read invoices": {
"main": [
[
{
"node": "Collect overdue",
"type": "main",
"index": 0
}
]
]
},
"Collect overdue": {
"main": [
[
{
"node": "Any overdue?",
"type": "main",
"index": 0
}
]
]
},
"Any overdue?": {
"main": [
[
{
"node": "Collections agent",
"type": "main",
"index": 0
}
]
]
},
"Claude model": {
"ai_languageModel": [
[
{
"node": "Collections agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Draft reminder": {
"ai_tool": [
[
{
"node": "Collections agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Update invoice": {
"ai_tool": [
[
{
"node": "Collections agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Alert team": {
"ai_tool": [
[
{
"node": "Collections agent",
"type": "ai_tool",
"index": 0
}
]
]
}
},
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"meta": {
"templateCredsSetupCompleted": false
},
"tags": []
}In Make, create a new scenario and add these modules in order:
- Schedule the scenario: Weekdays 9:00
- Google Sheets › Search Rows
- Tools › Set variable / Iterator + Array aggregator (same logic as the Code step)
- Filter on the next route: “Any overdue?”
- If yesMake AI Agents › Run an agent — give it these tools: “Draft reminder” (Gmail), “Update invoice” (Google Sheets), “Alert team” (Slack); paste the system prompt as the agent's instructions
- Every module run uses Make operations — the free plan includes a monthly quota.
- Make's built-in functions (map, sum, formatDate…) can replace most Code steps; for complex logic use a code module.
- Module and step names can differ slightly as Make and Zapier update their apps.
In Zapier, create a new Zap and add these steps in order:
- Schedule by Zapier — Weekdays 9:00
- Google Sheets — Lookup Spreadsheet Rows (or Code by Zapier)
- Code by Zapier — Run JavaScript (paste the code from the Code section)
- Filter by Zapier: “Any overdue?”
- If yesZapier Agents — create an agent (or AI by Zapier for a single step) — give it these tools: “Draft reminder” (Gmail), “Update invoice” (Google Sheets), “Alert team” (Slack); paste the system prompt as the agent's instructions
- Zaps with more than two steps need a paid Zapier plan.
- Webhooks, Filters/Paths and Code by Zapier are premium features on some Zapier plans.
- Code by Zapier runs JavaScript: reuse the code shown above, reading inputs from inputData instead of $json.
- Module and step names can differ slightly as Make and Zapier update their apps.
Tips
- Review the drafts each morning for the first few weeks, then keep the ones that read well as examples in the prompt.
Related flows
Customer support AI agent
A chat agent that answers from your FAQ, looks up orders, and hands tricky cases to a human.
Inbox assistant AI agent
Reads each new email and decides: draft a reply, create a task, alert you — or ignore it.
Meeting notes → actions AI agent
Paste your meeting notes; the agent creates tasks with owners, drafts the follow-up email and posts a summary.