ZUGFeRD-Rechnungen aus Excel erstellen: VBA-Makro mit API-Anbindung
Schritt-für-Schritt-Anleitung: Excel-Rechnungen per VBA-Makro an die Vellonode API senden und als ZUGFeRD 2.4 PDF zurückbekommen. Funktioniert auf Windows und macOS.
Vellonode Team
Redaktion
Warum Excel-Rechnungen ein E-Rechnungs-Problem haben
Viele kleine Unternehmen, Freelancer und Handwerksbetriebe erstellen ihre Rechnungen in Excel. Das funktioniert — bis der Kunde eine E-Rechnung im ZUGFeRD- oder XRechnung-Format verlangt. Spätestens seit der E-Rechnungspflicht für B2B ab 2025 wird das zum echten Problem.
Die naheliegende Lösung wäre, auf eine Rechnungssoftware umzusteigen. Aber wer mit Excel-Vorlagen arbeitet, hat oft gute Gründe dafür: individuelle Kalkulation, branchenspezifische Layouts oder schlicht die Gewohnheit eines eingespielten Workflows.
Die bessere Lösung: Excel behalten, E-Rechnung per API nachrüsten. Ein VBA-Makro exportiert die Rechnung als PDF und schickt sie an die Vellonode API. Zurück kommt ein validiertes ZUGFeRD 2.4 PDF — ohne Systemwechsel.
So funktioniert die Anbindung
Der Ablauf ist in drei Schritte aufgeteilt:
- Excel exportiert die Rechnung als PDF (über die eingebaute PDF-Export-Funktion)
- Ein VBA-Makro sendet das PDF an die API (per curl — auf Windows und macOS verfügbar)
- Die API liefert ein ZUGFeRD-PDF zurück (automatisch im Downloads-Ordner gespeichert)
Voraussetzungen
- Ein Vellonode-Konto (kostenlos registrieren unter vellonode.de/auth/register)
- Einen API-Key (unter Einstellungen → API im Dashboard erstellen)
- Ihre Organization-ID (ebenfalls unter Einstellungen → API sichtbar, mit Kopier-Button)
- Excel mit VBA-Unterstützung (Excel 2016 oder neuer, Windows und macOS)
- Windows 10 oder neuer (curl ist vorinstalliert) bzw. macOS (curl ist vorinstalliert)
Windows 7/8/8.1? Das Makro benötigt curl, das erst ab Windows 10 vorinstalliert ist. Auf älteren Versionen können Sie curl manuell nachrüsten: Laden Sie die Windows-Version von curl.se/windows herunter und legen Sie die curl.exe in C:\Windows\System32. Danach funktioniert das Makro wie beschrieben.
VBA-Makro einrichten
- In Excel Alt + F11 drücken (öffnet den VBA-Editor)
- Im Projektfenster links: Rechtsklick auf das Arbeitsmappen-Projekt → Einfügen → Modul
- Den kompletten VBA-Code (siehe unten) in das neue Modul einfügen
apiKeyundorgIdmit Ihren echten Werten ersetzen- Datei als
.xlsm(Makro-aktivierte Arbeitsmappe) speichern
Für einen Button: Entwicklertools → Einfügen → Schaltfläche → auf dem Blatt aufziehen → ExportAndConvert als Makro zuweisen. Falls der Reiter „Entwicklertools" fehlt: Datei → Optionen → Menüband anpassen → Entwicklertools aktivieren.
Kompletter VBA-Code
Der Code funktioniert auf Windows und macOS. Auf beiden Plattformen wird curl verwendet — die plattformspezifischen Unterschiede (Pfade, Shell-Aufrufe) werden automatisch über #If Mac Then behandelt.
Kopieren Sie den gesamten Code in ein einzelnes VBA-Modul:
Sub ExportAndConvert()
Dim pdfPath As String
#If Mac Then
pdfPath = Environ("HOME") & "/rechnung_export.pdf"
#Else
pdfPath = Environ("TEMP") & "\rechnung_export.pdf"
#End If
If Application.CountA(ActiveSheet.UsedRange) = 0 Then
MsgBox "Das aktive Blatt ist leer."
Exit Sub
End If
On Error Resume Next
Kill pdfPath
On Error GoTo 0
ActiveSheet.ExportAsFixedFormat _
Type:=xlTypePDF, _
Filename:=pdfPath, _
Quality:=xlQualityStandard, _
OpenAfterPublish:=False
If Dir(pdfPath) = "" Then
MsgBox "PDF konnte nicht erzeugt werden. " & _
"Bitte Druckbereich setzen unter " & _
"Seitenlayout > Druckbereich."
Exit Sub
End If
Call SendToVellonodeAPI(pdfPath)
End Sub
Sub SendToVellonodeAPI(pdfPath As String)
Dim apiKey As String
apiKey = "vk_live_IhrKeyHier" ' API-Key aus dem Dashboard
Dim orgId As String
orgId = "IHRE_ORG_ID" ' Organization-ID aus dem Dashboard
Dim apiUrl As String
apiUrl = "https://vellonode.de/api/v1/invoices/convert" _
& "?organizationId=" & orgId
Dim responsePath As String
Dim scriptPath As String
Dim dlFolder As String
#If Mac Then
responsePath = Environ("HOME") & "/rechnung_response.json"
scriptPath = Environ("HOME") & "/vellonode_upload.sh"
dlFolder = "/Users/" & Environ("USER") & "/Downloads/"
Open scriptPath For Output As #3
Print #3, "#!/bin/bash"
Print #3, "rm -f '" & responsePath & "'"
Print #3, "curl -s -o '" & responsePath & "' \"
Print #3, " -X POST \"
Print #3, " -H 'Authorization: Bearer " & apiKey & "' \"
Print #3, " -F 'file=@" & pdfPath & "' \"
Print #3, " -F 'format=zugferd,json' \"
Print #3, " '" & apiUrl & "'"
Close #3
Shell "/bin/bash " & scriptPath
#Else
responsePath = Environ("TEMP") & "\rechnung_response.json"
scriptPath = Environ("TEMP") & "\vellonode_upload.bat"
dlFolder = Environ("USERPROFILE") & "\Downloads\"
Open scriptPath For Output As #3
Print #3, "@echo off"
Print #3, "del /f """ & responsePath & """ 2>nul"
Print #3, "curl -s -o """ & responsePath & """ ^"
Print #3, " -X POST ^"
Print #3, " -H ""Authorization: Bearer " & apiKey & """ ^"
Print #3, " -F ""file=@" & pdfPath & """ ^"
Print #3, " -F ""format=zugferd,json"" ^"
Print #3, " """ & apiUrl & """"
Close #3
Shell "cmd /c """ & scriptPath & """", vbHide
#End If
' Warten bis curl fertig ist (max 60 Sekunden)
Dim waitCount As Integer
waitCount = 0
Do While waitCount < 60
Application.Wait Now + TimeValue("00:00:01")
waitCount = waitCount + 1
If Dir(responsePath) <> "" Then
If FileLen(responsePath) > 0 Then Exit Do
End If
Loop
If Dir(responsePath) = "" Or FileLen(responsePath) = 0 Then
MsgBox "Zeitueberschreitung - keine Antwort vom Server." _
& vbCrLf & "Bitte Internetverbindung und " _
& "API-Key pruefen."
Exit Sub
End If
' JSON lesen
Dim jsonData() As Byte
Open responsePath For Binary As #2
ReDim jsonData(LOF(2) - 1)
Get #2, , jsonData
Close #2
Dim jsonStr As String
jsonStr = StrConv(jsonData, vbUnicode)
' ZUGFeRD-URL extrahieren (PDF bevorzugt, Fallback auf XML)
Dim fileStart As Long
Dim zugferdExt As String
fileStart = InStr(jsonStr, """zugferd_pdf""")
If fileStart > 0 Then
zugferdExt = ".pdf"
Else
fileStart = InStr(jsonStr, """zugferd_xml""")
zugferdExt = ".xml"
End If
If fileStart = 0 Then
MsgBox "Keine ZUGFeRD-Datei in der Antwort." _
& vbCrLf & "Details: " & responsePath
Exit Sub
End If
Dim urlStart As Long
urlStart = InStr(fileStart, jsonStr, """url"":""") + 7
Dim urlEnd As Long
urlEnd = InStr(urlStart, jsonStr, """")
Dim zugferdUrl As String
zugferdUrl = Mid(jsonStr, urlStart, urlEnd - urlStart)
' ZUGFeRD-Datei herunterladen
Dim tempDlPath As String
Dim dlScriptPath As String
#If Mac Then
tempDlPath = Environ("HOME") & "/rechnung_zugferd" _
& zugferdExt
dlScriptPath = Environ("HOME") & "/vellonode_download.sh"
Open dlScriptPath For Output As #3
Print #3, "#!/bin/bash"
Print #3, "curl -s -o '" & tempDlPath _
& "' '" & zugferdUrl & "'"
Close #3
Shell "/bin/bash " & dlScriptPath
#Else
tempDlPath = Environ("TEMP") & "\rechnung_zugferd" _
& zugferdExt
dlScriptPath = Environ("TEMP") & "\vellonode_download.bat"
Open dlScriptPath For Output As #3
Print #3, "@echo off"
Print #3, "curl -s -o """ & tempDlPath _
& """ """ & zugferdUrl & """"
Close #3
Shell "cmd /c """ & dlScriptPath & """", vbHide
#End If
' Warten bis Download fertig
waitCount = 0
Do While waitCount < 30
Application.Wait Now + TimeValue("00:00:01")
waitCount = waitCount + 1
If Dir(tempDlPath) <> "" Then
If FileLen(tempDlPath) > 0 Then Exit Do
End If
Loop
Dim savePath As String
savePath = dlFolder & "rechnung_zugferd" & zugferdExt
If Dir(tempDlPath) <> "" And FileLen(tempDlPath) > 0 Then
#If Mac Then
' Mac: per VBA aus Sandbox in Downloads kopieren
Dim dlData() As Byte
Open tempDlPath For Binary As #2
ReDim dlData(LOF(2) - 1)
Get #2, , dlData
Close #2
Open savePath For Binary As #2
Put #2, , dlData
Close #2
#Else
' Windows: binaer lesen und schreiben (FileCopy
' scheitert wenn curl die Datei noch sperrt)
On Error Resume Next
Kill savePath
On Error GoTo 0
Dim dlData() As Byte
Open tempDlPath For Binary Access Read As #2
ReDim dlData(LOF(2) - 1)
Get #2, , dlData
Close #2
Open savePath For Binary As #2
Put #2, , dlData
Close #2
#End If
MsgBox "ZUGFeRD-Datei gespeichert unter:" _
& vbCrLf & savePath
Else
MsgBox "Download fehlgeschlagen." & vbCrLf _
& "URL (24h gueltig):" & vbCrLf _
& zugferdUrl
End If
End Sub
Speichern Sie den API-Key niemals direkt im Makro-Code, wenn die Datei mit anderen geteilt wird. Besser: Den Key in einer separaten, geschützten Zelle oder einer Umgebungsvariable ablegen.
Das Ergebnis: Ein Button in Excel
Nach der Einrichtung haben Sie einen Button in Excel, der:
- Die aktuelle Rechnung als PDF exportiert
- Das PDF an die Vellonode API sendet
- Die fertige ZUGFeRD-Datei im Downloads-Ordner speichert
Der gesamte Vorgang dauert wenige Sekunden. Ihre Excel-Vorlage bleibt unverändert — die E-Rechnungsfähigkeit kommt durch die API.
Die API liefert bevorzugt ein ZUGFeRD-PDF zurück — ein PDF mit eingebetteter XML. Falls die PDF-Einbettung nicht möglich ist (z.B. bei unvollständigen Rechnungsdaten), wird automatisch die ZUGFeRD-XML heruntergeladen.
Hinweise für macOS
Auf macOS gelten einige Besonderheiten:
- Excel-Sandbox: Dateien werden zunächst im internen Excel-Verzeichnis erzeugt und dann per VBA in den Downloads-Ordner kopiert
- Falls macOS nach Ordnerzugriff fragt → Erlauben klicken
- Das temporäre PDF wird im Arbeitsverzeichnis von Excel erzeugt, nur die fertige ZUGFeRD-Datei landet im Downloads-Ordner
Typischer Einsatz in der Praxis
Handwerksbetrieb mit individueller Kalkulation: Ein Elektroinstallateur kalkuliert Angebote und Rechnungen in Excel mit eigenen Formeln für Materialzuschläge, Fahrtkosten und Stundensätze. Das Layout ist über Jahre gewachsen und funktioniert. Mit dem VBA-Makro erzeugt er aus jeder Rechnung ein ZUGFeRD-PDF — ohne eine einzige Zelle zu ändern.
Freelancer mit Rechnungsvorlage: Eine Designerin nutzt eine schlichte Excel-Vorlage mit Logo, Positionen und Bankverbindung. Ihr größter Kunde (eine Behörde) verlangt XRechnung. Statt auf teure Software umzusteigen, schickt sie die PDF-Rechnung per Makro an die API und bekommt die XRechnung-XML zurück.
Grenzen und Alternativen
Das VBA-Makro funktioniert zuverlässig für den typischen Anwendungsfall: einzelne Rechnungen manuell konvertieren. Für höhere Volumina oder automatisierte Abläufe gibt es bessere Wege:
- Power Automate / Microsoft Flow: Kann die API ohne VBA aufrufen, z.B. ausgelöst durch einen neuen Eintrag in einer SharePoint-Liste
- Python-Script: Für Batch-Konvertierung mehrerer Dateien — die API akzeptiert denselben POST-Request
- Direkte Integration: Wenn Sie ohnehin eine eigene Anwendung entwickeln, nutzen Sie die API direkt aus Ihrem Backend
Die vollständige API-Dokumentation finden Sie als OpenAPI 3.1 Spezifikation unter /api/v1/openapi. Dort sind alle Felder, Fehlercodes und Formate dokumentiert.