An invoice chaser on AWS for a few dollars a month
A small business sends more invoices than anyone keeps in their head. The net-30 that went out three weeks ago and is now quietly two days late. The big project invoice the client keeps meaning to pay. The repeat customer who always pays, just always a week late. The one nobody followed up on because the person who would have was on holiday. Chasing money you are owed is awkward, easy to forget, and the first thing that slips when the week gets busy. This post walks through the design of a small chaser that watches every unpaid invoice, sends a polite reminder to the right person at the right time, and escalates if it goes seriously late — and stops the second the money lands.
Key takeaways
- Three sources for invoices: a Drive sheet from your accounting tool, an inbox forwarding lane, and a webhook lane.
- Every invoice ends in one of four moves on each tick: current, first nudge, follow-up, or escalate.
- Per-terms cadences: net-30 gets a nudge at 3 days late, a follow-up at 10, an escalation at 21.
- Reminders climb a friendly-to-firm tone ladder, respect quiet hours and weekends, and carry a pay-link.
- Designed on AWS for about $2.40/month at typical small-business volume.
The whole system on one page
Before any code, here’s the shape of what we’re designing.
What you set up once (the outside)
- Open invoices. A Google Sheet in a Drive folder, one row per invoice: number, customer, billing contact email, amount, issue date, due date, terms (net-15, net-30, due-on-receipt), a paid flag, and a link to the invoice PDF. You export this from your accounting tool once and let new invoices flow in from two other lanes covered in Part 2 — an inbox-forwarding lane (forward an invoice PDF to a dedicated address and the chaser proposes a row for one-tap approval) and a webhook lane (your accounting tool calls a small endpoint the moment an invoice is issued).
- A rules folder. Two short Google Docs in a Drive folder. The rules doc covers the reminder cadence for each set of terms — how many days past due the chaser should send, and how many times. Net-30 typically gets a nudge at 3 days late, a follow-up at 10, and an escalation at 21; due-on-receipt gets a tighter 1/7/14. The doc also lists the account owner per customer (or per invoice, if it overrides), the escalation target if the invoice stays unpaid, the quiet hours, and any holiday days to skip. The voice doc holds one reminder template per step of the tone ladder — the friendly nudge, the firmer follow-up, the final notice.
- Customers and owner. The billing contact at each customer receives the reminders. The internal account owner receives the escalation when an invoice goes seriously late. Reminders land with the invoice number, amount due, days past due, a link to the invoice PDF, and a pay-link the customer can click to settle on the spot.
What runs on every tick (the inside)
- The invoice intake. Three sources feed the list. The Drive sheet is the canonical store. New invoices can also be added via the inbox forwarding lane (forward a PDF to
invoices@your-company.com, the chaser uses Textract to read the PDF and Bedrock Haiku 4.5 to extract number, customer, amount, due date, then drops a one-tap approval card in the bookkeeper’s Slack to confirm before the row is added) and the webhook lane (your accounting tool posts new invoices to a small endpoint the moment they are issued). - The chaser. Runs once a day at 9am local. Reads the open invoices. For each one, computes days-past-due. Compares against the per-terms cadence in the rules doc. Picks one of four moves. Current: not yet due, or paid — do nothing. First nudge: just crossed the first cadence step — send a friendly reminder with the pay-link. Follow-up: crossed a later step with no payment — send a firmer reminder, mention when the previous one went out. Escalate: hit the final step with no payment — tell the account owner named in the rules doc; log it. The chaser itself doesn’t call a model on the daily tick — the move logic is plain Python.
- The sender. Reads the voice doc, formats the reminder message for the chosen move and tone, and sends it. Email goes through SES outbound. It honors quiet hours (no sends between 6pm and 8am local by default) and skips weekends and holidays. Every send writes a row in DynamoDB so the next day’s tick can tell whether a reminder already went out. A weekly digest summarizes everything that went out that week, plus what’s coming up. A monthly summary writes a one-paragraph cash-flow narrative: total outstanding, oldest invoices, biggest at-risk amounts.
In plain words
You invoiced Acme Co. $6,400 on net-30 terms. The due date passes and nobody at Acme has paid. On day 3 past due, the chaser emails their billing contact a friendly nudge: “Just a quick reminder — invoice #1042 for $6,400 was due on May 1. Here’s a link to pay or to the PDF if you need it.” No reply. On day 10 it sends a firmer follow-up that mentions the first one. Still nothing. On day 21 it escalates to your account owner, Sam, with the full history attached, so a human can pick up the phone. Meanwhile, the moment Acme pays — whether from the pay-link or a bank transfer your accounting tool records — the chase stops on the next tick and a short thank-you can go out. Nobody at Acme gets nagged about money they have already sent.
The cost of running this is about $2.40 a month at SMB volume. The cost of not running it is the invoice that ages 90 days because everyone was busy, the cash-flow crunch that follows, and the awkward call that gets harder the longer it waits.
Design rules that shaped every decision
- Every reminder ships with full context — invoice number, amount, days past due, pay-link, PDF. The customer never has to dig.
- Four moves, always. Current, first nudge, follow-up, escalate. There is no fifth.
- The tone ladder climbs from friendly to firm. Nothing rude ever goes out, even on the final notice.
- Quiet hours, weekends, and holidays are respected. A reminder is a finite resource; bad timing burns it.
- Payment stops the chase on the next tick. The chaser never nags an invoice that is already paid.
- Every send and every owner action is logged. Audit a collection next year and you can see every reminder that went out.
Why this shape
Most teams chase invoices in one of three ways: a person who remembers to do it when they have time, a spreadsheet of “who owes us” that nobody opens, or not at all. The person works until they are busy — and the weeks they are busiest are exactly the weeks cash is tightest. The spreadsheet is a list, not a system: it tells you who is late but does nothing about it. And “not at all” is how a healthy business ends up quietly lending its customers money for free.
The setup above keeps the invoice list in a sheet the team already exports, but adds a small system that looks at that list every day and acts only when something is actually overdue. Reminders go out early enough to matter. They are polite by default and only firmer when an invoice is genuinely late. They carry a pay-link so paying is one click. They escalate to a human cleanly when it is time for a phone call. And they stop the moment the money arrives. The chaser is invisible on the days nothing is owed; it only shows up when someone owes you money and has forgotten.
The next four posts walk through each piece in turn: how an invoice gets loaded, how overdue gets detected, how a reminder reaches the customer, and how a chase stops on payment. One diagram per post. A cost breakdown and a final engineering reference at the end.
All posts