Automation Workflows

Automating Invoice Reminders Without Annoying Good-Paying Clients

Our first automated reminder sequence treated every unpaid invoice identically, and it cost us a genuinely good client who was three days late for the first time in two years. Here is the tiered version that replaced it.

By Aissam Ait Ahmed Automation Workflows 0 comments

Our first automated invoice reminder sequence sent the exact same escalating message schedule to every overdue invoice, regardless of who the client was or their actual payment history — and it cost us an uncomfortable conversation with a client who had paid on time for two straight years and was three days late for the first time, only to receive the same slightly stern reminder template we'd send someone who'd ignored four previous invoices. This is the tiered version that replaced it, and specifically why treating every late payment identically was the actual mistake, not automation itself.

The original, one-size-fits-all sequence

Day 0 (due date): friendly reminder email
Day 3 overdue: firmer reminder email
Day 7 overdue: firm email + phone call flagged for follow-up
Day 14 overdue: final notice, mentions late fee

This is a completely reasonable escalation sequence in the abstract, and it's exactly the kind of thing most invoicing tools ship as a sensible default. The problem wasn't the sequence's logic — it was applying identical logic to a two-year, always-on-time client and a first-time client who'd already missed two previous invoices, as if their three days late meant the same thing about the likelihood of eventual payment, which it clearly didn't.

The actual incident that forced a rethink

A long-standing client, invoice three days overdue for genuinely the first time in a two-year relationship, received the "day 3 overdue: firmer reminder" template — worded firmly enough, in isolation, for a client we'd never actually had payment trouble with. They replied, understandably a little put off, essentially asking why they were getting what read as a stern payment-chasing email over three days on an account with a spotless two-year history. That reply is the entire reason this rebuild happened: the automation was technically working exactly as designed, and it was still the wrong behavior for that specific client's actual context.

The tiered version: payment history changes the schedule, not just the tone

function reminderTierFor(client) {
  const onTimeRate = client.invoices_paid_on_time / client.total_invoices;

  if (client.total_invoices < 2) {
    return 'new_client';       // limited history, moderate default schedule
  }
  if (onTimeRate >= 0.9) {
    return 'trusted';          // strong track record, gentler and slower schedule
  }
  if (onTimeRate >= 0.6) {
    return 'standard';         // the original default schedule
  }
  return 'high_risk';          // frequent late payment, faster escalation
}

Rather than one schedule for everyone, each tier gets a genuinely different cadence and tone, not just a softer word choice layered on top of the same underlying timing:

trusted:   Day 0 reminder, Day 10 gentle follow-up only if still unpaid
standard:  Day 0, Day 3, Day 7, Day 14 (the original sequence)
high_risk: Day 0, Day 1, Day 3 (with late fee mentioned earlier), Day 7 (call flagged immediately)
new_client: Day 0, Day 5, Day 10 — moderate, since there's no history yet to lean on either way

The trusted tier specifically doesn't send anything at all between day 0 and day 10 unless the invoice is still unpaid at that point — a client with a 90%+ on-time history gets the benefit of the doubt for over a week before the automation assumes something's actually wrong, rather than treating day 3 as inherently alarming regardless of who the client is.

Wording that reflects the tier, not just the timing

The trusted tier's day-10 message is deliberately worded differently from the standard tier's day-3 message, not just sent later:

Trusted tier wording: "Just a quick note — invoice #4471 shows as still
outstanding on our end. If it's already been sent, feel free to
ignore this! Otherwise, here's the link when convenient: [link]"

Standard tier wording: "This is a reminder that invoice #4471
was due on [date] and remains unpaid. Please arrange payment
at your earliest convenience: [link]"

The trusted-tier message explicitly acknowledges the possibility that payment is already in transit and hasn't been reconciled yet — a genuinely more likely explanation for a reliable client being briefly late than "they're avoiding payment," which is closer to the actual base rate for that specific tier of client based on their real history.

A failure mode this fix introduced, caught during testing

The tier calculation itself had a subtle bug in an early version: onTimeRate was computed including the currently-overdue invoice in the denominator, which meant a client's very first-ever late payment could mathematically drag a long history of on-time payments down enough to bump them out of the trusted tier on the exact invoice where trusted-tier treatment mattered most. The fix was calculating the rate from historical invoices only, explicitly excluding the current one being evaluated:

const historicalRate = client.invoices_paid_on_time /
  (client.total_invoices - 1); // exclude the current, still-open invoice

