DEV Community

T.M. Gunderson
T.M. Gunderson

Posted on

I Stopped Manually Creating Invoices — Here's the n8n Workflow That Generates and Sends Them Automatically

I Stopped Manually Creating Invoices — Here's the n8n Workflow That Generates and Sends Them Automatically

Every month, I'd spend an entire afternoon creating invoices. Look up the hours in the tracker, calculate the total, format the invoice in Google Docs, export to PDF, find the client email, write a cover note, attach the PDF, hit send. Then log the invoice number and amount in a spreadsheet so I'd remember to follow up later.

8–12 invoices. 5–6 hours. Every single month.

And the worst part? The errors. A wrong hourly rate here, a missed line item there, an invoice sent to the wrong contact. Each mistake meant an awkward follow-up email and a delayed payment. One month I underbilled a client by $1,200 because I copy-pasted the wrong rate. That mistake paid for a lot of automation.

So I built an n8n workflow that generates invoices from my project tracker, emails them to clients as PDFs, and logs everything — without me touching a keyboard.

Here's the full build.

Why Invoicing Is a Bigger Problem Than You Think

Most small businesses treat invoicing as a simple administrative task. You did the work, you send the bill. What's the big deal?

The big deal is this: the average small business has $12,000 in unpaid invoices at any given time (Intuit). That's not because clients are deadbeats. It's because invoicing delays create payment delays. The longer it takes you to send an invoice, the longer it takes to get paid.

Here's the cascade:

  • You finish work on Thursday. You don't invoice until the following Tuesday.
  • The client's AP department processes invoices on Wednesdays. Yours misses the cut.
  • Now you're waiting until the next Wednesday. That's 12 days of float on money you already earned.

Speed matters. Accuracy matters. Consistency matters. And manual invoicing fails on all three.

The Architecture: From Tracker to Paid

Here's what the workflow does, end to end:

Schedule Trigger (1st of each month)
  → Read project tracker (Google Sheets)
  → Filter for billable hours this period
  → Group by client
  → For each client:
      → Calculate totals + tax
      → Generate invoice number (sequential)
      → Create PDF from template
      → Send email with PDF attachment
      → Log invoice to tracking sheet
  → Send summary to Slack
Enter fullscreen mode Exit fullscreen mode

Total active time per month: 2 minutes to review the Slack summary and spot-check a couple invoices.

Step-by-Step Build

1. Schedule Trigger — Monthly Invoice Run

Set the trigger to fire on the 1st of each month. If you invoice mid-month, adjust accordingly.

