Bundul
Internal
Browse docs
Waiting for review

archive

Utility bill insights — executive brief

A frozen record of a decision at the time. Superseded by a new record rather than edited.

Written by the build · 2 September 2026

Utility bill insights — executive brief

Last updated 20 August 2026. Shipping now. Customer messaging stays off until we switch it on.


What

Two things, both about utility bills.

1. Telling customers where they stand. When a bill arrives, we tell the customer how it compares to their normal and whether they're heading for a credit or a bill at the end of the quarter.

"SDG&E bill: $284.00 — That's about $75 more than a normal August for you. Looks like you'll owe about $190 at the end of the quarter."

2. Keeping their monthly price honest. When someone's bills have genuinely settled at a new level, we recommend changing what we charge them each month, instead of chasing the same shortfall with a true-up every quarter forever.


Why

Customers pay us a fixed amount each month, but their utility bills vary. The gap builds up and we settle it quarterly. Today they only find out at the end.

The gaps are real. Across our two utility customers last quarter: one was $223 short, the other had overpaid by $606. That is money moving quietly in both directions.

The second half matters more than the first. A customer whose price is $200 a month too low will owe money every single quarter until the price changes. Telling them about it is helpful; fixing it is worth more.


How

We save every bill. Previously the system found up to ten past bills in a customer's inbox, kept two, and overwrote them next time — so "what's normal for this customer?" was unanswerable. Now every bill is kept.

We compare fairly. A bill covering 34 days isn't a spike against one covering 30. Money carried over from last month isn't this month's usage. And we compare against the same month last year, not the last few months — because comparing a summer bill to spring would flag the most predictable event in someone's year.

We stay quiet unless it matters. A bill has to be off by both a dollar amount and a percentage before we say anything. When we don't know someone's normal — most customers, for now — we say only what's plainly true and never pretend to know more.

Every sentence is written in advance. Ten of them. Nothing is generated on the fly, because these are statements about someone's money and every one should be read and approved before a customer receives it.

For pricing, we use a full twelve months. Not the last two quarters. Priced off summer, a seasonal customer would overpay all winter. Increases are capped, changes happen at most once a quarter, the customer is always told before it takes effect, and a person approves every one.

A price change never includes what someone already owes. That's settled separately by the true-up. Mixing them would collect the same money twice.


What the customer actually experiences

1. A bill arrives

We read their connected inbox on a schedule. When a bill we've never seen before turns up, we work out what it means. Most of the time we say nothing — a bill $6 off their normal isn't worth a phone buzz, and that silence is the most common outcome by design.

2. If it's worth saying, they get a notification

SDG&E bill: $284.00 That's about $75 more than a normal August for you. Looks like you'll owe about $190 at the end of the quarter.

Or, for a customer we don't know well enough to compare — which is most of them for now:

T-Mobile bill: $216.00 You're about $119 behind this quarter, with 1 month to go.

Or the good news version:

SDG&E bill: $118.00 That's about $25 less than a normal April for you. You're building up a credit of around $60.

All of this wording is editable by us in the admin, without a release — see "What we say" below. There are ten sentences in total and every one can be reworded. The numbers inside them are protected: an edit that removes one is refused, so a customer can never receive a sentence with a blank where a figure should be.

Only one message per customer per day, however many bills land at once.

3. They tap it

It opens a bill detail screen in the app:

  SDG&E — August bill                              $284.00
  ────────────────────────────────────────────────────────
  A normal August for you                          ~$208
  This bill                                    ▲ $76 more

  Why it might be higher
  • This bill covers 34 days. Yours usually cover 30.

  Your quarter so far (July–September) — all services
  You pay us          $175 a month  →  $525
  Your services cost  $222 + $247   →  $469 so far
  September estimate                   ~$183
  ────────────────────────────────────────────────────────
  Looks like you'll owe about $125 when the quarter ends
  (an estimate — we confirm the real number after September)

Three things about that screen:

  • The "a normal August for you" line disappears when we don't have the history to fill it. We don't show a zero or a guess — the block simply isn't there.
  • The quarter box covers their whole One Sub, not just this utility, because that's how the quarter-end settlement is actually calculated.
  • Any projected figure is always labelled an estimate, because the final number is confirmed by us after the quarter closes.

4. Separately: if their monthly amount is changing

A different notification, sent before the change takes effect, never after:

Your SDG&E amount is changing From September 1 you'll pay $369.25 a month instead of $295.40. Your bills have been costing more than we've been collecting, so this keeps you closer to even.

That one opens their existing One Sub screen. A person approves every price change; nothing moves automatically.


Where to find it in the admin

Bill insights — new item in the left menu, under Operations.

On that page What it does
Utility bill messages (top) The on/off switch, plus the allowlist that limits messaging to named accounts. Off by default.
When we speak up The five cut-offs that decide how often we say anything. Change them without a release.
What we say All ten sentences, editable. A rewording that drops a number is refused.
Per-customer cards Each customer's bills and the exact message we'd send them. Expand for the full history.
Replay every bill Runs every stored bill through the comparison and lists what we would have said. Sends nothing.

