Ich wollte meine Buchhaltung automatisieren, und zwar vollständig: Beleg kommt rein, Text wird erkannt, Konto wird gesetzt, Zahlung wird zugeordnet. Zwei Gesellschaften, zwei sevDesk-Mandanten, ein n8n-Server. Das klang nach einem überschaubaren Projekt. Heute läuft ungefähr die Hälfte davon automatisch, die andere Hälfte habe ich dagegen im August 2026 abgeschaltet. Sieben Workflows auf einen Schlag.
Der Grund war allerdings nicht, dass sie abgestürzt wären. Sie liefen. Sie meldeten monatelang „success“ und buchten dabei Belege an den falschen Monat.
Buchhaltung automatisieren heißt sechs verschiedene Probleme lösen
Der erste Denkfehler steckt schon im Wort. „Die Buchhaltung“ ist keine Aufgabe, sondern eine Kette von sechs, die nacheinander laufen und sehr unterschiedlich schwer sind. Wer das nicht trennt, baut einen Workflow, der an einer Stelle brilliert und an einer anderen still Schaden anrichtet.
Die sechs Stufen, in der Reihenfolge, in der ein Beleg sie durchläuft:
- Stufe 1, Belegbeschaffung: Die Rechnung muss überhaupt erst ins System. Per Scraper vom Rechnungsportal, per Weiterleitung aus dem Postfach, aus einem OneDrive-Ordner oder vom Dokumentenscanner neben dem Schreibtisch.
- Stufe 2, Texterkennung: Aus dem PDF müssen Lieferant, Rechnungsnummer, Datum sowie Betrag herausgelesen werden.
- Stufe 3, Kontierung: Der Beleg braucht ein Buchungskonto. Hier fängt das Steuerrecht an, und hier hört die Mustererkennung auf zu genügen.
- Stufe 4, Umsatzsteuer-Voranmeldung: Monatlich, mit eigener Belegpflicht, eigenem Konto sowie eigener Zahlung ans Finanzamt.
- Stufe 5, Zuordnung: Jeder Beleg muss an die passende Kontobewegung. Das klingt trivial, ist jedoch die gefährlichste Stufe überhaupt.
- Stufe 6, Kontrolle: Am Ende muss jemand sehen, wo eine Rechnung fehlt.
Stufe 1 und 2 sind reine Technik. Ab Stufe 3 geht es dagegen um Entscheidungen, die falsch sein können, ohne dass ein Fehler geworfen wird. Genau an dieser Grenze ist mein deterministischer Ansatz zerbrochen.
Was mit n8n hervorragend funktioniert hat
Die Belegbeschaffung läuft bis heute in n8n, und sie läuft gut. Ein eigener Playwright-Scraper zieht zweimal täglich die Rechnungen aus dem Rechnungsportal und schickt sie per Webhook weiter. Parallel dazu holt ein Workflow Rechnungen aus dem Outlook-Postfach, ein zweiter überwacht einen OneDrive-Ordner, ein dritter nimmt außerdem die Tankquittungen entgegen, die ich in den Dokumentenscanner lege. Insgesamt acht aktive Workflows, die nichts anderes tun, als Dokumente an die richtige Stelle zu transportieren.
Das ist der ideale Anwendungsfall für ein deterministisches Werkzeug, denn der Input ist ein PDF, der Output ein Beleg-Entwurf, und dazwischen liegt keine Beurteilung. Falls dieser Teil scheitert, scheitert er sichtbar: Es kommt nichts an.
Übrigens scheiterte er trotzdem einmal sehr unsichtbar, und das war die erste echte Warnung. Der Scraper meldete sich sieben Wochen lang als „healthy“, loggte brav alle fünf Minuten einen erfolgreichen Login und holte null Rechnungen. Das Portal war nämlich auf eine neue URL umgezogen. Der Healthcheck prüfte jedoch den HTTP-Server, nicht den Abruf. Erst ein zweiter Wächter, der schlicht das Datum des letzten gesendeten Belegs prüft, hat es aufgedeckt.
Ab Stufe 3 wurde es teuer
Die Beleg-Erkennung war noch machbar. Ein OCR-Dienst liest das PDF, ein Code-Node vergleicht den Lieferantennamen gegen eine Regelliste und überschreibt den vorgeschlagenen Kontovorschlag. Diese Liste ist über Monate gewachsen, denn die Texterkennung rät bei jedem zweiten Beleg daneben. Der Steuerberater landete zum Beispiel auf „Innungsbeiträge“, ein Autohaus auf „Fahrzeugkosten“ statt auf „Inspektion und Reparatur“.
Richtig kritisch wurde dagegen die Zuordnung. Mein Match-Workflow suchte zu jeder offenen Kontobewegung den passenden Beleg über Betrag und Datumsnähe, ergänzt um die Ähnlichkeit der Namen. Bei wiederkehrenden Beträgen ist das allerdings eine Falle: Wenn derselbe Anbieter jeden Monat zehn Euro abbucht, passen fünf Belege gleich gut, und der Workflow nahm regelmäßig den ältesten.
Dazu kam ein Fehler in der sevDesk-Schnittstelle selbst. Der Filter für unverbuchte Transaktionen liefert nämlich auch bereits gebuchte zurück. Mein Workflow hielt deshalb eine längst erledigte Transaktion aus dem Vormonat für offen und hängte den neuen Beleg zusätzlich daran. Ergebnis: zwei Belege an einer Zahlung, der Betrag zudem doppelt in der Voranmeldung.
Der Fehler, der mich am meisten gekostet hat
Bei einem Anbieter für Sachbezugskarten war ein einziger Beleg fünfmal angelegt worden, verteilt über zwei Tage. Nur einer davon war der Zahlung zugeordnet, die vier übrigen standen dagegen offen herum und zogen jeweils gut zwei Euro Vorsteuer. Das ist wenig Geld. Es ist allerdings eine falsche Voranmeldung, und die ist keine Kleinigkeit.
Die Ursache war keine Schlamperei im Code, sondern ein Rennen zwischen zwei Workflows. Ein Workflow legte leere Entwürfe an, ein zweiter befüllte sie per Texterkennung. Legte der erste zweimal an, befüllte der zweite zweimal. Der vorhandene Duplikatschutz prüfte jedoch die interne Beleg-ID, also genau das eine Merkmal, das bei zwei Entwürfen desselben PDFs garantiert unterschiedlich ist.
Die Voranmeldung, die es nie gab
Am deutlichsten wurde das Grundproblem bei der Umsatzsteuer. Ich hatte zwei Workflows gebaut, die monatlich die Voranmeldung aus sevDesk ziehen, einen Beleg daraus erzeugen und ihn schließlich gegen die Zahlung ans Finanzamt buchen sollten. Beide liefen täglich. Beide meldeten „success“.
Als ich im Juni nachsah, stellte sich jedoch heraus: Sie hatten seit März keinen einzigen Beleg erzeugt. Die Deduplizierungs-Tabelle enthielt nach wie vor nur die Startzeile vom Tag der Einrichtung. Der Grund war eine einzelne falsch verbundene Kante im Workflow. Der Verarbeitungsschritt hing nämlich am „fertig“-Ausgang der Schleife statt am „Schleifendurchlauf“-Ausgang. Der Lauf endete nach einer Sekunde, ohne irgendetwas zu tun, und meldete Erfolg.
Ich habe die Kante repariert. Danach lief der Workflow bis zum Upload und brach dort ab, denn die n8n-Instanz hatte das benötigte Node-Modul für den Datei-Upload gesperrt. Immerhin stand jetzt „error“ statt „success“ im Protokoll. Ein Beleg entstand dennoch nicht.
Vier Monate Voranmeldungen habe ich in dieser Zeit von Hand gebucht, ohne es zu wissen, während im Hintergrund täglich zwei grüne Läufe protokolliert wurden.
Warum deterministisch hier die falsche Wahl war
Ein Workflow-Werkzeug wie n8n ist stark, falls die Aufgabe eine feste Form hat. Es kennt jedoch nur zwei Zustände: Der Knoten hat einen Fehler geworfen oder eben nicht. Genau dazwischen liegt allerdings die gesamte Buchhaltung.
Ein Beleg am falschen Monat wirft keinen Fehler. Ein Beleg mit 19 Prozent Vorsteuer, der eigentlich nach dem Reverse-Charge-Verfahren gebucht gehört, wirft ebenfalls keinen Fehler. Eine Zuordnung an die Zahlung eines fremden Vorgangs wirft schließlich auch keinen. Alle drei Fälle sehen im Protokoll identisch aus mit einer korrekten Buchung, und alle drei fallen daher erst bei der Jahresabschlussprüfung auf oder gar nicht.
Dazu kommt der Wartungsaufwand. Jede neue Regel musste ich zudem in beiden Mandanten synchron einbauen, denn beide Gesellschaften hatten eigene, aber baugleiche Workflows. Einmal lag die Sonderlogik für Sachbezugskarten ausgerechnet nur im Workflow der Gesellschaft, die diesen Lieferanten gar nicht hat. Der Beleg wurde deshalb monatelang falsch gebucht, in zwei aufeinanderfolgenden Monaten identisch falsch.
Der Umbau: ein Skill statt sieben Workflows
Im August 2026 habe ich die sieben Workflows deaktiviert, die Entscheidungen trafen. Geblieben sind dagegen die acht, die nur Dokumente transportieren. An die Stelle der Automatik trat ein Claude-Code-Skill, also eine Sammlung aus Anleitung und Skripten, die ich als Monatslauf starte.
Wer seine Buchhaltung automatisieren will, braucht dafür vor allem einen Haltepunkt. Das Prinzip steht seitdem in einem Satz: erst zeigen, dann fragen, dann schreiben. Jedes Skript trennt sauber zwischen Prüfen und Ausführen. Der Prüflauf liest ausschließlich, und der Schreiblauf bekommt folglich eine bestätigte Liste übergeben, statt sich selbst etwas auszusuchen. Nach jedem Schreibvorgang wird zudem das Ergebnis nachgelesen.
Entscheidend ist, wo die Entscheidung fällt. Die Skripte schlagen vor, ich bestätige. Dabei sehe ich jeden Vorschlag mit seiner Begründung, mit den Alternativen und vor allem mit den Warnungen, auf die er sich stützt.
Was das im ersten echten Monatslauf gefangen hat
Ein Tankbeleg aus Schweden wurde als 1.336,82 Euro bei 25 Prozent Steuersatz erkannt. Die Bankbewegung zwei Tage später lautete dagegen auf 122,45 Euro, denn die Rechnung war in schwedischen Kronen ausgestellt. Hätte ich das durchgewunken, wären folglich rund 1.214 Euro Aufwand zu viel und etwa 267 Euro Vorsteuer entstanden, die es gar nicht gibt.
Aufgefallen ist es nur, weil die Beträge gegen die Kontoauszüge gehalten wurden. Daraus ist deshalb eine feste Prüfung geworden: Jeder erkannte Betrag wird gegen die offenen Bankbewegungen desselben Empfängers geprüft. Ein Steuersatz, den es in Deutschland nicht gibt, gilt seitdem als sicherstes Erkennungszeichen für einen Auslandsbeleg.
Im selben Lauf standen außerdem zwei leere Entwürfe zur Lohnabrechnung, aus denen die Erkennung jeweils das Gesamtbrutto mit 19 Prozent Vorsteuer lesen wollte. Auf Gehalt gibt es jedoch keine Vorsteuer. Zudem entstehen aus dieser einen Abrechnung zwei Belege, einer für die Auszahlung und einer für die Lohnsteuer, die erst Mitte des Folgemonats abgebucht wird. Die Erkennung überspringt solche Belege heute und meldet nur das Muster.
Fünf Regeln, die ich beim nächsten Mal von Anfang an anwenden würde
Aus dem gesamten Projekt sind schließlich fünf Regeln übrig geblieben, die sich auf jedes Automatisierungsvorhaben übertragen lassen, das mit Geld oder Recht zu tun hat.
- Ein grüner Status sagt nur, dass nichts abgestürzt ist. Er sagt dagegen nichts darüber, ob etwas Richtiges passiert ist. Jede Automatisierung, die schreibt, braucht daher eine Nachprüfung des geschriebenen Zustands, nicht des Rückgabewerts.
- Trenne Prüfen und Schreiben auf Ebene des Programms. Solange derselbe Lauf auswählt und ausführt, gibt es nämlich keinen Punkt, an dem ein Mensch eingreifen kann, ohne alles anzuhalten.
- Der Zustellweg einer Benachrichtigung gehört zur Funktion. Mein Monatscheck bricht deshalb heute mit Fehlercode ab, falls die Meldung nicht rausgeht. Ein Wecker, der nicht klingelt, sieht sonst genauso aus wie „nichts zu tun“.
- Automatisiere das Transportieren, nicht das Entscheiden. Alles, was Belege einsammelt und ablegt, darf unbeaufsichtigt laufen. Alles, was ein Konto wählt oder eine Zahlung zuordnet, braucht dagegen eine Bestätigung.
- Ein zweimal beobachtetes Verhalten ist keine Regel. Ich hatte aus zwei vorgefundenen Gutschriften abgeleitet, dass Erstattungen keine Reverse-Charge-Positionen brauchen. Beide Belege waren allerdings schlicht unvollständig. Bei steuerlichen Mustern zählt folglich die Fachlogik, nicht die Häufigkeit.
Die vierte Regel ist die wichtigste, und sie widerspricht dem, was in den meisten Automatisierungs-Präsentationen steht. Der Reiz einer Workflow-Plattform liegt nämlich darin, dass sie Ende zu Ende läuft. Genau das ist bei der Buchhaltung jedoch der Fehler.
Buchhaltung automatisieren: Wo ich heute stehe
Der Monatslauf dauert rund eine Stunde, verteilt auf vier Schritte. Am Ersten jedes Monats um 7:30 Uhr schickt mir zudem ein Cronjob eine Push-Nachricht mit den offenen Punkten. Gebucht wird nichts, denn die Meldung ist nur der Anstoß.
Ehrlicherweise heißt Buchhaltung automatisieren bei mir heute weniger Automatik als geplant. Es ist allerdings das erste Mal, dass ich den Büchern traue. Der Skill führt außerdem ein Erfahrungs-Log, in dem nach jedem Lauf Symptom, Ursache, Fix und die allgemeine Lehre stehen. Ein abgelehnter Vorschlag gilt dort ausdrücklich als Lernsignal, denn er zeigt, wo die Heuristik danebenlag, bevor daraus eine Fehlbuchung wurde.
Ob ich das irgendwann wieder auf vollautomatisch stelle, weiß ich nicht. Die drei Fehler, die ich beim Bau des Skills gefunden habe, wären unbeaufsichtigt jedenfalls alle durchgelaufen.
Technischer Anhang: die konkreten Fallen
Dieser Teil ist für alle, die selbst gerade ihre Buchhaltung automatisieren und mit einer Buchhaltungs-API arbeiten. Die folgenden Punkte haben mich jeweils mehrere Stunden gekostet.
Schnittstellen-Fallen bei sevDesk
- Der Filter für unverbuchte Transaktionen ist unzuverlässig und liefert auch bereits gebuchte zurück. Zuverlässig ist dagegen ausschließlich die Abfrage über den Status.
- Das Vorzeichen beim Buchen folgt der Bank. Der übergebene Betrag entspricht dem vorzeichenbehafteten Betrag der Kontobewegung. Bei einem Aufwand ist er folglich negativ, sonst zeigt die Oberfläche den doppelten offenen Betrag an, während die Schnittstelle „null offen“ meldet.
- Bei Einnahmebelegen bleibt die Transaktion nach dem Buchen auf „zugeordnet“ statt auf „gebucht“. Der Status muss dementsprechend anschließend selbst gesetzt werden.
- Fremdwährung ist über die Schnittstelle nicht reparierbar. Steht am Beleg eine andere Währung als Euro, rechnet sevDesk die Euro-Seite bei jedem Schreibvorgang erneut um, und zwar aus dem bereits umgerechneten Wert. Ein Beleg schrumpfte mir deshalb von 3,33 über 2,92 auf 2,24 Euro. Der Ausweg ist, den Beleg zunächst auf Euro umzustellen.
- Die Sortierung von Listen ist je Objekttyp verschieden. Belege kommen neueste zuerst, Voranmeldungen dagegen älteste zuerst, und der Sortierparameter wird schlicht ignoriert. Wer „die letzten N“ braucht, sortiert also lokal.
- Ob ein Konto anlagefähig ist, steht im Datenmodell, nicht im Namen. Ein Konto namens „Kleingeräte“ trug bei mir das Anlagekennzeichen, ein Konto namens „Büromaschinen, EDV-Anlagen“ dagegen nicht.
Fallen in n8n selbst
- Multipart-Uploads niemals von Hand im Code-Node zusammenbauen. Die manuelle Konstruktion der Boundaries hat bei mir monatelang funktioniert und dann nicht mehr. Der native HTTP-Node erledigt es dagegen zuverlässig.
- Externe Node-Module sind auf vielen Instanzen gesperrt. Falls die Instanz nur eine Whitelist erlaubt, hilft folglich nur die eingebaute Variante aus der Laufzeitumgebung.
- Der Loop-Node hat zwei Ausgänge, die leicht zu verwechseln sind. Hängt die Verarbeitung am „fertig“-Ausgang, endet der Lauf sofort und meldet dennoch Erfolg. Erkennungsmerkmal ist zudem eine Laufzeit von unter einer Sekunde bei erwarteter Mehrfachverarbeitung.
- Fehlende Umgebungsvariablen im Container werden nicht durchgereicht, auch wenn sie in der Konfigurationsdatei stehen. Sie müssen daher im Compose-File explizit gelistet sein.
- Ein Healthcheck auf den HTTP-Server beweist nichts über die Fachfunktion. Mein Scraper war sieben Wochen „healthy“ und holte null Belege. Die brauchbare Prüfung war schließlich das Alter des zuletzt verarbeiteten Datensatzes.
Und die Fachlogik
Zum Schluss zwei fachliche Punkte, die keine Technik sind und dennoch im Code landen mussten. Erstens trägt ein Beleg drei verschiedene Datumsfelder: Belegdatum, Leistungsdatum, Zahlungsdatum. Für die Zuordnung zur Voranmeldungsperiode zählt dabei das Leistungsdatum. Bei einem Gehalt, das im April für März gezahlt wird, landet der Beleg sonst in der falschen



