KI & Automatisierung5 Min. Lesezeit

DMS-Integration per API: PDF-Rechnungen automatisch strukturiert archivieren

Wie DMS- und Archiv-Software die Vellonode API nutzt, um eingehende PDF-Rechnungen automatisch zu indexieren und GoBD-konform abzulegen. Mit Code-Beispiel und Architekturüberblick.

VT

Vellonode Team

Redaktion

|

Das Problem: PDF-Rechnungen im DMS ohne Struktur

Dokumentenmanagementsysteme sind dafür gebaut, Dokumente zu speichern, zu versionieren und wiederzufinden. Aber ein PDF allein enthält keine maschinenlesbaren Metadaten. Wer eine Eingangsrechnung im DMS ablegen will, muss die Rechnungsdaten manuell erfassen: Rechnungsnummer, Lieferant, Betrag, Datum — alles von Hand eintippen, damit das Dokument später auffindbar ist.

Bei kleinen Belegmengen ist das vertretbar. Bei hunderten Eingangsrechnungen pro Monat wird es zum Flaschenhals. Und die manuelle Erfassung bringt Fehlerquellen: vertippte Rechnungsnummern, falsche Beträge, inkonsistente Lieferantennamen.

Die Lösung: PDF hochladen, JSON zurückbekommen

Die Vellonode API extrahiert aus jedem PDF sämtliche Rechnungsdaten als strukturiertes JSON. Dieses JSON kann direkt als Metadaten-Set für das DMS verwendet werden — ohne manuellen Zwischenschritt.

Was die API liefert

Ein einzelner POST-Request mit dem PDF liefert:

  • Rechnungsnummer, Datum, Fälligkeitsdatum
  • Verkäufer und Käufer mit Name, USt-ID und Adresse
  • Alle Positionen mit Beschreibung, Menge, Einzelpreis, Steuersatz
  • Summen (Netto, USt, Brutto) und Steuer-Aufschlüsselung
  • Validierungsergebnis gegen EN16931

Das JSON hat eine feste Struktur — ideal für die automatische Indexierung.

Architektur: DMS + API

Eingangskanal (E-Mail, Scanner, Upload)
         │
         ▼
   PDF im Eingangsordner
         │
         ▼
   API-Aufruf: POST /api/v1/invoices/convert?organizationId=IHRE_ORG_ID
         │
         ▼
   JSON-Response mit Rechnungsdaten
         │
         ├──▶ DMS: Dokument + Metadaten speichern
         ├──▶ Index: Volltextsuche aktualisieren
         └──▶ Optional: ZUGFeRD-XML archivieren

Integration in den DMS-Workflow

Die meisten DMS-Systeme bieten einen der folgenden Integrationswege:

1. Überwachter Import-Ordner: Das DMS überwacht einen Ordner. Ein Script legt dort das PDF plus eine Metadaten-Datei (XML oder JSON) ab. Das DMS importiert beides automatisch.

2. REST-API des DMS: Viele moderne DMS-Systeme (DocuWare, d.velop, ELO, Amagno) bieten eigene APIs. Das Integrations-Script ruft erst die Vellonode API auf, dann die DMS-API.

3. Webhook/Event-basiert: Das DMS löst bei einem neuen Dokument einen Webhook aus. Ein Microservice fängt den Webhook ab, ruft die Vellonode API auf und schreibt die Metadaten zurück.

Code-Beispiel: Python-Integration

Das folgende Script überwacht einen Eingangsordner, extrahiert Rechnungsdaten per API und legt das Ergebnis strukturiert ab:

import os
import json
import time
import shutil
import requests
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

# organizationId ist ein Pflichtparameter – abrufbar über GET /api/v1/organizations
API_URL = "https://vellonode.de/api/v1/invoices/convert?organizationId=IHRE_ORG_ID"
API_KEY = "vk_live_IhrKeyHier"

INPUT_DIR = Path("./eingang")
ARCHIVE_DIR = Path("./archiv")


