Blog · October 17, 2026

How bill detection works — the ledger proposes, you approve

Oikonome never asks you to type in your bills. It reads the charges your bank already sent, finds the ones that repeat on a cycle, and proposes them. Here is exactly how that works, what it refuses to do on its own, and why a price hike needs no paperwork.

Every budgeting app has a bills screen, and most of them want you to fill it in by hand: name, amount, due date, repeat. You do it once, the gym raises its price, the streaming service adds a dollar, and six months later the screen is wrong in ways you stopped noticing.

Oikonome takes the other road. Your bank already sends every charge. The bills are in there. The app's job is to find them and keep them honest, and to do it without ever inventing a bill you did not have.

It reads the ledger, not a form

Detection scans up to 48 months of your transaction history looking for merchants that charge on a steady cycle: weekly, every two weeks, monthly, every two, three or six months, or yearly. A merchant has to show up at least three times on a cycle before it is proposed, two for the long cycles like semi-annual and annual where three would take a year and a half to collect.

It runs every night, and on demand from the Bills page with the Find bills & income button. It looks for income the same way, so paychecks and transfers that arrive on a schedule show up beside the bills, and the page's headline sums both as evened-out monthly figures: what bills cost per month, what income brings in, and what is left for everything else.

Everything is a proposal

This is the part that matters. Detection does not write to your bills. It writes proposals, and a proposal sits in a pending list until you approve or dismiss it. The kinds it makes:

If your bank connection knows its own recurring streams, those are cross-checked nightly too. A stream the bank sees that no bill covers arrives as a proposal marked "the bank sees this recurring", through the same approve-or-dismiss door. You can switch that cross-check off and rely on the ledger alone.

Dismissing is not a shrug the app forgets. Dismiss an amount offer and that price is not offered again until the bill's amount changes. Approve an amount offer after you have already edited the amount yourself and the offer is refused and withdrawn: your number stands.

The one thing that moves on its own: amount drift

Subscription prices creep. If every $1 change needed a proposal and a click, the pending list would be a chore and people would stop reading it. So there is exactly one automatic change.

When a bill's real charge settles at a new amount, at least a dollar different, the bill's amount updates itself and leaves an audit entry. The move is bounded by the bill's matching tolerance, which is half the bill up to $30 for a bill under $120, and a quarter of the bill above that. A $15 subscription can drift by $7.50, an $80 one by $30, a $200 one by $50.

A bigger jump is not drift. A gym that goes from $80 to $45 is not the same bill at a new price until you say so, which is why that case comes through as an amount change proposal with the charge attached: "charged $45.00 on 10/01, the bill says $80.00". Approve it, or edit the bill yourself.

And if you did edit the amount yourself, detection leaves it alone for 60 days. A run that was already under way when you saved never writes over your edit either. Your edit wins.

A bill also remembers the amounts it had before, so a payment made at an earlier price stays marked paid months after the amount moved, whether it moved by drift, by proposal or by your hand.

How a charge is matched to a bill

A bill carries the merchants it pays. A charge matches when it is one of those merchants' rows within the amount tolerance. That is merchant identity, not text matching, so renaming or merging a merchant keeps the bill paid. Only a bill with no merchants on it falls back to matching words in the transaction text, and there an apostrophe does not matter: "Juniper's Market" finds the charge a bank prints as "JUNIPERS MARKET".

Three rules keep matching from flattering you:

Bills that are really caps

Not every recurring cost has a due date. Groceries repeat, but on no schedule and at no fixed amount. For those there are envelope bills: the amount is a spending cap, monthly or an annual pool for lumpy costs like car repairs. Detection can propose monthly envelopes for refill-style spending; annual pools are always yours to create.

An envelope can also pool a whole category. A pet-care pool draws from the vet, the groomer and the pet store, three merchants no matcher could unify, because you told it the category. Scheduled bills still match first, so a real bill's charge never leaks into a pool.

A bill that categorises its own charges

Some merchants are two things. A gym's monthly membership is fitness; the coffee at its café is dining. A merchant rule can only say one, so an expense bill can say the other: set "matched charges are …" to a category and every charge the bill matches is categorised that way, past and future. Charges at the merchant that the bill does not match keep the merchant's own category, a category you set on a row yourself always wins, and deleting the bill puts its rows back.

Why it is built this way

The old way, a form you fill in, fails slowly and silently. The ledger way fails loudly or not at all: a bill either has charges behind it or it is a proposal waiting for you. Nothing on the Bills page exists because someone typed it in and forgot. Everything on it has been charged, on a cycle, by a merchant the app can name, and the only number that ever changes without you is a small price drift that leaves a note saying it did.

That is also what makes the daily email trustworthy. When it says a bill is due tomorrow, the bill is real, the amount is current, and the day you paid it last is on record.

Start free — 30 days, no card Try the live demo Self-host it