{
  "type": "n8n-nodes-base.scheduleTrigger",
  "parameters": {
    "rule": {
      "interval": [
        {
          "field": "cronexpression",
          "expression": "0 9 1 * *"
        }
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This fires at 9 AM on the 1st of every month. Adjust the hour for your timezone.

2. Read Project Tracker

Pull all billable entries from your Google Sheet. My tracker has columns: Date, Client, Project, Hours, Rate, Description, Billed (yes/no).

{
  "type": "n8n-nodes-base.googleSheets",
  "parameters": {
    "operation": "read",
    "documentId": "YOUR_SPREADSHEET_ID",
    "range": "Billable!A:G",
    "options": {
      "filterByFormula": "AND(G=\"no\",NOT(ISBLANK(A)))"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The filter pulls only unbilled rows (G="no") with actual data.

3. Group by Client

Use the Item Lists node to group entries by client name:

{
  "type": "n8n-nodes-base.itemLists",
  "parameters": {
    "operation": "groupBy",
    "aggregate": [
      {
        "field": "Hours",
        "type": "sum",
        "outputName": "totalHours"
      },
      {
        "field": "Rate",
        "type": "average",
        "outputName": "avgRate"
      }
    ],
    "groupByField": "Client"
  }
}
Enter fullscreen mode Exit fullscreen mode

This gives you one item per client with their total hours and average rate for the period.

4. Calculate Invoice Totals

Add a Set node (or Code node if your pricing is complex) to calculate:

{
  "type": "n8n-nodes-base.set",
  "parameters": {
    "assignments": [
      { "name": "subtotal", "value": "={{$json.totalHours * $json.avgRate}}" },
      { "name": "tax", "value": "={{$json.subtotal * 0.05}}" },
      { "name": "total", "value": "={{$json.subtotal + $json.tax}}" },
      { "name": "invoiceNumber", "value": "=INV-{{new Date().getFullYear()}}{{String(new Date().getMonth()+1).padStart(2,'0')}}-{{String($itemIndex+1).padStart(3,'0')}}" },
      { "name": "invoiceDate", "value": "={{new Date().toISOString().split('T')[0]}}" },
      { "name": "dueDate", "value": "={{new Date(Date.now()+30*86400000).toISOString().split('T')[0]}}" }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

This generates invoice numbers like INV-202607-001, calculates tax (5% GST — adjust for your region), and sets a 30-day due date.

5. Generate PDF Invoice

Use the HTML node to create a formatted invoice, then convert to PDF:

{
  "type": "n8n-nodes-base.html",
  "parameters": {
    "operation": "generateHtmlTemplate",
    "template": "<html><head><style>body{font-family:Helvetica,Arial,sans-serif;margin:40px}table{width:100%;border-collapse:collapse;margin:20px 0}th,td{border:1px solid #ddd;padding:8px;text-align:left}th{background:#2563eb;color:white}.total-row{font-weight:bold;background:#f0f0f0}.header{display:flex;justify-content:space-between;margin-bottom:30px}.from{font-size:12px;color:#666}.invoice-title{font-size:24px;font-weight:bold}</style></head><body><div class='header'><div><div class='invoice-title'>INVOICE</div><div>{{invoiceNumber}}</div><div>Issued: {{invoiceDate}}</div><div>Due: {{dueDate}}</div></div><div class='from'><strong>From:</strong><br>Your Company Name<br>your@email.com</div></div><div><strong>Bill To:</strong><br>{{Client}}</div><table><tr><th>Description</th><th>Hours</th><th>Rate</th><th>Amount</th></tr><tr><td>Professional Services</td><td>{{totalHours}}</td><td>${{avgRate}}</td><td>${{subtotal}}</td></tr><tr class='total-row'><td colspan='3'>Subtotal</td><td>${{subtotal}}</td></tr><tr><td colspan='3'>Tax</td><td>${{tax}}</td></tr><tr class='total-row'><td colspan='3'>Total Due</td><td>${{total}}</td></tr></table><p>Payment terms: Net 30. Please include invoice number {{invoiceNumber}} with payment.</p></body></html>"
  }
}
Enter fullscreen mode Exit fullscreen mode

Then pipe through a PDF conversion node (or use the HTML-to-PDF utility in n8n).

6. Send Invoice Email

{
  "type": "n8n-nodes-base.emailSend",
  "parameters": {
    "fromEmail": "billing@yourcompany.com",
    "toEmail": "={{$json.clientEmail}}",
    "subject": "=Invoice {{invoiceNumber}} - Your Company",
    "text": "=Hi {{Client}},\n\nPlease find attached invoice {{invoiceNumber}} for professional services rendered.\n\nTotal: ${{total}}\nDue date: {{dueDate}}\n\nPlease include the invoice number with your payment.\n\nThank you for your business.",
    "attachments": "invoice.pdf"
  }
}
Enter fullscreen mode Exit fullscreen mode

7. Mark Rows as Billed and Log Invoice

After sending, update the source sheet to mark rows as billed and log the invoice:

{
  "type": "n8n-nodes-base.googleSheets",
  "parameters": {
    "operation": "update",
    "documentId": "YOUR_SPREADSHEET_ID",
    "range": "Billable!G:G",
    "valueInputOption": "RAW",
    "data": "={{ JSON.stringify([$json.rowNumbers.map(r => ['yes'])]) }}"
  }
}
Enter fullscreen mode Exit fullscreen mode

And append to an Invoice Log sheet:

{
  "type": "n8n-nodes-base.googleSheets",
  "parameters": {
    "operation": "append",
    "documentId": "YOUR_SPREADSHEET_ID",
    "range": "InvoiceLog!A:F",
    "options": {
      "useAppend": true
    },
    "columns": {
      "mappingMode": "defineBelow",
      "value": {
        "InvoiceNumber": "={{$json.invoiceNumber}}",
        "Client": "={{$json.Client}}",
        "Amount": "={{$json.total}}",
        "SentDate": "={{$json.invoiceDate}}",
        "DueDate": "={{$json.dueDate}}",
        "Status": "sent"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

8. Slack Summary

{
  "type": "n8n-nodes-base.slack",
  "parameters": {
    "channel": "#finance",
    "text": "=📋 Invoices sent: {{$json.length}} clients\n💰 Total billed: ${{ $json.reduce((s,i) => s + i.total, 0).toFixed(2) }}\n📅 Next invoice run: 1st of next month"
  }
}
Enter fullscreen mode Exit fullscreen mode

The Results After 3 Months

Metric Before After
Time spent invoicing/month 5-6 hours 15 minutes (review only)
Invoice errors/month 1-2 0
Average days to send invoice after work completed 5 days 1 day
Late payments (30+ days) ~20% ~8%

The late payment drop was the biggest surprise. Clients pay faster when they get invoices faster. The 30-day clock starts when they receive the invoice, not when you finish the work.

Common Gotchas

Tax rates vary. If you bill clients in different jurisdictions, add a tax-rate lookup table. Don't hardcode 5% like I did initially and then wonder why your out-of-state clients got wrong invoices.

Line items matter. The simple template above rolls everything into one line item. If your clients need detailed breakdowns (they often do for their own accounting), pull individual entries from the tracker instead of grouping.

Test with a fake client first. Set up a test row in your tracker with your own email as the client. Run the workflow manually before enabling the schedule. Nobody wants to discover their invoice template has a typo after it's already in a client's inbox.

Payment reminders are the natural next step. Once invoices are logged consistently, you can build a second workflow that checks the Invoice Log for overdue invoices and sends polite reminders. That's a separate build — but it becomes trivial once your invoicing is automated.

What This Gets You

  • 5+ hours back every month — no more manual invoice creation
  • Fewer errors — calculations are consistent every time
  • Faster payment — invoices go out within 24 hours of the billing period
  • Audit trail — every invoice is logged with number, amount, date, and status
  • Scalability — 2 clients or 20, the workflow doesn't care

The full workflow JSON is available in our Invoice Automation Playbook. Copy it, import it into n8n, and customize the template and tax rates for your business. You'll have automated invoicing running in under an hour.


If you found this useful, follow for more automation builds. I break down real workflows that small businesses use to get hours back every week — no theory, just working systems.

Top comments (0)