{
  "name": "Generate invoices and send payment reminders with escalation using Google Sheets",
  "nodes": [
    {
      "id": "sticky-overview",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -280,
        80
      ],
      "parameters": {
        "width": 400,
        "height": 560,
        "content": "## Generate invoices and send payment reminders with escalation\n\n### Who is this for?\nFreelancers, small businesses, and finance teams who need automated invoice management with intelligent payment follow-ups.\n\n### What this workflow does\n**Invoice Generation:**\n- Receives order data with line items\n- Splits and calculates each line item\n- Aggregates totals and saves to Sheets\n- Sends invoice email with payment link\n\n**Payment Reminders:**\n- Daily check for overdue invoices\n- Loops through unpaid items\n- 5-level escalation via Switch node\n- Slack notification for collections\n\n### Setup\n1. Create Google Sheet with invoice columns\n2. Connect Google Sheets and Gmail\n3. Configure Slack for escalations\n\n### Requirements\n- Google Sheets\n- Gmail account\n- Slack workspace"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-step1",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        180,
        180
      ],
      "parameters": {
        "color": 7,
        "width": 280,
        "height": 80,
        "content": "**Step 1: Invoice Creation**\nReceive order data, generate invoice ID, and initialize invoice fields."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-step2",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        560,
        180
      ],
      "parameters": {
        "color": 7,
        "width": 300,
        "height": 80,
        "content": "**Step 2: Line Item Processing**\nSplit Out each line item, calculate totals, and Aggregate results."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-step3",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1320,
        420
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 80,
        "content": "**Step 3: Daily Overdue Check**\nSchedule Trigger runs daily. Loop Over batches each invoice for processing."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-step4",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1980,
        180
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 80,
        "content": "**Step 4: Reminder Escalation**\nSwitch routes to 5 reminder levels based on days overdue. Collections at 60+ days."
      },
      "typeVersion": 1
    },
    {
      "id": "webhook-invoice",
      "name": "New Invoice Request",
      "type": "n8n-nodes-base.webhook",
      "position": [
        180,
        300
      ],
      "parameters": {
        "path": "create-invoice",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "init-invoice",
      "name": "Initialize Invoice",
      "type": "n8n-nodes-base.set",
      "position": [
        400,
        300
      ],
      "parameters": {
        "mode": "manual",
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "1",
              "name": "invoiceId",
              "type": "string",
              "value": "={{ 'INV-' + $now.format('yyyy') + '-' + String(Math.floor(Math.random() * 9000) + 1000) }}"
            },
            {
              "id": "2",
              "name": "clientName",
              "type": "string",
              "value": "={{ $json.body.clientName }}"
            },
            {
              "id": "3",
              "name": "clientEmail",
              "type": "string",
              "value": "={{ $json.body.clientEmail }}"
            },
            {
              "id": "4",
              "name": "lineItems",
              "type": "object",
              "value": "={{ $json.body.lineItems }}"
            },
            {
              "id": "5",
              "name": "paymentTerms",
              "type": "number",
              "value": "={{ $json.body.paymentTerms || 30 }}"
            },
            {
              "id": "6",
              "name": "taxRate",
              "type": "number",
              "value": "={{ $json.body.taxRate || 0.1 }}"
            },
            {
              "id": "7",
              "name": "createdAt",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            },
            {
              "id": "8",
              "name": "dueDate",
              "type": "string",
              "value": "={{ $now.plus({ days: $json.body.paymentTerms || 30 }).toISODate() }}"
            }
          ]
        },
        "duplicateItem": false
      },
      "typeVersion": 3.4
    },
    {
      "id": "split-line-items",
      "name": "Split Line Items",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        620,
        300
      ],
      "parameters": {
        "include": "allOtherFields",
        "options": {},
        "fieldToSplitOut": "lineItems"
      },
      "typeVersion": 1
    },
    {
      "id": "calc-line-total",
      "name": "Calculate Line Total",
      "type": "n8n-nodes-base.code",
      "position": [
        840,
        300
      ],
      "parameters": {
        "jsCode": "const item = $input.first().json;\nconst lineItem = item.lineItems;\n\nconst quantity = lineItem.quantity || 1;\nconst rate = lineItem.rate || 0;\nconst lineTotal = quantity * rate;\n\nreturn [{\n  json: {\n    invoiceId: item.invoiceId,\n    clientName: item.clientName,\n    clientEmail: item.clientEmail,\n    taxRate: item.taxRate,\n    dueDate: item.dueDate,\n    lineItem: {\n      description: lineItem.description,\n      quantity: quantity,\n      rate: rate,\n      lineTotal: lineTotal\n    }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "aggregate-lines",
      "name": "Aggregate Line Items",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        1060,
        300
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "processedLines"
      },
      "typeVersion": 1
    },
    {
      "id": "build-invoice",
      "name": "Build Invoice Object",
      "type": "n8n-nodes-base.code",
      "position": [
        1280,
        300
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst lines = data.processedLines || [];\n\nconst invoiceId = lines[0]?.invoiceId || 'unknown';\nconst clientName = lines[0]?.clientName || '';\nconst clientEmail = lines[0]?.clientEmail || '';\nconst taxRate = lines[0]?.taxRate || 0.1;\nconst dueDate = lines[0]?.dueDate || '';\n\nconst subtotal = lines.reduce((sum, l) => sum + (l.lineItem?.lineTotal || 0), 0);\nconst tax = Math.round(subtotal * taxRate * 100) / 100;\nconst total = subtotal + tax;\n\nconst paymentLink = `https://pay.company.com/invoice/${invoiceId}`;\n\nreturn [{\n  json: {\n    invoiceId: invoiceId,\n    clientName: clientName,\n    clientEmail: clientEmail,\n    lineItems: lines.map(l => l.lineItem),\n    subtotal: subtotal,\n    taxRate: taxRate,\n    tax: tax,\n    total: total,\n    dueDate: dueDate,\n    paymentLink: paymentLink,\n    status: 'pending',\n    createdAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "save-invoice",
      "name": "Save Invoice to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1500,
        300
      ],
      "parameters": {
        "columns": {
          "value": {
            "Tax": "={{ $json.tax }}",
            "Email": "={{ $json.clientEmail }}",
            "Total": "={{ $json.total }}",
            "Client": "={{ $json.clientName }}",
            "Status": "={{ $json.status }}",
            "Created": "={{ $json.createdAt }}",
            "Due Date": "={{ $json.dueDate }}",
            "Subtotal": "={{ $json.subtotal }}",
            "Invoice ID": "={{ $json.invoiceId }}",
            "Last Reminder": ""
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Invoices"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "send-invoice-email",
      "name": "Send Invoice Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1720,
        300
      ],
      "parameters": {
        "sendTo": "={{ $json.clientEmail }}",
        "message": "=Dear {{ $json.clientName }},\n\nPlease find your invoice details:\n\nInvoice: {{ $json.invoiceId }}\nAmount: ${{ $json.total.toFixed(2) }}\nDue Date: {{ $json.dueDate }}\n\nPay now: {{ $json.paymentLink }}\n\nThank you!",
        "options": {},
        "subject": "=Invoice {{ $json.invoiceId }} from Company"
      },
      "typeVersion": 2.1
    },
    {
      "id": "invoice-response",
      "name": "Invoice Created Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1940,
        300
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, invoiceId: $json.invoiceId, total: $json.total, dueDate: $json.dueDate }) }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "daily-check",
      "name": "Daily Overdue Check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1320,
        540
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "get-unpaid",
      "name": "Get Unpaid Invoices",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1540,
        540
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "pending",
              "lookupColumn": "Status"
            }
          ]
        },
        "operation": "getMany",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Invoices"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "calc-overdue",
      "name": "Calculate Overdue Days",
      "type": "n8n-nodes-base.code",
      "position": [
        1760,
        540
      ],
      "parameters": {
        "jsCode": "const invoices = $input.all();\nconst today = new Date();\n\nconst overdueInvoices = invoices.map(inv => {\n  const dueDate = new Date(inv.json['Due Date']);\n  const daysOverdue = Math.floor((today - dueDate) / (1000 * 60 * 60 * 24));\n  \n  let reminderLevel = 'none';\n  if (daysOverdue >= 60) reminderLevel = 'collections';\n  else if (daysOverdue >= 31) reminderLevel = 'final';\n  else if (daysOverdue >= 15) reminderLevel = 'urgent';\n  else if (daysOverdue >= 8) reminderLevel = 'second';\n  else if (daysOverdue >= 1) reminderLevel = 'first';\n  \n  return {\n    json: {\n      invoiceId: inv.json['Invoice ID'],\n      clientName: inv.json['Client'],\n      clientEmail: inv.json['Email'],\n      total: parseFloat(inv.json['Total']) || 0,\n      dueDate: inv.json['Due Date'],\n      daysOverdue: daysOverdue,\n      reminderLevel: reminderLevel\n    }\n  };\n}).filter(inv => inv.json.daysOverdue > 0);\n\nreturn overdueInvoices;"
      },
      "typeVersion": 2
    },
    {
      "id": "loop-invoices",
      "name": "Loop Over Invoices",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1980,
        540
      ],
      "parameters": {
        "options": {},
        "batchSize": 1
      },
      "typeVersion": 3
    },
    {
      "id": "route-reminder",
      "name": "Route by Reminder Level",
      "type": "n8n-nodes-base.switch",
      "position": [
        2200,
        540
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "First",
              "conditions": {
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.reminderLevel }}",
                    "rightValue": "first"
                  }
                ]
              }
            },
            {
              "outputKey": "Second",
              "conditions": {
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.reminderLevel }}",
                    "rightValue": "second"
                  }
                ]
              }
            },
            {
              "outputKey": "Urgent",
              "conditions": {
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.reminderLevel }}",
                    "rightValue": "urgent"
                  }
                ]
              }
            },
            {
              "outputKey": "Final",
              "conditions": {
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.reminderLevel }}",
                    "rightValue": "final"
                  }
                ]
              }
            },
            {
              "outputKey": "Collections",
              "conditions": {
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.reminderLevel }}",
                    "rightValue": "collections"
                  }
                ]
              }
            }
          ]
        },
        "options": {
          "fallbackOutput": "none"
        }
      },
      "typeVersion": 3.2
    },
    {
      "id": "first-reminder",
      "name": "First Reminder Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2440,
        320
      ],
      "parameters": {
        "sendTo": "={{ $json.clientEmail }}",
        "message": "=Hi {{ $json.clientName }},\n\nThis is a friendly reminder that invoice {{ $json.invoiceId }} for ${{ $json.total.toFixed(2) }} was due on {{ $json.dueDate }}.\n\nPlease process payment at your earliest convenience.\n\nThank you!",
        "options": {},
        "subject": "=Reminder: Invoice {{ $json.invoiceId }} is Due"
      },
      "typeVersion": 2.1
    },
    {
      "id": "second-reminder",
      "name": "Second Reminder Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2440,
        460
      ],
      "parameters": {
        "sendTo": "={{ $json.clientEmail }}",
        "message": "=Dear {{ $json.clientName }},\n\nYour invoice {{ $json.invoiceId }} is now {{ $json.daysOverdue }} days overdue.\n\nAmount: ${{ $json.total.toFixed(2) }}\n\nPlease arrange payment immediately.",
        "options": {},
        "subject": "=Second Notice: Invoice {{ $json.invoiceId }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "urgent-reminder",
      "name": "Urgent Reminder Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2440,
        600
      ],
      "parameters": {
        "sendTo": "={{ $json.clientEmail }}",
        "message": "=Dear {{ $json.clientName }},\n\nURGENT: Your account has an outstanding balance of ${{ $json.total.toFixed(2) }} that is {{ $json.daysOverdue }} days past due.\n\nPlease contact us immediately.",
        "options": {},
        "subject": "=URGENT: Invoice {{ $json.invoiceId }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "final-reminder",
      "name": "Final Notice Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2440,
        740
      ],
      "parameters": {
        "sendTo": "={{ $json.clientEmail }}",
        "message": "=Dear {{ $json.clientName }},\n\nFINAL NOTICE: Invoice {{ $json.invoiceId }} is now {{ $json.daysOverdue }} days overdue.\n\nAmount: ${{ $json.total.toFixed(2) }}\n\nThis is your final notice before collections.",
        "options": {},
        "subject": "=FINAL NOTICE: Invoice {{ $json.invoiceId }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "collections-slack",
      "name": "Escalate to Collections",
      "type": "n8n-nodes-base.slack",
      "position": [
        2440,
        880
      ],
      "parameters": {
        "text": "=:warning: *Collections Escalation*\n\nInvoice: {{ $json.invoiceId }}\nClient: {{ $json.clientName }}\nAmount: ${{ $json.total.toFixed(2) }}\nDays Overdue: {{ $json.daysOverdue }}",
        "select": "channel",
        "resource": "message",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "#collections"
        },
        "operation": "post",
        "otherOptions": {}
      },
      "typeVersion": 2.2
    },
    {
      "id": "merge-reminders",
      "name": "Merge Reminder Paths",
      "type": "n8n-nodes-base.merge",
      "position": [
        2680,
        540
      ],
      "parameters": {
        "mode": "chooseBranch",
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "update-reminder-date",
      "name": "Update Reminder Date",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2900,
        540
      ],
      "parameters": {
        "columns": {
          "value": {
            "Invoice ID": "={{ $json.invoiceId }}",
            "Last Reminder": "={{ $now.toISO() }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Invoices"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "continue-loop",
      "name": "Continue Loop",
      "type": "n8n-nodes-base.code",
      "position": [
        3120,
        540
      ],
      "parameters": {
        "jsCode": "return $input.all();"
      },
      "typeVersion": 2
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Continue Loop": {
      "main": [
        [
          {
            "node": "Loop Over Invoices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Line Items": {
      "main": [
        [
          {
            "node": "Calculate Line Total",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Final Notice Email": {
      "main": [
        [
          {
            "node": "Merge Reminder Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Initialize Invoice": {
      "main": [
        [
          {
            "node": "Split Line Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Invoices": {
      "main": [
        [
          {
            "node": "Route by Reminder Level",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Send Invoice Email": {
      "main": [
        [
          {
            "node": "Invoice Created Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Overdue Check": {
      "main": [
        [
          {
            "node": "Get Unpaid Invoices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Unpaid Invoices": {
      "main": [
        [
          {
            "node": "Calculate Overdue Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New Invoice Request": {
      "main": [
        [
          {
            "node": "Initialize Invoice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Line Items": {
      "main": [
        [
          {
            "node": "Build Invoice Object",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Invoice Object": {
      "main": [
        [
          {
            "node": "Save Invoice to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Line Total": {
      "main": [
        [
          {
            "node": "Aggregate Line Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "First Reminder Email": {
      "main": [
        [
          {
            "node": "Merge Reminder Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Reminder Paths": {
      "main": [
        [
          {
            "node": "Update Reminder Date",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Reminder Date": {
      "main": [
        [
          {
            "node": "Continue Loop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Invoice to Sheet": {
      "main": [
        [
          {
            "node": "Send Invoice Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Second Reminder Email": {
      "main": [
        [
          {
            "node": "Merge Reminder Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Urgent Reminder Email": {
      "main": [
        [
          {
            "node": "Merge Reminder Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Overdue Days": {
      "main": [
        [
          {
            "node": "Loop Over Invoices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Escalate to Collections": {
      "main": [
        [
          {
            "node": "Merge Reminder Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Reminder Level": {
      "main": [
        [
          {
            "node": "First Reminder Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Second Reminder Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Urgent Reminder Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Final Notice Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Escalate to Collections",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}