Skip to main content
Tilbake til blogg
OpenClawMarch 28, 202611 min

My AI does my accounting: the full pipeline explained

From bank transaction to suggested booking to approved entry. How the Budget agent handles Norwegian accounting with human approval at every step.

DB

David Bakke

Founder, Bakke & Co

PostShare
ForsidebildeOpenClaw

The problem with small-company accounting in Norway

I run a Norwegian AS (aksjeselskap). That means I have legal obligations around bookkeeping. Every transaction needs to be booked to the correct account code per the Norwegian kontoplan. Receipts need to be attached to transactions. VAT needs to be reported correctly. An accountant reviews everything at year-end, and they will notice if things are wrong.

For a solo founder running multiple companies and projects, this is death by a thousand paper cuts. Each expense needs a booking. Each booking needs the right account code. Each receipt needs to be matched to the right transaction. It's fifteen minutes of work spread across thirty transactions a month, none of which is intellectually demanding but all of which is mandatory.

I built the Budget agent (nicknamed Budsy) to handle as much of this as possible while keeping me in the approval loop. After three weeks of active development, here's how the full pipeline works.

The booking architecture

A booking in Budsy's world represents a single accounting entry: this transaction from the bank maps to this account code in the Norwegian kontoplan. A grocery purchase goes to 4300 (office supplies) or 7140 (travel expenses related to meals) depending on context. A software subscription goes to 6540 (IT costs). A client payment gets booked as revenue.

The system has three core database tables.

bookings stores the actual accounting entries. Each booking links to a bank transaction, carries an account code, an amount, a status, and optional metadata like VAT category and notes.

account_codes holds the Norwegian kontoplan. I seeded it with 31 accounts covering the most common codes from 3000 (sales revenue) through 7770 (other operating expenses), plus a handful of balance sheet accounts. This isn't the full kontoplan. It's the subset that covers 95% of a typical small AS's transactions.

vendor_account_mappings is where the learning happens. Every time I approve a booking, the system records which vendor was mapped to which account code. When that vendor appears again, Budsy knows what I chose last time.

How a transaction flows through the system

Here's the full lifecycle, from bank import to completed booking.

Step 1: Transaction import. Bank transactions land in Budsy's database through the Fiken integration. Fiken is a Norwegian cloud accounting platform. Its API provides bank transaction data including amounts, dates, merchant names, and reference text. Budsy pulls new transactions periodically and stores them locally.

Step 2: Suggestion generation. When Budsy encounters a new transaction, it runs through a three-signal suggestion engine to propose an account code.

The first signal is vendor history. Budsy checks vendor_account_mappings for the merchant name. If I've booked ten transactions from "GitHub" to account 6540 (IT costs), the eleventh transaction from GitHub will get a high-confidence suggestion for 6540. This is the strongest signal and the one that improves over time.

The second signal is description keyword matching. The transaction reference text often contains clues. "Taxi" or "transport" suggests 7140 (travel). "Kontor" (office) suggests 6300 (office rent) or 4300 (office supplies). This is pattern matching, not intelligence, but it catches a lot of common cases.

The third signal is receipt analysis. If a receipt is attached to the transaction (more on this below), Budsy extracts line items using GPT-4o-mini's vision capability. A receipt from a hardware store listing "HDMI cable" and "USB hub" suggests IT equipment. A receipt from a restaurant suggests meal expenses.

These three signals get weighted and combined into a suggested account code with a confidence score.

Step 3: Confidence routing. The confidence score determines what happens next.

High confidence (strong vendor history match, consistent with keywords): the suggestion appears in the booking queue with a green indicator. I can approve it with one click.

Medium confidence (partial match, or conflicting signals): the suggestion appears with an amber indicator. The UI shows me what the system thinks and why, so I can either accept it or pick a different code.

Low confidence (new vendor, no history, ambiguous description): the transaction gets flagged for manual review. No suggestion is made. I need to pick the account code myself.

Step 4: Human approval. This is the step I refuse to automate. Every booking requires my explicit approval. I click "Book this" on the transaction row, review the suggested account code in the BookingDrawer, and confirm.

For high-confidence suggestions, this takes about two seconds per transaction. Review the vendor, confirm the code, click approve. For flagged transactions, it takes longer because I need to think about which account code is correct. But even the fast approvals are intentional. I see every transaction that gets booked.

Step 5: Learning. When I approve a booking, Budsy upserts the vendor-to-account mapping. If this is the first time I've booked a transaction from "Spotify," the mapping is created. If it's the fifteenth time, the existing mapping's confidence score increases. This is the learning loop that makes the system faster over time. Each approval teaches Budsy something.

The receipt pipeline

Receipts deserve their own section because this is where multiple agents collaborate.

The starting point is a Slack channel called #ai-receipts. When I get a receipt (PDF, photo from my phone, email attachment), I drop it in that channel. Two agents process it.

Budsy extracts the structured data: vendor name, total amount, date, individual line items, VAT amount. This extraction uses GPT-4o-mini's vision model, which is surprisingly good at parsing receipt images. Even crumpled paper receipts with poor lighting usually get extracted correctly. Norwegian receipts with "MVA" (VAT) amounts get parsed correctly about 90% of the time.

The Folio agent handles the bank attachment side. Folio connects to my actual bank through the Folio API (a Norwegian fintech service for bank integrations). When Budsy extracts the receipt data, it sends the structured information to Folio via sessions_send. Folio then tries to match the receipt to a bank transaction by amount and approximate date, and attaches the receipt file to the matching transaction.

