Ein ZUGFeRD-Dokument ist ein PDF/A-3b, in das das CII-XML nach EN 16931 als benannte Datei eingebettet ist (bei ZUGFeRD 2.1+/Factur-X: factur-x.xml), mit einer Dokument-Zuordnung (AFRelationship) und einem XMP-Extension-Schema, das Profil und Dateinamen deklariert. Selbst gebaut ist das fehleranfällig — PDF/A-3b-Konformität, XMP und Beleg-XML-Konsistenz müssen exakt stimmen. Über eine API bekommst du das fertige PDF/A-3 aus einem Call.
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.
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 erklärt.
Fortlauf erzeugt aktuelle ZUGFeRD-2.x-Profile 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. Base-URL und Test-Key bekommst du mit dem Sandbox-Zugang im Entwickler-Bereich.
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 →