Case Study · February 28, 2026

$87,000 in Open Invoices. Automated Follow-Up. 90 Days.

Ebby's Podcast ~6 min episode
Share LinkedIn X Email
Case Study February 28, 2026 10 min read

He came to me because cash flow was becoming unpredictable. He had $87,000 across 23 outstanding invoices. Some were 60 days overdue. He was creating invoices by hand when jobs closed, following up when he remembered, and tracking everything in a spreadsheet that was weeks out of date.

$87K
Total open invoices
$79K
Collected in 120 days
120 days
Collection window
23
Outstanding client accounts
Key insight: The invoices were not uncollectable. There was no system sending the right message at the right time. Automation replaced the silence.

In my opinion, average time to collect had stretched to 42 days (a DSO well above the 30-day industry standard for service businesses). For a solo operator running a 12-year consulting business, that is not sustainable. At 42-day DSO, he was effectively lending money to every client, interest-free, for six weeks per project. The $87,000 was not money he lost. It was money he had already earned that he could not use. Every week that cash sat in unpaid invoices was a week it could not fund payroll, equipment, or the contractor he needed to bring on.

The invoicing process looked like this: job completes, he notes it in Slack, writes the invoice in QuickBooks two or three days later, sends a PDF attachment by email. First follow-up comes when he remembers to check. By that point, the client has moved to the next thing, the invoice is buried in an email thread from a week ago, and inertia takes over. The 2024 Atradius Payment Practices Index found that 34 percent of B2B invoices in North America are paid past agreed terms. That figure is higher in service businesses. The invoices that go past 30 days are not usually disputes. They are silence that nobody broke.

What the Aging Breakdown Showed

Before building anything, I pulled his QuickBooks data and ran an aging analysis. The picture was clearer than I expected. Most of the money was still collectible. Less than 15 percent had been outstanding more than 90 days. That told me this was not a client quality problem. It was a follow-up timing problem.

AR Aging at Intake

0-30 days 31-60 days 61-90 days 90+ days $35K, 40% $28K, 32% $15K, 17% $9K, 11%

72 percent of outstanding balance was under 60 days old, collectible with the right timing and channel.

The $9,000 beyond 90 days was riskier but not written off. Two of those clients had ongoing retainers. They were not refusing to pay. They had drifted. The invoices had become background noise. This is the core insight: most unpaid invoices are not disputes. They are items that fell off the client's radar at the same time the consultant stopped following up.

What I Built and How It Works

The system connects his project management tool, QuickBooks, Stripe, and Twilio. When a job is marked complete in the PM tool, a webhook fires to an orchestration layer I built in Python. That layer pulls the job metadata, creates the invoice in QuickBooks via API, and sends the client a payment link through Stripe by email within minutes of job close. No manual step. No delay. The invoice hits the client's inbox while the work is still fresh.

From there, the system runs a daily check. If the invoice is unpaid at 7 days, a reminder goes out by email with the payment link. At 14 days, a shorter follow-up with a direct Stripe URL. At 21 days, if still unpaid, it escalates to SMS via Twilio. The logic behind the channel switch matters: email follow-ups in a professional context get opened around 20-25 percent of the time. SMS read rates are close to 98 percent within minutes. A text from a business contact is harder to ignore than an email buried between notifications.

What I have found is that the system classifies each client by payment history before deciding cadence. Clients who historically paid within 7 days get lighter-touch reminders. Clients who regularly paid at 25-30 days get a different schedule. This matters because treating every client identically generates friction with good payers. Pattern matching on historical behavior is simple to implement in Python and meaningfully changes how clients perceive the outreach.

At 45 days, the final escalation sends from my client's personal email directly, flagged as requiring attention, with a 48-hour window. Every invoice that reached this stage was either paid or a payment plan was arranged within a week. The combination of personal sender, urgency framing, and a specific timeline consistently broke the inertia. By 45 days overdue, clients know the invoice exists. What they need is a reason to act today.

One thing I want to be direct about: the Fair Debt Collection Practices Act applies to third-party collectors, not to creditors collecting their own invoices. My client was collecting invoices he issued for work he completed. The messages are professional, factual, and stop immediately upon payment. If you build something similar, run your message templates by a lawyer before deploying. The B2B context here is appropriate, this is a business following up on work delivered, not a collections agency.

What Changed in 120 Days

