Automating Invoice Generation and Payment Tracking With Google Sheets
Automate10 min read·March 5, 2026·--

Automating Invoice Generation and Payment Tracking With Google Sheets

Stop chasing unpaid invoices with spreadsheets and WhatsApp messages. Build a free automated system that generates invoices, sends reminders, and tracks payments — all from Google Sheets.

@
@kivorablog
March 5, 2026
Share

The Invoice Problem Every Freelancer Knows


You delivered the work. You sent the invoice. Then... silence. You wait a week, send a polite WhatsApp reminder. Another week, a follow-up email. Month-end, you're still chasing ₦350,000 across 4 clients.


Late payments aren't just annoying — they're a cash flow killer. A 2024 survey of Nigerian freelancers found that 68% experience payment delays averaging 23 days past due.


Here's the fix: automate the entire invoicing lifecycle with Google Sheets and Apps Script. Zero cost. Zero server. Zero excuses.


What This System Does


StepManual ProcessAutomated System
Create invoice15 min in Word/Canva2 sec from template
Send to clientManual emailAuto-email as PDF
Track paymentCheck bank, update sheetAuto-update from Paystack
Send reminderWhatsApp, prayAuto-reminder at 7, 14, 21 days
Mark overdueHighlight manuallyColor-coded auto-status



Tool Comparison: Invoicing for African Businesses


ToolFree?Auto-RemindersPaystack IntegrationMonthly Cost
Google Sheets + ScriptYesYes (custom)Yes (API)₦0
Wave AppsYesYesNo₦0
Zoho InvoiceYes (up to 10)YesNo₦0–₦5,400
FreshBooksNoYesNo₦18,000+
BonsaiNoYesNo₦13,500+
QuickBooksNoYesNo₦20,000+

The Google Sheets approach wins because it's free, fully customisable, and integrates with Paystack — the payment gateway that actually works in Nigeria and Ghana.




Step 1: Set Up Your Invoice Sheet


Create a Google Sheet with these columns:


ColumnHeaderExample
AInvoice #INV-2026-001
BClient NameTechCo Lagos
CClient Emailpay@techco.ng
DAmount (₦)350000
EIssue Date2026-03-01
FDue Date2026-03-15
GStatusPending
HDays Overdue0
IReminder Count0
JPayment Ref
KNotesWeb redesign project



Step 2: Auto-Generate Invoice PDFs


Open Extensions → Apps Script and add this:


function generateInvoicePDF(row) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Invoices');
  const data = sheet.getRange(row, 1, 1, 11).getValues()[0];

  const [invoiceNo, client, email, amount, issueDate, dueDate] = data;

  // Create invoice document from template
  const templateId = 'YOUR_TEMPLATE_DOC_ID';
  const docCopy = DriveApp.getFileById(templateId).makeCopy();
  const doc = DocumentApp.openById(docCopy.getId());

  const body = doc.getBody();
  body.replaceText('{{invoice_no}}', invoiceNo);
  body.replaceText('{{client_name}}', client);
  body.replaceText('{{amount}}', '₦' + Number(amount).toLocaleString());
  body.replaceText('{{issue_date}}', issueDate);
  body.replaceText('{{due_date}}', dueDate);

  doc.saveAndClose();

  // Export as PDF
  const pdfBlob = docCopy.getAs(MimeType.PDF);
  pdfBlob.setName(invoiceNo + '.pdf');

  // Send via email
  MailApp.sendEmail({
    to: email,
    subject: 'Invoice ' + invoiceNo + ' from Your Business Name',
    body: 'Hi ' + client + ',\n\nPlease find attached invoice ' + invoiceNo + ' for ₦' + Number(amount).toLocaleString() + '.\n\nPayment is due by ' + dueDate + '.\n\nThank you for your business.',
    attachments: [pdfBlob]
  });

  // Clean up temp doc
  DriveApp.getFileById(docCopy.getId()).setTrashed(true);

  sheet.getRange(row, 7).setValue('Sent');
}



