Eine ZUGFeRD-Rechnung ist ein PDF/A-3b mit eingebettetem CII-XML nach EN 16931. Drei Dinge müssen stimmen: die PDF/A-3b-Konformität, die eingebettete Datei mit normiertem Namen (aktuell factur-x.xml) samt AFRelationship und das XMP-Extension-Schema mit dem Profil. Per API holst du die fertige Datei mit einem Call: GET /invoices/{id}/pdf.
Ein ZUGFeRD-Dokument sieht aus wie ein PDF — technisch ist es ein PDF/A-3b mit einer eingebetteten XML-Datei. Genau diese Einbettung ist der Teil, an dem selbstgebaute Lösungen scheitern: Die Bytes müssen an der richtigen Stelle stehen, richtig benannt und richtig zugeordnet sein, sonst lehnt der Empfänger die Datei ab. Dieser Beitrag zeigt, wie das XML ins PDF kommt — und wie du dir den Embedder sparst.
Was „ZUGFeRD-PDF/A-3“ konkret bedeutet
ZUGFeRD (bzw. Factur-X) ist ein hybrides Format: ein für Menschen lesbares PDF plus ein für Maschinen lesbares CII-XML nach EN 16931, beides in einer Datei. Damit das funktioniert, sind drei Dinge gleichzeitig wahr:
- Die Hülle ist ein PDF/A-3b. PDF/A ist das Archivprofil von PDF (eingebettete Schriften, definierter Farbraum, keine externen Abhängigkeiten). Nur die Version 3 erlaubt beliebige eingebettete Dateien — die Vorgänger PDF/A-1 und A-2 nicht. Die Stufe b (basic) verlangt visuelle Reproduzierbarkeit, nicht die volle Tag-Struktur von Stufe a.
- Das CII-XML liegt als eingebettete Datei drin. Nicht als Anhang „irgendwo“,
sondern als
EmbeddedFilemit einer Dokument-Zuordnung (AFRelationship, meistData) auf Dokumentebene, damit ein Reader die Rechnung als das führende Nutzdatum erkennt. - Der Metadaten-Stream deklariert das Format. Ein XMP-Extension-Schema im
PDF-Metadaten-Block nennt
DocumentType(INVOICE),DocumentFileName,VersionundConformanceLevel(also das ZUGFeRD-Profil).
Der Dateiname ist normiert — nicht frei wählbar
Die eingebettete XML-Datei muss exakt heißen, wie es der jeweilige Standard vorschreibt. Das ist ein häufiger, stiller Fehler:
| Standardstand | Dateiname der eingebetteten XML |
|---|---|
| ZUGFeRD 1.0 | ZUGFeRD-invoice.xml |
| ZUGFeRD 2.0 | zugferd-invoice.xml |
| ZUGFeRD 2.1+ / Factur-X | factur-x.xml |
Weicht der Name ab, findet der Empfänger das strukturierte Original nicht — das PDF gilt dann als reines Bild-PDF und erfüllt die E-Rechnungspflicht nicht.
Warum der sichtbare Teil und das XML deckungsgleich sein müssen
Unter der E-Rechnungspflicht ist bei einem hybriden Beleg das XML der führende, maßgebliche Teil — das PDF ist die visuelle Kopie. Weichen Sichtbeleg und XML voneinander ab (andere Summe, anderes Datum), ist das ein echtes Problem, kein Schönheitsfehler. Deshalb sollten beide aus derselben Datenquelle entstehen und nicht getrennt gerendert werden.
Der pragmatische Weg: per API statt Embedder selbst bauen
Du kannst PDF/A-3b-Konformität, XMP-Extension-Schema, AFRelationship und die
Namenskonvention mit einer PDF-Bibliothek selbst zusammensetzen — und jede
Format-Aktualisierung selbst nachziehen. Oder du delegierst die Erzeugung an eine
E-Rechnung API. Der Flow ist derselbe wie bei
ZUGFeRD/Factur-X per API:
1) Rechnung anlegen — Kunde und Positionen; der Server rechnet Summen und USt.
POST /invoices
Authorization: Bearer sk_test_…
Idempotency-Key: 6f1c2a…
Content-Type: application/json
{
"currency": "EUR",
"buyer": {
"name": "Achsfeld Transporte GmbH",
"addressLine1": "Gewerbestraße 8",
"postalCode": "59192",
"city": "Bergkamen",
"countryCode": "DE"
},
"invoiceTypeCode": "380",
"deliveryDate": "2026-07-31",
"lineItems": [
{ "description": "Ladevorgänge Juli 2026", "quantity": "1",
"unitCode": "C62", "unitPriceNet": "420.00", "taxRatePercent": "19" }
]
}2) Ausstellen — die Nummer wird atomar vergeben; aus einem EN-16931-Datenmodell entsteht das ZUGFeRD-PDF/A-3 mit dem eingebetteten CII-XML — Sichtbeleg und strukturierte Daten stammen aus einer Quelle und sind deshalb deckungsgleich:
PATCH /invoices/{id}/finalize
Authorization: Bearer sk_test_…3) PDF/A-3 abholen — die fertige hybride Datei:
GET /invoices/{id}/pdf
Authorization: Bearer sk_test_…
# → application/pdf: PDF/A-3b mit eingebettetem factur-x.xmlBrauchst du für die Vorschau ein Bild vor dem Ausstellen, liefert
GET /invoices/{id}/preview.pdf einen nicht persistierten, gewässerzeichneten
Entwurf mit demselben Renderer — die Vorschau ist byte-treu zu dem, was das Finalisieren
erzeugt.
Checkliste: woran du eine korrekte ZUGFeRD-PDF erkennst
- PDF/A-3b, nicht A-2 oder ein „normales“ PDF (per Preflight prüfbar).
- Genau eine eingebettete XML mit dem normkonformen Dateinamen (
factur-x.xmlbei aktuellen Profilen). - XMP-Extension-Schema vorhanden, mit passendem
ConformanceLevel(Profil). - Das eingebettete XML ist valides CII nach EN 16931 — wie du das prüfst, steht unter KoSIT-Validierung.
- Sichtbeleg und XML stimmen überein — Summen, Datum, Nummer; siehe oben.
Fortlauf erzeugt ZUGFeRD 2.x im Profil EN 16931 (COMFORT) als PDF/A-3b serverseitig und preflight-geprüft, aus demselben Datenmodell wie die XRechnung — kein zweiter Renderpfad, keine Divergenz zwischen PDF und XML.
Der gezeigte Code ist illustrativ: Die vollständige Dokumentation steht dir in der API-Referenz zur Verfügung — dort steht auch die Base-URL, offen und ohne Anmeldung. Den Test-Key legst du dir selbst im Kontobereich an (Schritt für Schritt im Quickstart).
Selbst ausprobieren? Der Zugang ist kostenlos und ohne Karte — Produkt-Einstieg für Entwickler →
Oder den eigenen Abrechnungsfluss konkret durchsprechen: 30 Min, technisch, mit dem Gründer →