Inside HDL 5 september 2026 7 min läsning

We Moved Our Own Books First

What replacing our own accounting system taught us about when a business should own a system rather than rent it.

Most of the integration work we do for clients follows the same shape. There is a system that holds something important, another system that needs it, and a gap between them that nobody planned for. The gap is usually filled by a person copying values between two screens.

This summer we had that problem on our own books, so we solved it on ourselves before writing about it. This is what we learned, including the parts that argue against doing what we did.

The situation

HDL Group bills for consulting time. That time is tracked, approved and priced inside our own platform. Every part of that chain belongs to us: the time entries, the approvals, the client record, the rates.

Everything except the invoice. That lived in Fortnox, and Fortnox worked. The bookkeeping was correct, the VAT returns were filed, nothing was on fire.

But the last step of a workflow we had built end to end was a step we could only reach through somebody else’s interface. When we wanted the invoice to carry data our platform already held, the options were to ask and wait, or to type it in twice.

We moved our books to an open-source accounting system running on our own server. Same double-entry engine, same Swedish statutory rules, same chart of accounts. The difference is who can change it.

The question worth asking before you do this

Not ”is the SaaS good enough”. It usually is. The question is narrower:

Is this system part of your product, or beside it?

If invoicing is something the office does at month end, a good SaaS is the right answer and you should stop reading. The vendor runs the servers, patches the security holes, and files the regulatory updates when the rules change. That is worth paying for, and doing it yourself to save a subscription fee is a bad trade.

If invoicing is a step inside software you already run, the calculation changes. The system is already a component of your stack. You are just renting a component you cannot modify, and paying for that in workarounds rather than in cash.

For us, the deciding moment came about a week in. Our platform needed the seller details to put on an invoice: the legal name as printed, the organisation number, the VAT number, the address, the bank details. The accounting system held all of it. Its API let you write some of it and read none of it.

With a closed vendor, that thread ends. You file a request, you wait, and in the meantime somebody types an organisation number into a second system by hand. A transcription error there produces an invoice that is not legally compliant, and nobody notices for months.

Instead we wrote the missing endpoint. Designed, tested, reviewed, in production the same afternoon. The point is not that we are fast. The point is that the effort finally matched the size of the problem.

What actually goes wrong

Owning both ends does not remove integration bugs. It moves them, and the new ones are quieter. Three we hit, all of which generalise well past accounting.

The default that meant ”nobody chose”

Two settings decide how an invoice presents the seller: whether the company is registered for VAT, and whether it is approved for F-tax. Both are stored as simple yes/no values, and both have a default for a company that has not been through setup.

Inside the accounting system this is fine. A person walks through onboarding, sees the toggles, and understands that a switch they have not touched means nothing yet.

The moment a second system reads those values, ”not chosen yet” silently becomes ”decided”. A VAT-registered company that never opened the setting would read as not registered, and its invoices would go out without a VAT number. The other flag defaults the opposite way, so a company that never touched it would have a statutory claim about its tax status printed on its invoices on the strength of a default nobody selected.

Neither failure produces an error. Both produce a document that looks right.

We now copy neither value. Both are asked once, unanswered, on the other side. It has become a standing question in our integration work: could this value be here because nobody chose it?

The same fact in two places

The company name exists in two columns in that system’s database. Nothing keeps them equal, and only one of them is printed on invoices. An integration that seeded itself from the wrong one would put a subtly incorrect legal name on a statutory document, and every test would pass.

Every mature system has a few of these. They are invisible from the outside and obvious from the inside, which is an argument for reading the schema of anything you integrate deeply with.

Pointing at the wrong server

Our platform spent an afternoon reporting that a newly built endpoint did not exist. It did. The platform was calling the vendor’s hosted service rather than our own installation, because the connector shipped the hosted address as its default and nobody overrode it. The hosted service answered every request, returned plausible errors, and reported itself healthy.

That bug cannot happen with a SaaS, because there is only one address. Self-hosting invents it. If you take this route, put the base address somewhere it has to be set rather than defaulted.

The part most articles leave out

Self-hosting a business-critical system is not free, and the costs are not the licence fee you stop paying.

You take on the deploy, and it has to be careful: ours refuses to build if there are schema changes that have not been applied, because it will not alter a production ledger on its own initiative. You take on the database migrations, which means a human decides when the books’ structure changes and takes a backup first. You take on the backups themselves, and you only find out whether you have them by restoring one. You take on upgrades, and every local change you keep is work you redo each time.

You also take on the security surface. That cuts both ways. While building the integration we found a real vulnerability in the system’s invoice preview, which validated nothing and could produce a clean, correctly numbered invoice document for arbitrary content. We fixed it the same day and it is fixed for everyone who runs that software. Against a closed vendor, that finding is an email you send and then wait on.

If you do not have someone who will own those things on an ordinary Tuesday, the answer is the SaaS. This is not a decision to make because it sounds appealing.

Where we are now

Our books have been running on the new system for weeks. Ledger, supplier invoices, fixed assets with depreciation, VAT returns. That part is finished and completely unremarkable, which is the highest praise a bookkeeping migration can earn.

The integration is half built. The accounting side is live. Our platform can create an invoice, own the document it sends, and hand the ledger everything it needs to book and archive it. What is missing is the renderer on our own side, so Fortnox still produces the invoices our clients actually receive while we finish it.

We are publishing this from the middle of the work rather than after a launch, because the middle is where the useful parts are. The decision was not hard. The three bugs above were, and none of them were in the code we set out to write.

If you are weighing the same decision, the shortest version of what we would say: rent the system if it sits beside your product, own it if it sits inside it, and either way go and read its schema before you trust what its API hands you.

We build and integrate systems for companies with this shape of problem. If it sounds like yours, get in touch.

;