DEV Community

Archit Mittal
Archit Mittal

Posted on

How I Built an Automated WhatsApp Invoice System for My Client in 3 Hours

Last week, a CA firm in Jaipur reached out to me with a painfully familiar problem: their accountant was manually creating invoices in Excel, converting them to PDF, and sending them on WhatsApp — one by one — to 200+ clients every month.

Time spent? About 3 full working days. Every. Single. Month.

I built them an automated system in 3 hours flat. Here's exactly how.

The Problem

The firm's workflow looked like this:

  1. Open Excel template
  2. Fill in client name, GSTIN, amounts, due dates
  3. Save as PDF
  4. Open WhatsApp Web
  5. Search for the client
  6. Attach PDF, type a message, hit send
  7. Repeat 200 times

The accountant was burning ~24 hours/month on this. At ₹300/hour, that's ₹7,200/month — or ₹86,400/year — just on invoice delivery.

The Solution: Python + WhatsApp Business API

I broke the automation into 3 parts:

  1. Invoice generation from a Google Sheet
  2. PDF creation using a template
  3. WhatsApp delivery via the Business API

Part 1: Pull Data from Google Sheets

First, I set up a Google Sheet as the single source of truth. The accountant just fills in the data — the script does the rest.

import gspread
from google.oauth2.service_account import Credentials

SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly']
creds = Credentials.from_service_account_file('creds.json', scopes=SCOPES)
client = gspread.authorize(creds)

sheet = client.open("Monthly_Invoices").worksheet("April2026")
records = sheet.get_all_records()

# Each record has: client_name, gstin, amount, due_date, phone
print(f"Found {len(records)} invoices to process")
Enter fullscreen mode Exit fullscreen mode

This gives us a clean list of dictionaries — one per client.

Part 2: Generate PDF Invoices

I used reportlab for PDF generation. The firm wanted their letterhead, so I created a reusable template.

from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from datetime import datetime
import os

def generate_invoice(data, output_dir="invoices"):
    os.makedirs(output_dir, exist_ok=True)

    filename = f"{output_dir}/{data['client_name']}_{datetime.now().strftime('%B_%Y')}.pdf"
    c = canvas.Canvas(filename, pagesize=A4)
    width, height = A4

    # Header
    c.setFont("Helvetica-Bold", 18)
    c.drawString(1*inch, height - 1*inch, "TAX INVOICE")

    # Firm details
    c.setFont("Helvetica", 10)
    c.drawString(1*inch, height - 1.5*inch, "Sharma & Associates, Chartered Accountants")
    c.drawString(1*inch, height - 1.7*inch, "GSTIN: 08AXXXX1234X1Z5")

    # Client details
    c.setFont("Helvetica-Bold", 11)
    c.drawString(1*inch, height - 2.5*inch, f"Bill To: {data['client_name']}")
    c.setFont("Helvetica", 10)
    c.drawString(1*inch, height - 2.8*inch, f"GSTIN: {data['gstin']}")

    # Invoice details
    inv_number = f"INV-{datetime.now().strftime('%Y%m')}-{data['client_name'][:3].upper()}"
    c.drawString(4.5*inch, height - 2.5*inch, f"Invoice #: {inv_number}")
    c.drawString(4.5*inch, height - 2.8*inch, f"Date: {datetime.now().strftime('%d-%m-%Y')}")
    c.drawString(4.5*inch, height - 3.1*inch, f"Due Date: {data['due_date']}")

    # Amount table
    amount = float(data['amount'])
    gst = amount * 0.18
    total = amount + gst

    y_pos = height - 4*inch
    c.setFont("Helvetica-Bold", 10)
    c.drawString(1*inch, y_pos, "Description")
    c.drawString(5*inch, y_pos, "Amount (₹)")

    c.setFont("Helvetica", 10)
    c.drawString(1*inch, y_pos - 0.4*inch, "Professional Services")
    c.drawString(5*inch, y_pos - 0.4*inch, f"{amount:,.2f}")
    c.drawString(1*inch, y_pos - 0.7*inch, "GST @ 18%")
    c.drawString(5*inch, y_pos - 0.7*inch, f"{gst:,.2f}")

    c.setFont("Helvetica-Bold", 12)
    c.drawString(1*inch, y_pos - 1.2*inch, "TOTAL")
    c.drawString(5*inch, y_pos - 1.2*inch, f"{total:,.2f}")

    # Bank details
    c.setFont("Helvetica", 9)
    c.drawString(1*inch, 2*inch, "Bank: HDFC Bank | A/C: XXXX1234 | IFSC: HDFC0001234")

    c.save()
    return filename