True-up — the existing page, now with a Monthly price check panel at the top: who's on the wrong price, what it should be, and Approve / Leave it. Correctly-priced customers are listed too, collapsed.

Notification settings — the same on/off switch also appears here, next to the other automations.


What we didn't do, and why

Actually moving the price on the day it takes effect. Recommending, approving, recording and notifying are all built. The final step — changing the live payment — is not. It touches what customers are charged, so it needs its own careful pass rather than being tacked onto the end. Paused by decision, not oversight.

Because of that: the Approve button should be disabled for now. Approving today would schedule a change and tell the customer about it, but the change wouldn't happen. Small fix, worth doing before this goes live.

The customer's bill screen in the mobile app. Everything behind it is built and handed over in docs/utility-bill-insights-fe-handover.md. The screen itself is the mobile team's.

Any kind of machine learning. We have four utility customers and roughly one bill a month each. That is not enough to train anything on, and a model built on it would be a complicated way of doing arithmetic nobody could then explain. What we built is: add up the bills, take the middle value, subtract, check the gap is big enough. Every number can be checked with a calculator. If the data ever justifies more, it slots in behind the same interface.

Emailing customers. Push only for now. The quarter-end position is the piece that would earn an email — people want a record of that. A per-bill nudge doesn't.


Four bugs found along the way

Each was found by testing against real data, and each was already costing us something.

  1. Customer prices were being set from incomplete data. The code that averages a customer's past payments to set their monthly price was only reading the first 100 transactions out of as many as 1,228. Every utility price came from an arbitrary partial sample. Likely a direct contributor to the gaps above.
  2. We were ignoring the payment history we needed. Once Bundul takes over paying a utility, the bank stops seeing the customer pay it directly and marks the record inactive — and we were filtering those out. We were excluding exactly the customers we wanted history for.
  3. A safety guarantee was never switched on. The rule that stops us notifying a customer about the same bill repeatedly relies on a database setting that production doesn't apply automatically. It would have shipped as a silent repeat-notification bug.
  4. 39 stale utility records cluttering the admin, left behind by failed bundling attempts. Removed, backed up, and the cause fixed so they stop accumulating.

Where things stand

This is going out now. Deploying is safe on its own: customer messaging is off by default and controlled from the dashboard, so nothing reaches anyone until someone deliberately turns it on.

What the deploy actually changes on day one:

  • Bill history starts collecting. The next invoice refresh saves every bill it finds instead of two. Until now that history was being read and thrown away, so this is the thing that has been costing us time.
  • Customer prices stop being set from partial data. The pagination fix takes effect immediately for any new utility.
  • The admin screens go live — bill insights, the cut-offs, the wording, the price check. Read-only until there's data in them.
  • Nothing is sent. No push, no email, no price change.

One thing to do before anyone approves a price change: the Approve button on the True-up price panel should be disabled. Approving today schedules a change and tells the customer, but the change itself isn't built yet. It's a small fix — worth doing in the first follow-up rather than leaving a button that looks like it works.

The first few weeks

When What
Straight after deploy Nothing visible. History begins accruing on the next refresh.
Within days Bills appear on the Bill insights screen. Read the messages we would send.
Once the copy reads right Switch messaging on for our own accounts only, using the allowlist.
After that Widen to real customers.
~6 months in Enough history for the price check to start recommending changes.
~12 months in "Higher than a normal August" becomes possible for today's customers — see below for why it's sooner for some.

What each customer gets on day one

We pull history from three places on the first refresh after deploy:

  1. Their inbox — up to ten past bills, with the richest detail.
  2. Their bank — where their payments to that utility are visible, sometimes going back two years.
  3. Our own records — every bill we have already paid on their behalf. We know the exact amount and the exact month, because we paid it.

The third one matters more than it sounds. One of our customers has no visible bank transactions at all — but we hold six months of their electricity bills and four of their gas, because we paid every one of them.

For the four utilities on the book today:

Customer Utility History on day one
nkem So Cal Edison ~6 months, from bills we paid ($116–$984 — a very variable account)
nkem So Cal Gas ~4 months, same source
Olamide Aqua Finance A full year — but it's a flat $96.53 every month, so there is never anything to comment on
Olamide T-Mobile A few months from the bank plus the inbox

So nobody starts from zero. What none of them have yet is a full year, which is what "higher than a normal August" requires — that is roughly a year out for these four, and it accrues month by month from here.

That is a fact about these four customers, not about the feature. A new customer whose bank shows two years of payments to their electricity company gets the full comparison the day they bundle.

The quarter position — "you're $119 behind with a month to go" — works as soon as messaging is switched on, for every customer, with no history at all. That's the part worth watching first.

Detail: docs/utility-bill-insights-plan.md. For the mobile team: docs/utility-bill-insights-fe-handover.md.

This is a record of a decision at the time. It is not edited — write a new record that supersedes it.