DEV Community

Cover image for Cashcast: which invoices will be paid late? TabPFN on a laptop, checked month by month
StarKnight
StarKnight

Posted on

Cashcast: which invoices will be paid late? TabPFN on a laptop, checked month by month

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

Cashcast tells a small business which unpaid invoices are likely to be paid late, when each payment is likely to arrive, and how much cash to expect over the next eight weeks. It runs on your own laptop, so your client list and amounts stay with you.

It is built for people who send invoices and then wait: freelancers, small agencies, and the bookkeepers who manage receivables for several small clients. For them, late payment is not an abstract metric. It decides whether rent goes out on time and how much of the week goes into writing reminder emails.

Cashcast reads an invoice history (one CSV row per invoice: client, issue date, due date, amount, paid date) and, for every invoice still open, shows:

  • the chance it will be paid late,
  • a likely payment date with an 80% range, updated for how long the invoice has already been open,
  • a suggested next step: contact today, send a reminder now, remind on the due date, or do nothing,
  • the weekly cash you can expect from invoices already sent.

Who would pay for it, and why. Cloud accounting suites offer payment reminders and cash-flow views, but they expect the whole ledger in their cloud and a monthly subscription. Cashcast is a single-purpose tool that works on the CSV export people already have. A one-time license for freelancers, or a per-client fee for bookkeepers, is a plausible model, because the value is easy to state: fewer surprises in cash flow and less time spent chasing the clients who were going to pay anyway. Running the model locally is what makes a small one-time price workable, since there is no per-prediction API bill behind it.

Cashcast demo: pick a date, see the riskiest invoices, reveal what happened, then the backtest

Demo

Live demo (no install): https://starknightt.github.io/cashcast/

The live page is a replay. It uses IBM's public accounts receivable sample: 2,466 invoices from 100 business clients, 2012 to 2013, 36% of them paid late. Pick a date and you see what Cashcast would have told you that morning, using only invoices that had been paid before that date. Then tick Reveal what happened to put the real payment dates next to the predictions.

Things to try:

  1. Click Sep 1, 2013. 78 invoices are unpaid. Cashcast expects 4,327 to come in over the next four weeks (the sample has no currency). Tick Reveal what happened: 4,241 actually came in.
  2. Click Needs action to see only the invoices it suggests chasing, each with a one-line history computed from the data, for example "Paid 13 of 15 past invoices late, 36 days to pay on average."
  3. Scroll to Does it work? for the month-by-month backtest, including the result where TabPFN did not win.

Replay of Sep 1, 2013 with outcomes revealed

Using it on your own invoices needs Python 3.10 or newer and works on a CPU. python -m cashcast.serve opens the same page in local mode with a file picker. The file goes from the browser to the Python process on 127.0.0.1 and nowhere else. On the bundled 404-row sample ledger, scoring takes about 7 to 9 seconds on my laptop CPU.

Code

GitHub logo StarKnightt / cashcast

Know which invoices will be paid late, and when the money will arrive. TabPFN on your laptop, client data stays local.

Cashcast

Know which invoices will be paid late, and when the money will arrive, before you chase anyone.

Cashcast is a small receivables tool for freelancers, small agencies and the bookkeepers who manage invoices for them. You give it your invoice history as a CSV. TabPFN, an open tabular foundation model, runs on your own laptop and tells you, for every unpaid invoice:

  • the chance it will be paid late,
  • a likely payment date with an 80% range, updated for how long the invoice has already been open,
  • a suggested next step (contact today, send a reminder now, remind on the due date, or nothing),
  • how much cash to expect in each of the next eight weeks.

Your client list and amounts never leave your computer.

Live demo: https://starknightt.github.io/cashcast/ (demo mode, no install)

Cashcast demo: pick a date, see the riskiest invoices, reveal what happened, then the backtest

Demo mode

The live page replays IBM's public accounts receivable sample (2,466 invoices from 100 business…

MIT licensed. The pipeline is about 660 lines of Python plus a static page with no build step:

cashcast/data.py       load the IBM sample or any CSV into one schema
cashcast/features.py   what was known on the day each invoice was sent
cashcast/model.py      TabPFN v2 classifier and regressor
cashcast/forecast.py   late chance, payment range, 8-week cash simulation
cashcast/backtest.py   month-by-month test against four baselines
cashcast/demo.py       replay data for the live page, scored against reality
cashcast/serve.py      local mode (standard library HTTP server)
Enter fullscreen mode Exit fullscreen mode

How I Built It

TabPFN does the predicting

TabPFN is a tabular foundation model from Prior Labs. It was pre-trained on millions of synthetic tables, so instead of training a model on your data, you hand it your table and it predicts in a single forward pass. For a small business with a few hundred or a few thousand invoices, that matters: there is no training loop to tune and nothing to babysit.

Cashcast uses two TabPFN v2 models on the same features:

  • a classifier for "will this invoice be paid after its due date?",
  • a regressor for "how many days until it is paid?", asked for 49 quantiles so it returns a full distribution per invoice rather than one number.

Features from the day the invoice was sent

The easiest way to fool yourself with this kind of data is leakage. The IBM sample includes columns such as DaysToSettle and Disputed that are only known after an invoice is settled, and using them makes the task look far easier than it is.

So features.py builds every row from what you knew on the day the invoice went out: how many of that client's invoices had already been paid, how often they were late, their average and most recent days to pay, how many of their invoices were still open, and how this amount compares with their usual. Invoices paid after the issue date are invisible to that row.

