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.

AI agentAdvanced Saves ~10 hours/month AI AgentsBy Flowngn team

Works with n8n · Make · Zapier

How it works

Trigger: Schedule — weekdays at 9:00
  1. The agent reads your Invoices sheet.
  2. For each unpaid invoice past its due date, it picks the next step based on how late it is.
  3. It drafts a reminder email in Gmail (friendly, then firmer) and notes it in the sheet.
  4. 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.

System prompt
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

trueWeekdays 9:00Schedule TriggerGRead invoicesGoogle Sheets{ }Collect overdueCode⑂Any overdue?IfCollections agentAI AgentClaude modelAnthropic Chat Mo…GDraft reminderGmail ToolGUpdate invoiceGoogle Sheets ToolSAlert teamSlack Tool
Trigger App Logic Code AI Agent tool

Code

JavaScript used in the Code step — already included in the template below.

Collect overdue · Code node
// 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.

Workflow JSON
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": []
}

Open in the playground to edit →

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