The polite-but-firm invoice chaser.
You stay the good cop.
BadCop sends an escalating ladder of payment reminders from a separate accounts@ address, applies the late fee your contract already allows, and stops the moment the money lands. Free, open source, zero dependencies.
Get it on GitHub Just want the email templates?$ badcop run --dry-run
DRY RUN INV-1001 -> ap@acme.example [firm, +7d]
Subject: Overdue: invoice INV-1001 (7 days past due)
DRY RUN INV-1002 -> hello@bluefern.example [courtesy, -3d]
Subject: Invoice INV-1002 is due on 2026-09-11
2 would be sent
Why it exists
A freelancer on r/smallbusiness added up their year and found $3,200 in late and unpaid invoices. Not because clients were crooks, but because sending the firm email felt rude, so it went out on day 10 instead of day 1, and the late fee in the contract was never enforced. They asked for a "bad cop". This is it.
The ladder
- T-3dCourtesy. Heads-up that the invoice is due soon, with the pay link.
- T+1dFriendly. It was due yesterday; when can we expect it?
- T+7dFirm. A week overdue; asks for a date and mentions the fee terms.
- T+14dFinal notice. Applies the contractual late fee; balance goes up.
- T+30dEscalate. Goes to you, not the client: call, payment plan, or small claims.
Every step is sent exactly once per invoice. If the client pays after the friendly reminder, nothing else goes out. If they don't, the tone escalates on a fixed schedule so you never have to decide whether today is the day.
How it works
- Keep a CSV of your invoices. Export it from Wave, FreshBooks, QuickBooks, Stripe or whatever you use, or type it in.
- Run
badcop runonce a day with cron, a systemd timer or Docker. Or skip the server entirely: the template repository runs it every morning on GitHub Actions for free. - Export a bank CSV when money comes in.
badcop match bank.csv --applycloses the matching invoices. badcop reporttells you what is outstanding, how old it is, and what it is costing your clients.
Install
pip install badcop
badcop init # sample config, ledger and templates
badcop run --dry-run # read every email before anything is sent
Python 3.11 or newer. Nothing else. Full documentation on GitHub. Don't want to run anything? Use the template: a private repo that runs the ladder daily on GitHub Actions, with your invoices in a CSV or a Google Sheet.
Free resources
Questions
Will this annoy my clients?
The default copy is courteous through the firm step and factual in the final notice. Nothing threatens; the last step goes to you, not to the client. You can preview and rewrite every email before anything is sent.
Can I charge late fees?
Only if your contract or invoice terms say so. BadCop applies whatever percentage and grace period you enter and nothing else. Set it to zero if you have no such terms.
Does it send invoices?
No. It chases the ones you already sent, from whatever tool you use. Keep invoicing the way you do.
Does it need my bank login?
No. Payment matching reads a CSV you export from your bank or payment processor.
What does it cost?
The command-line tool is free and open source (MIT). A hosted version that runs it for you is planned; the CLI will stay free.