The problem cutovers create
A business that has always invoiced monthly in arrears gives its clients roughly a month between invoices. Move that business onto per-job invoicing — where a completed job drafts its own invoice — and the old and new rhythms collide exactly once.
The last month-in-arrears invoice goes out late, because migrating took time. Then the first job of the new month completes, and the platform drafts an invoice immediately. The client receives a second invoice one to four days after paying the first.
Nothing is wrong with either invoice. They cover different work. But the client's inbox tells a different story, and the call you get is "I've just paid you."
The fix is not to delay the invoices. It is to sort the queue before you send, and let each invoice carry the explanation it actually needs.
Sort the queue by evidence, not by feel
Pull every draft with list_invoices(status: "draft"). For each one, get the client's own invoice history with list_client_invoices(client_id).
Two facts decide the bucket:
- The gap — days between the previous invoice's
sent_atand this draft. Notesent_at, notissued_at. An invoice issued in July but emailed in August lands in the client's inbox in August, and the inbox is what they react to. - Whether that previous invoice is paid. A client who just paid needs reassurance. A client who hasn't needs something else entirely.
| Bucket | Test | Treatment |
|---|---|---|
| A — close behind | previous invoice sent within about a week and paid | Send with a short timing note |
| B — first invoice ever | no previous invoice for this client | Welcome note. No timing note |
| C — normal spacing | previous invoice sent more than a month ago | Send plain |
| D — blocked | a data problem surfaced at preview | Fix before sending |
Bucket B matters more than it looks. A brand-new client who receives "your July invoice went out late" has no idea what you're talking about — you have just made a clean first impression confusing. Buckets are about who needs which explanation, not about being thorough.
Write the note once, vary it slightly
Personalise the send with update_pending_send(cover_message) after staging and before approving. See Personalise a staged send for how the slot renders.
A note that works has four moves in it:
- Name the cause without drama. "Your last invoice only went out at the start of the month while we were moving across to a new system."
- Say the thing they're actually worried about, in your own words. "Nothing has doubled up — the last invoice covered the previous visit, and this one covers this month's."
- Promise the rhythm returns. "From here it settles back to the normal monthly spacing." This is the sentence that stops them wondering whether it happens every month.
- Thank them for the payment they just made. They did the right thing quickly; say so.
Vary only the site or service reference between clients. Resist rewriting the whole note per client — consistency is not impersonal here, and four hand-written variants is four chances to say something you did not mean.
What "blocked" looks like
Preview is where data problems surface, because the preview shows you the resolved recipient rather than the record you assumed was there. Things worth stopping for:
- The billing email is not the client's. During a migration it is common to find a placeholder — often the operator's own address — sitting in
billing_email. The send will succeed and reach nobody who matters. - The client record is still a lead. Invoicing works, but reporting and portal access will behave oddly later.
- Terms are due-on-receipt when they shouldn't be. The invoice renders with a due date equal to its issue date, so it arrives already due. That reads as aggressive when the real cause is an unset field.
Fix the record, then send. Do not send and fix afterwards — the client already has the email.
Verify at the end, not per invoice
When the run is finished, call list_invoices(status: "draft") again. An empty result is the assertion that matters: every draft was dealt with, not skipped. Then spot-check that anything you settled outside the email path reads status: "paid" and outstanding_cents: 0.
Do it once, then make it structural
Timing notes are a one-off remedy for a one-off collision. The durable fix is the client's invoicing cadence — per job on completion, consolidated for the period, or on the period's last job. Set that deliberately per client during onboarding and the collision never recurs.