Automation · Accounting · API September 2026

From Manual Entry to API:
Automating Invoice Posting

A finance colleague was typing every outgoing invoice into our accounting system by hand. We already generated those invoices from a Google Sheet. This is how I closed the gap with the accounting system's API, and why the hardest parts were not the code.

The Starting Point

We had a Google Sheets tool that already did a lot. It read a Transactions tab, built a branded PDF invoice per partner, logged the result, and emailed the client. The one manual step left was the accounting system. Every invoice that left the building also had to be typed, line by line, into Bexio for bookkeeping. Same numbers, entered twice, once by a machine and once by a person.

That is exactly the kind of task worth automating: repetitive, rule based, and already sitting on top of clean structured data. The accounting system had a REST API. The invoicing tool was Google Apps Script. The two just were not talking.

The Obvious Plan, and Why I Changed It

My first instinct was a separate service: a small script that reads an export, maps it, and posts to the API on a schedule. It would have worked. But the invoicing logic already lived inside the Apps Script project, and the data was already in memory at the moment each invoice was built. A separate service would mean new infrastructure, a second copy of the mapping, and a sync problem to babysit.

So the integration became one more step in the pipeline that already existed. No new server, no cron job, no second source of truth. The posting call reads the same rows the PDF was built from and sends them straight to the API. Fewer moving parts is almost always the right answer.

Lesson Before you build a new system next to an old one, check whether the old one is already holding everything you need. The cheapest integration is the one that reuses data that is already in hand.

Mapping Is a Business Decision, Not a Coding One

Every line on an invoice has to land on the correct revenue account. Development work on one account, management fee on another, travel and hardware on a third, and each of those splits again by whether the partner is domestic or foreign. That mapping is not something an engineer should invent. It belongs to the person who does the books.

The accounting colleague built the full mapping in a spreadsheet. My job was to turn her rules into logic the API call could follow: group the lines by account, sum each group, and send one clean position per account with a fixed label. Names of individual people and free text descriptions never reach the accounting system. They stay on the client PDF, where they belong. The books get a tidy, auditable summary.

Idempotency, or How Not to Create the Same Invoice Twice

The scary part of any write integration is duplicates. Run the job twice and you do not want two copies of the same invoice in the books. I leaned on a reference field the API exposes for exactly this: each invoice carries its own number as a stable key. Before creating anything, the script asks the accounting system whether an invoice with that key already exists. If it does, it skips and moves on. The invoice log in the sheet also records the returned ID, so a second glance confirms what already went through.

This matters more than it sounds, because the log gets cleared and rebuilt every cycle. The only reliable guard against duplicates is one that lives on the accounting side, keyed to something that never changes.

Lesson Any integration that writes to a system of record needs an idempotency key the target system can see. A checkmark in your own sheet is not enough, because your sheet can be reset. The key has to live where the data lands.

The APIs Never Sit Where You Expect

A dull but very real part of the work: the accounting API mixes versions. Accounts sat on one path, taxes and currencies on another, and units were on a third path under a slightly different name. Nothing was wrong with the credentials. The endpoints simply lived where the vendor put them, not where the pattern suggested. A short diagnostic function that pings each lookup and prints what it finds saved a lot of guessing. When you meet a new API, build the map of what actually responds before you build anything on top of it.

The Lesson That Changed the Whole Design

I first wired the posting to happen automatically at the end of invoice generation. Generate the invoice, post it. Clean, in my head.

Then the person who actually does the work told me how she works. She generates an invoice, checks the amounts, fixes something, generates again, and repeats until it is right. Sometimes many times for one invoice. If posting rides on generation, the first half correct version lands in the books, and later fixes get skipped because the invoice already exists. That is worse than doing nothing.

Her point was simple and correct: the invoice should reach the accounting system at the moment she confirms it is right, not on every trial run. So the posting moved off generation and onto a separate action she triggers by hand, once, when the invoice is final. The tool now waits for the human, instead of racing ahead of them.

Lesson Automate the step, not the judgement. The person closest to the work usually knows where the trigger belongs better than the person writing the code. Ask them how they actually work before you decide when your automation fires.

Where the Mapping Should Really Live

One open thread is honest to leave open. Right now the rule that sends a line to the correct account still lives partly in code, matched on keywords. It works, but it is fragile: rename a service category and a line can silently miss its rule. And it hides the logic from the one person who should own it.

The next step is to lift that mapping out of the code and into the catalog the accounting colleague already maintains, as a column she fills in. New service, new row, no developer needed. The measure of a good internal tool is whether it keeps working after the person who built it has moved on. Until the mapping lives in the sheet, this one does not fully pass that test yet. That is the work still to do.

What Shipped

An invoice that used to be typed by hand now posts to the accounting system as a reviewed draft with one click, on the correct accounts, with the correct tax treatment, and with a guard against duplicates. Nothing is issued automatically. A person still confirms every one. The machine just stopped asking a human to copy numbers it already had.

The interesting part was never the API. It was learning where a human belongs in the loop, and building the tool around that instead of around my first idea of it.