class InvoiceHandler(FileSystemEventHandler):
    def on_created(self, event):
        if not event.is_directory and \
           event.src_path.lower().endswith(".pdf"):
            time.sleep(1)  # Datei vollständig geschrieben
            self.process(Path(event.src_path))

    def process(self, pdf_path):
        print(f"Verarbeite: {pdf_path.name}")

        with open(pdf_path, "rb") as f:
            response = requests.post(
                API_URL,
                headers={
                    "Authorization": f"Bearer {API_KEY}"
                },
                files={
                    "file": (
                        pdf_path.name,
                        f,
                        "application/pdf",
                    )
                },
                data={"format": "json"},
            )

        if response.status_code != 200:
            print(f"  Fehler: {response.status_code}")
            return

        result = response.json()
        data = result["data"]

        # Ordnerstruktur: archiv/2026/06/Lieferant/
        seller = (data.get("seller") or {}).get("name")
        seller_safe = "".join(
            c if c.isalnum() or c in " -_" else "_"
            for c in (seller or "Unbekannt")
        ).strip()
        invoice_date = data.get("invoiceDate", "")
        year = invoice_date[:4] if invoice_date else "0000"
        month = invoice_date[5:7] if len(
            invoice_date
        ) >= 7 else "00"

        target_dir = ARCHIVE_DIR / year / month / seller_safe
        target_dir.mkdir(parents=True, exist_ok=True)

        # PDF kopieren
        invoice_nr = data.get("invoiceNumber", "unbekannt")
        safe_nr = "".join(
            c if c.isalnum() or c in "-_" else "_"
            for c in invoice_nr
        )
        target_pdf = target_dir / f"{safe_nr}.pdf"
        shutil.copy2(pdf_path, target_pdf)

        # Metadaten als JSON daneben
        meta_path = target_dir / f"{safe_nr}.meta.json"
        metadata = {
            "invoiceNumber": invoice_nr,
            "invoiceDate": invoice_date,
            "seller": data.get("seller"),
            "buyer": data.get("buyer"),
            "totals": data.get("totals"),
            "currency": data.get("currency"),
            "lines": len(data.get("lines", [])),
            "validation": result.get("validation"),
            "sourceFile": pdf_path.name,
            "processedAt": time.strftime("%Y-%m-%dT%H:%M:%S"),
        }
        with open(meta_path, "w", encoding="utf-8") as mf:
            json.dump(metadata, mf,
                      ensure_ascii=False, indent=2)

        # Original entfernen
        pdf_path.unlink()

        gross = (data.get("totals") or {}).get("grossTotal")
        print(f"  Archiviert: {invoice_nr} "
              f"({seller}, {gross} EUR)")


if __name__ == "__main__":
    INPUT_DIR.mkdir(exist_ok=True)
    ARCHIVE_DIR.mkdir(exist_ok=True)

    observer = Observer()
    observer.schedule(
        InvoiceHandler(), str(INPUT_DIR), recursive=False
    )
    observer.start()
    print(f"Überwache {INPUT_DIR}...")

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

Das Script nutzt die watchdog-Bibliothek für die Ordnerüberwachung. Installation: pip install watchdog requests. Für den Produktivbetrieb empfiehlt sich ein systemd-Service oder Docker-Container.

Praxisbeispiel: Mittelständischer Großhandel mit 800 Eingangsrechnungen pro Monat

Ein Elektrogroßhändler mit 12 Standorten erhält monatlich rund 800 Eingangsrechnungen von 200 verschiedenen Lieferanten. Die Rechnungen kommen per E-Mail, über EDI-Schnittstellen und teilweise noch per Post (eingescannt).

Ausgangslage:

  • DMS: d.velop documents (on-premise)
  • Rechnungen wurden manuell indexiert: Lieferant, Rechnungsnummer, Betrag, Datum
  • 2 Mitarbeiter verbrachten zusammen 40 Stunden pro Monat mit der Indexierung
  • Suchfunktion im DMS nur über manuell eingegebene Felder

Nach der API-Integration:

  • Ein Python-Service überwacht den Import-Ordner des DMS
  • Jedes neue PDF wird an die Vellonode API gesendet
  • Die extrahierten Metadaten werden als d.velop-Indexdaten zurückgeschrieben
  • Zusätzlich werden alle Positionsdetails in einer Elasticsearch-Instanz indexiert

Ergebnis:

  • Indexierungszeit: von 40 auf 4 Stunden pro Monat (nur noch Prüfung gelb/rot markierter Felder)
  • Neue Suchfähigkeit: Volltextsuche über Positionen, Artikelnummern, Beträge
  • Automatische Lieferantenerkennung über USt-ID statt manueller Zuordnung

Metadaten-Mapping für gängige DMS-Systeme

JSON-FeldDocuWared.velopELOAmagno
invoiceNumberRECHNUNGSNRBelegnummerRechnungsnummerinvoice_number
invoiceDateRECHNUNGSDATUMBelegdatumRechnungsdatuminvoice_date
seller.nameLIEFERANTAbsenderKontaktsender
seller.vatIdUST_IDSteuernummerUSt-IdNrvat_id
totals.grossTotalBETRAGBruttobetragBetragamount
currencyWAEHRUNGWährungWährungcurrency

Die JSON-Feldnamen der API sind stabil — das Mapping muss nur einmal eingerichtet werden.

Neben dem strukturierten JSON kann die API auch ZUGFeRD-XML erzeugen. Für die Langzeitarchivierung nach GoBD ist das ZUGFeRD-PDF (PDF/A-3 mit eingebettetem XML) das ideale Format — maschinenlesbar und gleichzeitig visuell prüfbar.

Nächste Schritte

  1. Konto erstellen unter vellonode.de/auth/register
  2. API-Key generieren unter Einstellungen → API
  3. Testlauf mit 10 echten Eingangsrechnungen
  4. Metadaten-Mapping für Ihr DMS definieren
  5. Integration in den produktiven Import-Workflow

Die API-Dokumentation finden Sie unter /api/v1/openapi.

Passende Lösungen

Weitere Artikel

Bleiben Sie informiert

Erhalten Sie die neuesten Artikel zu E-Rechnung, Buchhaltung und KI-Automatisierung direkt in Ihr Postfach.

Kein Spam, jederzeit abbestellbar. Datenschutzerklärung