This two-agent split exists because the concerns are genuinely different. Budsy cares about accounting classification. Folio cares about bank record matching. Keeping them separate means each agent has a focused job and the security surface is smaller. Folio only has permissions to read transactions and write attachments. It can't initiate payments or modify balances.

The account code picker

One design decision that turned out to matter more than I expected: the AccountCodePicker component.

Accounting is about codes. The Norwegian kontoplan has hundreds of them. A dropdown list with 300 options is useless. So the picker does a few things to make selection fast.

It groups codes by category. Revenue codes (3000-3999) are together. Purchase costs (4000-4999) are together. Personnel costs (5000-5999), office costs (6000-6999), and so on. This matches how accountants think about the chart.

It shows recent selections first. If I've been booking a bunch of software subscriptions, the IT costs codes float to the top. This handles the common case where you're processing a batch of similar expenses.

It supports search. Type "reise" and you get travel-related codes. Type "6540" and you go directly to that code. For someone who knows their kontoplan (I'm learning it by repetition at this point), direct code entry is the fastest path.

Why I never auto-approve bookings

The temptation is obvious. If vendor history says "GitHub = 6540" with high confidence based on fifteen prior approvals, why not just auto-book it?

Three reasons.

First, context changes. A GitHub payment is usually a subscription (6540, IT costs). But once a year, it might be a GitHub Enterprise purchase that should go to a different code. If I auto-book, I'd miss that.

Second, Norwegian accounting compliance. The bookkeeper (regnskapsforer) needs to trust that I've reviewed every entry. If I tell them "my AI auto-booked these," that's a different conversation than "I reviewed and approved each one." The legal responsibility for correct bookkeeping sits with me, not with my AI.

Third, errors compound. If an auto-booking is wrong and I don't catch it for three months, correcting it means adjusting prior periods. In Norwegian accounting, this is messier than it sounds. Quarterly VAT returns might need amending. It's far easier to spend two seconds approving each booking than to untangle months of incorrect entries.

So the rule is firm: suggest everything, auto-approve nothing. The system can be as confident as it wants. I still click the button.

Reimbursement detection

This is a small feature that saved me real headaches.

I use personal bank accounts for some business expenses (yes, this happens). When Budsy sees a transaction from a personal bank account that's associated with my AS, it automatically flags it as a potential reimbursement. The flag shows up in the UI with a distinct indicator.

This matters because reimbursements have different accounting treatment. A business expense paid from a personal account creates a claim against the company. It needs to be tracked separately and eventually reimbursed. If I miss the flag and book it like a normal expense, the accounting gets tangled.

The detection logic is simple: transaction comes from a bank account where company_id doesn't match the account's owner company_type. That mismatch triggers the flag. It's not AI. It's a database check. But it's the kind of thing I'd miss manually when processing a batch of transactions late on a Tuesday evening.

The Fiken integration

Fiken is where the official books live. Budsy is the workspace where I classify and approve. The two need to talk to each other.

The Fiken API is REST-based and well-documented. Budsy uses it for two things: pulling bank transaction data into its own database, and (eventually) pushing approved bookings back to Fiken as journal entries. The pull side is working. The push side is still in development.

The gap matters because right now, I approve bookings in Budsy and then someone (me or an accountant) still needs to enter them in Fiken. This is an acceptable temporary state because the approval workflow in Budsy is already much faster than doing everything in Fiken's UI directly. But the end goal is to close the loop: approve in Budsy, auto-create the journal entry in Fiken, and the only manual step is the quarterly review.

The Fiken push requires mapping Budsy's account codes to Fiken's internal account IDs. The Norwegian kontoplan is standardized, so the numbers match, but Fiken's API wants its own identifiers. This mapping is straightforward but hasn't been built yet.

What still requires my attention

Budsy handles the routine stuff well. Software subscriptions, recurring costs, clear-cut expenses. These flow through the suggestion engine, get high-confidence scores, and I approve them in a few seconds each.

What still needs manual work:

Ambiguous transactions. Some bank transaction descriptions are cryptic. A payment to "SumUp *Misc" could be from any merchant that uses SumUp as a payment processor. Budsy can't suggest an account code because there's no vendor history for "SumUp *Misc." These get flagged and I have to figure out what the purchase actually was.

Multi-category receipts. A trip to an electronics store where I bought both business equipment and a personal item. The receipt has one total but two different accounting treatments. Budsy doesn't split receipts yet. I have to handle these manually.

New vendors. The first transaction from any vendor has no history. The system gets smarter fast (one approval creates the mapping), but that first occurrence is always manual.

End-of-period adjustments. Accruals, prepayments, depreciation. These aren't transaction-driven. They're accounting concepts that require judgment. I don't expect Budsy to handle these, probably ever.

Fifty-two days in

I started building OpenClaw on February 4th. Budsy came together in mid-March. In its first week of active use, it processed about 40 transactions. Roughly 25 of those got high-confidence suggestions that I approved in under five seconds each. Ten got medium-confidence suggestions that needed a quick review. Five were flagged for manual classification.

That's not a bad ratio for a system that's still building its vendor history. As the mappings accumulate, the high-confidence percentage will climb. By the time I've processed a few hundred transactions, most recurring expenses should be essentially one-click approvals.

The receipt pipeline is newer and still rough around the edges. OCR accuracy on Norwegian receipts is about 90%, which means one in ten needs manual correction. The bank matching in Folio works when the amount is exact and the date is close, but struggles with transactions that post a few days after the purchase.

It's not perfect. It's not supposed to be. It's supposed to make a tedious task fast enough that I actually do it on time instead of letting receipts pile up for three months and then spending a painful Saturday catching up. By that measure, it's working.

openclawaccountingbudgetautomation