From a distribution to a decision

The regressor's quantiles are the useful part. An invoice that is still unpaid after 25 days is a different bet from one sent yesterday, so forecast.py conditions each distribution on the invoice's current age. From that it reads:

  • the late chance, as P(paid after the due date | still unpaid today),
  • the 10th, 50th and 90th percentile payment dates,
  • 2,000 simulated payment schedules per date, summed per week, for the cash forecast.

The next-step labels are plain rules on top (overdue: contact today; 50% or more: reminder now; 30% or more: remind on the due date). The history line beside each invoice is computed, not generated, so it cannot state something the data does not support.

Did it work? A month-by-month backtest

For each month from January to October 2013, every model was trained only on invoices paid before that month began, then predicted the invoices sent during the month: 1,075 predictions in total. TabPFN on a CPU accepts up to 1,000 training rows by default, so each month uses the 1,000 most recent paid invoices.

Model AUC Brier Chase recall Pay day error 80% range hit
TabPFN v2 0.882 0.127 51.0% 5.92 days 79.1%
Logistic / linear regression 0.890 0.122 50.4% 5.81 days 79.3%
Client's own history 0.870 0.135 50.7% 6.54 days n/a
Gradient-boosted trees 0.864 0.154 49.9% 6.23 days 57.5%
Base rate 0.5 0.218 18.4% 10.11 days n/a

"Chase recall" answers the business question: if you only chase the riskiest 20% of each month's invoices, what share of the late ones do you catch? Lower Brier and pay day error are better.

The honest reading: on this dataset, a plain logistic and linear regression is as accurate as TabPFN. I expected TabPFN to win clearly, and it did not. It does clearly beat gradient-boosted trees trained on the same rows, whose 80% ranges caught the real payment date only 57.5% of the time.

The probabilities are well calibrated. Of the 586 invoices TabPFN put below 20%, 8.7% were paid late (it predicted 6.2% on average). Of the 116 it put above 80%, 90.5% were (it predicted 87.4%).

Backtest table, range adaptivity and calibration chart from the live page

Where TabPFN earns its place

A linear model with a residual band gives every invoice the same width of uncertainty. TabPFN gives each invoice its own distribution. To test whether that matters, I split clients into thirds by how much their past payment times varied:

80% range Steady clients Middle Erratic clients
TabPFN 80.5% hit, 16.0 days wide 83.3%, 17.7 days 73.3%, 19.9 days
Linear 83.8% hit, 18.4 days wide 84.2%, 18.5 days 69.7%, 18.4 days

TabPFN narrows the range for reliable payers and widens it for erratic ones, which is what you want, but the effect is modest and both under-cover the erratic third. The distribution is also what lets Cashcast answer "this invoice is 20 days old and still unpaid, now what?" without a separate model.

Checking the replay itself

The numbers on the live page were scored against what really happened (10 dates, 905 unpaid-invoice rows):

  • Of 296 "send a reminder now" flags, 78% were in fact paid late. Among all not-yet-due invoices on those dates, 43% were.
  • The real payment date fell inside the 80% range 77.1% of the time, with a typical miss of 5.78 days.
  • The weekly cash forecast is the weak spot: the actual amount landed inside its 80% range in only 67.5% of 80 weeks. The simulation treats invoices as independent, so the range is too narrow.
  • Each date takes about 40 seconds on a laptop CPU (classifier plus regressor on 1,000 rows).

What it still cannot do

  • The sample contains only invoices that were eventually paid, so Cashcast has never seen an invoice that is written off.
  • Every invoice in the sample has 30-day terms, so the effect of other terms is untested.
  • It suggests whom to contact; it does not send anything.
  • The live page is a replay of a public dataset. Before trusting it on your own ledger, run the same backtest on your history (python -m cashcast.backtest shows how).

Why Does Open Innovation Matter?

It keeps the client list at home. An invoice ledger is a list of who pays you, how much and how reliably. With open weights, Cashcast runs on the laptop that already holds that file. The local server binds to 127.0.0.1, and after the one-time weight download, scoring still works with all network traffic blocked (I tested it by routing every request to a dead proxy).

It makes a small, cheap product possible. A one-time-license tool cannot carry a per-prediction API bill. Open weights mean the marginal cost of a forecast is a few seconds of CPU.

The license has to allow selling it. Prior Labs recommends its newest model, TabPFN-3.5, through its API under a free non-commercial license. That is a fine choice for research, but a side hustle is commercial by definition. TabPFN v2 weights are published under the Prior Labs License 1.1 (Apache 2.0 with an attribution requirement, which is why the page says "Built with PriorLabs-TabPFN"), download from Hugging Face without an account, and run offline. For this product, the openness of the license mattered as much as the openness of the weights. The trade-off is that I may be leaving accuracy on the table by not using the newest model.

Openness made the evaluation possible. Because the model, the data and the code are all inspectable, I could run every baseline on exactly the same rows and publish the result where TabPFN did not win. A black-box API would have given me a number to trust instead of a number to check.

Prize Categories

Best Use of TabPFN. TabPFN does all of the predicting in Cashcast. A TabPFN v2 classifier estimates the chance each invoice is paid late, and a TabPFN v2 regressor returns a 49-quantile distribution of days to payment, which Cashcast conditions on invoice age to produce payment ranges, reminder decisions and an eight-week cash simulation. It is tested month by month against four baselines on 1,075 invoices, with calibration and per-client range checks, and the results, including where a linear model matched it, are published in the repository and on the live page.

Top comments (0)