This is worth calling out because it's exactly the kind of bug that's invisible until you trace through the specific case it breaks — a long-time trusted client's very first late invoice is precisely the scenario the tiering system exists to handle gracefully, and the original calculation would have failed that exact case at the exact moment it mattered.

Generating and sending the actual invoices this reminder logic tracks

The reminders themselves reference invoices generated through our own invoice generator, with the due date and invoice number embedded directly in the reminder template rather than manually re-typed for each reminder — a small detail, but manually re-entering an invoice number into a reminder email is exactly the kind of low-stakes-feeling manual step that occasionally introduces a typo, and a reminder email referencing the wrong invoice number undermines trust in the whole system far more than the minor time savings of automating that one field was ever worth risking.

What changed in outcomes after the tiered version shipped

We didn't run a rigorous controlled comparison, but informally tracked client replies to reminder emails for three months after the tiered version launched versus the three months before: the volume of confused or mildly annoyed replies from otherwise-reliable clients dropped to essentially zero, while overall days-to-payment across the whole client base stayed roughly flat — meaning the gentler treatment for trusted clients didn't cost us anything in actual collection speed, since those clients were already paying reliably regardless of reminder tone. The high-risk tier's faster escalation, meanwhile, modestly improved collection speed specifically for the clients who'd historically needed the extra pressure, which is exactly the segment where a firmer, faster sequence should matter more.

Where full automation still isn't the right call

Even the tiered version stops short of fully automating the highest-stakes decision: whether to actually apply a late fee or escalate to a collections conversation on a high-risk-tier client past day 14. That step still routes to a human for a judgment call rather than firing automatically, deliberately, because the cost of getting that specific decision wrong — damaging a relationship that might have a reasonable explanation behind the pattern, or failing to escalate a genuinely bad-faith non-payer promptly — is high enough that the time saved by fully automating it isn't worth the risk, which is exactly the kind of cost-versus-benefit judgment call covered more generally in when to automate a process versus when to leave it manual. The reminder scheduling and wording is safely automatable because a slightly-off gentle reminder is low-cost to get wrong; a wrongly-applied late fee or a premature collections escalation is not.

The general lesson

  • "Treat everyone the same" feels fair in the abstract and often isn't, in practice, when the underlying situations being treated identically are genuinely different — a first-time three-days-late and a habitual non-payer are not the same event wearing different clothes.
  • Automation failures aren't always technical bugs. Version one worked exactly as designed and still produced a bad outcome, because the design itself didn't account for a real, common case.
  • Test the edge case that matters most, not just the common path. The tier-calculation bug specifically broke the exact scenario the whole redesign existed to handle well — a good client's rare late payment — which is precisely the case worth deliberately testing for, not just the average case.
  • The client who complained did us a genuine favor. A quieter, less-reliable client hitting the same rigid schedule might have just paid late again without ever saying anything, leaving the underlying design flaw invisible for much longer than it actually was.

How the tiers get recalculated as a client's history changes

A client's tier isn't fixed once and forgotten — it's recalculated on every new invoice, based on their full payment history up to that point, which means a client can genuinely move between tiers over time as their real behavior changes. A new client starts in the moderate default tier with limited history to draw on either way, and graduates into the trusted tier once they've built up enough of an on-time track record to earn it — typically somewhere around five or six consistently on-time invoices in our experience, though we didn't fix that threshold rigidly and adjusted it slightly after watching how the tier assignments played out against real client behavior over the first several months. Equally, a previously-trusted client who starts missing payments repeatedly drifts back down toward the standard or high-risk tier automatically, without anyone needing to manually flag the change — the system tracks the actual, current pattern rather than a one-time judgment made when the relationship started.

This dynamic recalculation matters because a static, one-time tier assignment would eventually drift out of sync with reality in both directions — treating a client who'd started paying erratically as still "trusted" well past the point that label reflected their actual current behavior, or conversely, never letting a new client earn their way into gentler treatment no matter how reliably they'd started paying since that first, moderate-tier invoice. Recalculating on every invoice, rather than on some separate periodic schedule run occasionally in the background, also means the tier a client is treated at always reflects their real, current behavior as of the most up-to-date information genuinely available at the exact moment a reminder decision actually needs to be made.

Comments

Join the conversation on this article.

Comments are rendered server-side so the discussion stays visible to readers without relying on a separate widget or client-side app.

No comments yet.

Be the first visitor to add a thoughtful comment on this article.

Leave a comment

Share a useful thought, question, or response.

Be constructive, stay on topic, and avoid posting personal or sensitive information.

Back to Blog More in Automation Workflows Free Resources Explore Tools