Step 3: Auto-Calculate Overdue Status


Add a daily trigger that updates overdue counts:


function updateOverdueStatus() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Invoices');
  const lastRow = sheet.getLastRow();
  const today = new Date();

  for (let row = 2; row <= lastRow; row++) {
    const status = sheet.getRange(row, 7).getValue();
    const dueDate = new Date(sheet.getRange(row, 6).getValue());

    if (status === 'Paid') continue;

    const daysOverdue = Math.floor((today - dueDate) / (1000 * 60 * 60 * 24));
    sheet.getRange(row, 8).setValue(Math.max(0, daysOverdue));

    // Update status and color
    if (daysOverdue > 21) {
      sheet.getRange(row, 7).setValue('Overdue');
      sheet.getRange(row, 7).setBackground('#ff4444');
    } else if (daysOverdue > 0) {
      sheet.getRange(row, 7).setValue('Late');
      sheet.getRange(row, 7).setBackground('#ffaa00');
    }
  }
}



Step 4: Automated Payment Reminders


function sendReminders() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Invoices');
  const lastRow = sheet.getLastRow();

  for (let row = 2; row <= lastRow; row++) {
    const status = sheet.getRange(row, 7).getValue();
    const daysOverdue = sheet.getRange(row, 8).getValue();
    const reminderCount = sheet.getRange(row, 9).getValue();
    const client = sheet.getRange(row, 2).getValue();
    const email = sheet.getRange(row, 3).getValue();
    const amount = sheet.getRange(row, 4).getValue();
    const invoiceNo = sheet.getRange(row, 1).getValue();

    // Send at 7, 14, and 21 days overdue
    const shouldRemind = [7, 14, 21].includes(daysOverdue) && reminderCount < 3;

    if (status !== 'Paid' && shouldRemind) {
      const tone = daysOverdue >= 21 ? 'firm' : daysOverdue >= 14 ? 'direct' : 'friendly';

      const subjects = {
        friendly: 'Friendly Reminder: Invoice ' + invoiceNo,
        direct: 'Follow-Up: Invoice ' + invoiceNo + ' is ' + daysOverdue + ' days overdue',
        firm: 'URGENT: Invoice ' + invoiceNo + ' — Immediate Payment Required'
      };

      MailApp.sendEmail({
        to: email,
        subject: subjects[tone],
        body: 'Hi ' + client + ',\n\nThis is a ' + tone + ' reminder that invoice ' + invoiceNo + ' for ₦' + Number(amount).toLocaleString() + ' is ' + daysOverdue + ' days overdue.\n\nPlease process payment at your earliest convenience.'
      });

      sheet.getRange(row, 9).setValue(reminderCount + 1);
    }
  }
}



Step 5: Set Up the Triggers


In Apps Script, go to Triggers (clock icon) and add:


FunctionTrigger TypeFrequency
updateOverdueStatusTime-drivenDaily, 8:00am
sendRemindersTime-drivenDaily, 9:00am



Common Gotchas


IssueCauseFix
Emails not sendingGmail daily limit (100/day)Stay under 80; use Mailgun for volume
PDF template not foundWrong Doc IDCopy ID from the URL of your template
Dates showing as numbersSheets serial number formatUse `new Date()` conversion
Reminders going to paid clientsStatus not updatedMark "Paid" immediately on payment
Paystack webhook not workingWrong sheet name in scriptMatch the sheet name exactly



Real Impact


A freelance designer in Lagos implemented this system and tracked the results over 3 months:


  • Average payment time: dropped from 23 days to 9 days
  • Overdue invoices: reduced by 74%
  • Time on invoice admin: from 4 hours/week to 15 minutes/week
  • Cost: ₦0

The system works because it removes the awkwardness of manual follow-ups. Clients receive professional, escalating reminders automatically. No emotions. No forgetting. No lost invoices.

Read more on Kivora Blog

Read more on Kivora Blog

Get started →