# Generate all invoices
invoice_files = {}
for record in records:
    path = generate_invoice(record)
    invoice_files[record['phone']] = {
        'path': path,
        'name': record['client_name'],
        'amount': record['amount']
    }
    print(f"Generated invoice for {record['client_name']}")
Enter fullscreen mode Exit fullscreen mode

Part 3: Send via WhatsApp Business API

This is where the magic happens. I used the WhatsApp Business API (via a BSP provider — costs about ₹0.50 per message in India).

import requests
import time

WHATSAPP_TOKEN = "your_access_token"
PHONE_NUMBER_ID = "your_phone_number_id"

def upload_media(file_path):
    """Upload PDF to WhatsApp media endpoint"""
    url = f"https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}/media"
    headers = {"Authorization": f"Bearer {WHATSAPP_TOKEN}"}

    with open(file_path, 'rb') as f:
        files = {'file': (file_path, f, 'application/pdf')}
        data = {'messaging_product': 'whatsapp'}
        response = requests.post(url, headers=headers, files=files, data=data)

    return response.json().get('id')

def send_invoice(phone, media_id, client_name, amount):
    """Send invoice PDF with a message via WhatsApp"""
    url = f"https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}/messages"
    headers = {
        "Authorization": f"Bearer {WHATSAPP_TOKEN}",
        "Content-Type": "application/json"
    }

    payload = {
        "messaging_product": "whatsapp",
        "to": f"91{phone}",
        "type": "document",
        "document": {
            "id": media_id,
            "filename": f"Invoice_{client_name}.pdf",
            "caption": (
                f"Hello {client_name},\n\n"
                f"Please find attached your invoice for the amount "
                f"of Rs.{float(amount):,.2f} (inclusive of GST).\n\n"
                f"Kindly process the payment by the due date.\n\n"
                f"Regards,\nSharma & Associates"
            )
        }
    }

    response = requests.post(url, headers=headers, json=payload)
    return response.json()

# Send all invoices
sent_count = 0
failed = []

for phone, info in invoice_files.items():
    try:
        media_id = upload_media(info['path'])
        result = send_invoice(phone, media_id, info['name'], info['amount'])

        if 'messages' in result:
            sent_count += 1
            print(f"Sent to {info['name']} ({phone})")
        else:
            failed.append(info['name'])
            print(f"Failed for {info['name']}: {result}")

        time.sleep(1)  # Rate limiting

    except Exception as e:
        failed.append(info['name'])
        print(f"Error for {info['name']}: {e}")

print(f"\nDone! Sent: {sent_count} | Failed: {len(failed)}")
if failed:
    print(f"Failed clients: {', '.join(failed)}")
Enter fullscreen mode Exit fullscreen mode

Putting It All Together

I wrapped everything in a single run.py script with logging and error handling. The accountant now:

  1. Updates the Google Sheet with this month's data
  2. Runs python run.py (or I set up a cron job for the 1st of every month)
  3. Gets a summary in the terminal showing what was sent

Total execution time for 200 invoices: under 8 minutes.

The Numbers

Metric Before After
Time per month ~24 hours ~8 minutes
Cost per month ₹7,200 (labor) ₹100 (API costs)
Errors (wrong client) 2-3/month 0
Client complaints Frequent delays Same-day delivery

Annual savings: ₹85,200 in labor + eliminated errors.

The firm paid me ₹15,000 for this build — ROI in less than 2 months.

Key Lessons

Start with the spreadsheet. Every automation project I take on starts with understanding the data source. If the data is messy, fix that first. The actual automation is the easy part.

Use APIs, not UI bots. I could have used browser automation to control WhatsApp Web, but that breaks every time WhatsApp updates their UI. The Business API is stable, fast, and gives you delivery receipts.

Build for the person who runs it. The accountant isn't a developer. So I made the Google Sheet the only interface they touch. No terminal, no config files, no "just edit this JSON."

Charge for value, not hours. This took me 3 hours. If I charged hourly at ₹1,000/hr, that's ₹3,000. But I saved them ₹85,000/year. ₹15,000 is a no-brainer for the client and fair compensation for the value delivered.

Want to Build Something Similar?

Here's the minimal setup you need:

  • Python 3.8+
  • gspread and google-auth for Sheets access
  • reportlab for PDF generation
  • WhatsApp Business API access (apply through Meta Business)
  • A Google Cloud service account with Sheets API enabled

Install everything with:

pip install gspread google-auth reportlab requests
Enter fullscreen mode Exit fullscreen mode

The entire codebase is under 200 lines. You could build this in an afternoon.


I'm Archit Mittal — I automate chaos for businesses. Follow me for daily automation content.

Top comments (0)