The system went live on a Monday. By the end of that first week, 11 reminders had gone out to invoices in the 7-to-30-day window. Five payments came in from clients who had simply not acted yet. By day 21, 18 more invoices had cleared. The SMS escalation had a response rate that surprised me. Three of the first five clients who got a text paid the same day.

By day 120: outstanding balance dropped from $87,000 to $8,000. Average collection cycle cut from 42 days to 16 days. My client has not sent a single manual follow-up since the system went live. The total infrastructure cost to run this is under $30 a month. Twilio charges roughly $0.0075 per SMS at their base rate. SendGrid handles email. The Python scheduler runs on a five-dollar VPS. The cost is negligible against the working capital impact of cutting DSO by 26 days across all active projects.

The cash flow shift was visible in the first 30 days. Because invoices were clearing faster, the owner had liquidity he had not had in months. He brought on a contractor for the first time in two years. He funded a piece of equipment he had been deferring. The $87,000 was not a windfall, he had earned it. The system just made sure he could actually use it.

What You Can Build With the Same Stack

The components are available to any service business: QuickBooks API, SendGrid, Twilio, and a Python script on a small VPS. The logic is not complex. What makes it work is consistency. The system sends reminders at the right time, every time, regardless of what else is happening in the business. It does not forget. It does not hesitate because it feels awkward to ask for payment again. It does not treat two invoices differently because one client is a friend and the other is a newer account.

In my experience, start with one automation: overdue invoices past 7 days. Build it, run it for 30 days, and measure the response rate. Then add the 14-day email. Then the 21-day SMS escalation. Each step is independently testable and generates improvement on its own. By month three, you will have a system that collects faster than manual follow-up ever did, costs almost nothing to run, and frees the time you were spending chasing invoices for work that generates new revenue instead.

The full technical breakdown of the architecture, the Python implementation, and the QuickBooks webhook setup is in the case study linked below.

What to Track Once the System Runs

The metrics that matter most are not the ones most people track. Cash collected is obvious. What tells you whether the system is actually working, and where to tune it, is the response rate by channel and the shift in DSO by aging bucket over time.

Days Sales Outstanding is the baseline. Take total outstanding receivables, divide by your average daily revenue, and you get DSO. At 42 days, my client was financing six weeks of client work interest-free per project. At 16 days, that number became manageable. Track it weekly for the first 90 days. The trend matters more than the absolute number because the first month after launch will look artificially good as the oldest invoices clear. You want to see whether the new average is holding at 30, 60, or 90 days after launch.

Response rate by channel gives you the data to decide where to put pressure. In my client's case, the 7-day email had about a 22 percent response rate. The 14-day follow-up added another 18 percent of the outstanding balance. The 21-day SMS was the threshold that moved the chronic delayers. What I was looking for was whether the SMS response rate was worth the slight friction of a text from a business contact. It was, by a significant margin, for invoices in the 21-45 day range. I adjusted the cadence after the first 30 days, moved SMS up from day 21 to day 18 for clients who had a history of paying in that window.

Collection rate by aging bucket tells you which segment is the problem. In the first 90 days, we cleared 94 percent of the 0-60 day bucket and 78 percent of the 61-90 day bucket. The 90-plus bucket settled at 65 percent collected through the automated sequence. The remaining 35 percent in that bucket required a direct phone call. That is useful information: the system handles the easy cases automatically. The genuinely stuck invoices, where there is a dispute or a cash flow problem on the client's side, surface themselves quickly when everything else clears and those remain open.

The Python scheduler I built runs on a $5 DigitalOcean droplet. It uses APScheduler to check outstanding invoices every morning at 7 AM. The QuickBooks API token refreshes via OAuth2 automatically. When the scheduler identifies an invoice crossing the 7-day threshold, it calls SendGrid to send the first reminder. The Stripe payment link is generated at invoice creation and stored in the database, the reminder email references the same link, so the client never has to find an old email. At the 21-day threshold, it calls Twilio to send the SMS. Total API costs: SendGrid free tier covers the first 100 emails per day, which is more than enough for a solo operator. Twilio at $0.0075 per SMS means 100 text messages costs 75 cents. The VPS runs the scheduler and the QuickBooks OAuth refresh and nothing else. The total infrastructure bill for the first six months was under $35.

← All posts
"Most unpaid invoices are not disputes, they are reminders that never went out."

30 Minutes. Honest Assessment. No Pitch.

You describe what is eating your time. I tell you honestly whether I can fix it, what it takes, and what it costs.