ZUGFeRD E-Rechnung per API erstellen: Anleitung mit RechnungsAPI

Die elektronische Rechnungsstellung nimmt im deutschen B2B-Bereich stark zu. Während XRechnung hauptsächlich für Behörden verwendet wird, etabliert sich ZUGFeRD als der Standard für elektronische Rechnungen zwischen Unternehmen. ZUGFeRD-Rechnungen kombinieren das Beste aus beiden Welten: eine menschenlesbare PDF-Rechnung mit strukturierten XML-Daten für die automatisierte Verarbeitung.

In diesem Tutorial zeigen wir, wie Sie mit der RechnungsAPI einfach und schnell ZUGFeRD-konforme Rechnungen erstellen können - ohne sich in die komplexen technischen Details einarbeiten zu müssen.

Inhaltsverzeichnis

Was ist ZUGFeRD?

ZUGFeRD (Zentraler User Guide des Forums elektronische Rechnung Deutschland) ist der deutsche Standard für elektronische Rechnungen im Business-to-Business-Bereich. Anders als XRechnung, die als reine XML-Datei daherkommt, ist ZUGFeRD eine hybride Lösung: Eine normale PDF-Rechnung wird um strukturierte XML-Daten erweitert, die als unsichtbare Anlage in der PDF-Datei eingebettet sind.

Das Problem mit nativen CII/XML-Implementierungen

ZUGFeRD basiert auf dem UN/CEFACT CII (Cross Industry Invoice) Format - einem XML-Standard, der noch komplexer als UBL ist. Wer schon einmal versucht hat, ZUGFeRD-Rechnungen direkt als XML zu erstellen, weiß: Es ist ein Alptraum aus verschachtelten Strukturen, kryptischen Codes und strikten Validierungsregeln. Hinzu kommt das aufwendige Einbetten der XML-Datei in die PDF.

Adressdarstellung: Ein Beispiel der Komplexität

Eine einfache Adresse in nativem CII erfordert eine tiefe XML-Verschachtelung:

xml
<ram:PostalTradeAddress>
    <ram:LineOne>Musterstraße 55a</ram:LineOne>
    <ram:CityName>Hamburg</ram:CityName>
    <ram:PostcodeCode>12345</ram:PostcodeCode>
    <ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>

Dieselbe Adresse in der RechnungsAPI benötigt nur wenige Zeilen verständliches JSON:

json
{
  "address": {
    "line1": "Musterstraße 55a",
    "postalCode": "12345",
    "city": "Hamburg",
    "country": "DE"
  }
}

Komplexe Datenstrukturen: Wo CII versagt

Bei ZUGFeRD müssen Sie mit XML-Namespace-Präfixen wie ram:, udt: und qdt: arbeiten. Eine einzige Rechnungsposition sieht in nativem CII so aus:

xml
<ram:IncludedSupplyChainTradeLineItem>
    <ram:AssociatedDocumentLineDocument>
        <ram:LineID>1</ram:LineID>
    </ram:AssociatedDocumentLineDocument>
    <ram:SpecifiedTradeProduct>
        <ram:Name>Beratung und Konzeption</ram:Name>
        <ram:Description>Analyse und Erarbeitung eines Konzepts</ram:Description>
    </ram:SpecifiedTradeProduct>
    <ram:SpecifiedLineTradeAgreement>
        <ram:NetPriceProductTradePrice>
            <ram:ChargeAmount>95.00</ram:ChargeAmount>
        </ram:NetPriceProductTradePrice>
    </ram:SpecifiedLineTradeAgreement>
    <ram:SpecifiedLineTradeDelivery>
        <ram:BilledQuantity unitCode="HUR">3</ram:BilledQuantity>
    </ram:SpecifiedLineTradeDelivery>
    <ram:SpecifiedLineTradeSettlement>
        <ram:ApplicableTradeTax>
            <ram:CategoryCode>S</ram:CategoryCode>
            <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
        </ram:ApplicableTradeTax>
        <ram:SpecifiedTradeSettlementLineMonetarySummation>
            <ram:LineTotalAmount>285.00</ram:LineTotalAmount>
        </ram:SpecifiedTradeSettlementLineMonetarySummation>
    </ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>

Die gleiche Information im RechnungsAPI-Format ist deutlich einfacher:

json
{
  "unitPrice": { "value": "95.00", "currency": "EUR" },
  "quantity": { "value": "3", "unit": "HUR" },
  "item": {
    "name": "Beratung und Konzeption",
    "description": "Analyse und Erarbeitung eines Konzepts",
    "vat": { "code": "S", "rate": "19.00" }
  }
}

Die RechnungsAPI abstrahiert diese Komplexität komplett weg und generiert im Hintergrund das vollständig konforme CII-XML.

Die Vorteile von ZUGFeRD im B2B-Bereich

ZUGFeRD bietet gegenüber traditionellen PDF-Rechnungen erhebliche Vorteile, die sich sowohl für Absender als auch Empfänger auszahlen.

Für den Absender bedeutet ZUGFeRD, dass die gewohnte PDF-Optik erhalten bleibt, während gleichzeitig die Möglichkeit zur automatisierten Verarbeitung beim Empfänger geschaffen wird. Dies führt zu reduzierten Rückfragen zu Rechnungsdetails und beschleunigten Zahlungsprozessen. Die Investition in ZUGFeRD zahlt sich durch die verbesserte Kundenerfahrung und optimierte Cashflow-Zyklen schnell aus.

Der Empfänger profitiert von der Möglichkeit des automatischen Imports in ERP-Systeme, wodurch die manuelle Dateneingabe entfällt. Dies führt nicht nur zu weniger Fehlern bei der Buchung, sondern auch zu deutlich schnelleren Freigabeprozessen. Moderne ERP-Systeme können ZUGFeRD-Rechnungen vollautomatisch verarbeiten, von der Kreditorenerkennung bis zur Kostenstellenzuordnung.

Setup und Installation: Der erste Schritt

Wir verwenden im nachfolgenden Abschnitt beispielhaft das offizielle Node.js SDK von RechnungsAPI. Da RechnungsAPI eine REST-API mit OpenAPI Spezifikation ist, lassen sich die notwendigen HTTP-Aufrufe in jeder gängigen Programmiersprache erledigen. Auch die automatische Erstellung von Client-Code mithilfe von OpenAPI-Generatoren ist möglich.

Beginnen wir mit der praktischen Implementierung. Zuerst installieren wir den offiziellen Client:

bash
npm install @rechnungs-api/client

Das SDK bietet vollständige TypeScript-Unterstützung, was bedeutet, dass Ihr Editor Sie bei der Entwicklung mit Autocomplete und Typprüfung unterstützt. Dies reduziert Fehler erheblich und beschleunigt die Entwicklung.

Client-Initialisierung

Erstellen Sie eine Client-Instanz mit Ihrem API-Key. Den Key erhalten Sie nach der Registrierung auf rechnungs-api.de.

typescript
import { Client } from "@rechnungs-api/client";

export const client = new Client({
  // Verwenden Sie Umgebungsvariablen für produktive Anwendungen
  apiKey: process.env.RECHNUNGS_API_KEY || "YOUR_API_KEY",
});

Wichtig: Verwenden Sie für Tests den Test-API-Key (beginnt mit test_) und für Produktion den Produktions-Key (beginnt mit prod_).

Schritt-für-Schritt: ZUGFeRD-Rechnung erstellen

Schritt 1: Absender definieren - Ihr Unternehmen professionell darstellen

Der Absender ist, falls Sie nicht im Namen anderer Rechnungen erstellen wollen, in der Regel Ihr eigenes Unternehmen. Für ZUGFeRD sind vollständige Unternehmensdaten wichtig, damit die automatisierte Verarbeitung beim Empfänger reibungslos funktioniert:

typescript
import type { SenderParty } from "@rechnungs-api/client";

const sender: SenderParty = {
  name: "Muster GmbH",
  address: {
    line1: "Musterstraße 55a",
    postalCode: "12345", 
    city: "Hamburg",
    country: "DE",
  },
  contact: {
    name: "Max Mustermann",
    email: "max.mustermann@example.com", 
    phone: "+49123456789",
    website: "https://www.example.com",
  },
  vatId: "DE123456789", // USt-IdNr.
  taxId: "12/345/67890", // Steuernummer
  owner: "Max Mustermann", // Geschäftsführer
  registration: {
    office: "Amtsgericht Hamburg",
    number: "HRB 123456",
  },
};

Detaillierte Erläuterung der wichtigsten Felder:

  • vatId: Die Umsatzsteuer-Identifikationsnummer ist für umsatzsteuerpflichtige Unternehmen verpflichtend und muss das Format "DE" + 9 Ziffern haben.
  • taxId: Die Steuernummer des zuständigen Finanzamts. Das Format variiert je nach Bundesland.
  • owner: Bei Kapitalgesellschaften (GmbH, AG) ist die Angabe der vertretungsberechtigten Person erforderlich.
  • registration: Handelsregistereintrag ist bei Kapitalgesellschaften verpflichtend. Die Angaben müssen exakt mit dem Handelsregisterauszug übereinstimmen.

Schritt 2: Empfänger definieren - Kunden für B2B optimiert erfassen

Der Empfänger ist Ihr Geschäftskunde. Bei ZUGFeRD ist es besonders wichtig, dass die Empfängerdaten vollständig sind, damit die automatisierte Verarbeitung funktioniert:

typescript
import type { RecipientParty } from "@rechnungs-api/client";

const recipient: RecipientParty = {
  name: "Beispiel UG (haftungsbeschränkt)",
  address: {
    line1: "Musterweg 3c",
    postalCode: "54321",
    city: "Berlin", 
    country: "DE",
  },
  electronicAddress: {
    scheme: "EM",
    value: "buchhaltung@beispiel-ug.de",
  },
  contact: {
    name: "Erika Musterfrau",
    email: "erika.musterfrau@beispiel-ug.de",
    phone: "+49987654321",
  },
  vatId: "DE987654321",
};

Das Feld eletronicAddress wird dabei mit dem Electronic Address Scheme (EAS) definiert. Für B2B-Anwendungen ist die Verwendung des Codes EM (E-Mail Adresse) üblich.

Schritt 3: Zahlungsinformationen für Unternehmen definieren

B2B-Zahlungsinformationen unterscheiden sich oft von B2C-Rechnungen durch längere Zahlungsziele und spezifische Bankverbindungen:

typescript
import type { PaymentInformation } from "@rechnungs-api/client";

const payment: PaymentInformation = {
  means: [{
    code: "30", // SEPA-Überweisung 
    bankAccount: {
      bankName: "Muster Bank",
      iban: "DE12345678901234567890",
      bic: "MUSTDE12XXX",
    },
  }],
  terms: "Zahlbar innerhalb von 30 Tagen netto",
};

Verständnis der Payment Codes: Die Codes folgen dem UNTDID 4461. Die wichtigsten Codes für deutsche Unternehmen sind:

  • 30 - SEPA-Überweisung (am häufigsten verwendet)
  • 10 - Barzahlung (für Kleinbeträge)
  • 20 - Scheckzahlung (selten verwendet)
  • 30 - Krebitübertragung (SEPA-Überweisung)
  • 31 - Debitübertragung (Lastschriftverfahren)
  • 42 - Zahlung direkt an Bank
  • 48 - Bankguthaben (Gutschrift)

Schritt 4: Rechnungspositionen mit B2B-Focus

Bei B2B-Rechnungen sind detaillierte Beschreibungen und einheitliche Codes besonders wichtig:

typescript
import type { DocumentLineCreateRequest } from "@rechnungs-api/client";

const lines: DocumentLineCreateRequest[] = [
  {
    unitPrice: { value: "95.00", currency: "EUR" },
    item: {
      name: "Beratung und Konzeption",
      description: "Analyse und Erarbeitung eines Konzepts für die Digitalisierung",
      vat: { code: "S", rate: "19.00" }, // S = Standard rate
    },
    quantity: { value: "3", unit: "HUR" }, // HUR = Hour (Stunde)
  },
  {
    unitPrice: { value: "500.00", currency: "EUR" },
    item: {
      name: "Logo-Design",
      description: "Entwicklung eines Corporate Designs inkl. Logo",
      vat: { code: "S", rate: "19.00" },
    },
    quantity: { value: "1", unit: "H87" }, // H87 = Piece (Stück)
  },
];

Hinweise zu Einheit-Codes: Die API verwendet die UN/ECE-Empfehlung No. 20 für Einheitencodes. Diese standardisierten Codes stellen sicher, dass Ihre Rechnungen international verständlich sind:

  • HUR = Stunde (Hour) - für Dienstleistungen
  • H87 = Stück (Piece) - für einzelne Artikel oder Leistungen
  • MTR = Meter - für Längenangaben
  • MTK = Quadratmeter - für Flächenangaben
  • MTQ = Kubikmeter - für Volumenangaben
  • KGM = Kilogramm - für Gewichtsangaben
  • DAY = Tag - für tagesweise Abrechnung

Umsatzsteuer-Codes und deren Bedeutung: Die Umsatzsteuer-Codes folgen dem internationalen Standard UNTDID 5305:

  • S = Standard rate (Normalsatz, aktuell 19% in Deutschland)
  • A = Reduced rate (ermäßigter Satz, aktuell 7% in Deutschland)
  • Z = Zero rate (0% - für bestimmte Leistungen wie Ausfuhrlieferungen)
  • E = Exempt (steuerbefreit - für bestimmte medizinische, pädagogische Leistungen)
  • AE = VAT Reverse Charge (Reverse-Charge-Verfahren bei B2B-Leistungen)

Schritt 5: ZUGFeRD-Konfiguration aktivieren

Die ZUGFeRD-Konfiguration unterscheidet sich leicht von XRechnung. ZUGFeRD nutzt das CII-Format und ist primär für B2B-Anwendungen optimiert:

typescript
import type { EInvoiceConfiguration } from "@rechnungs-api/client";

const eInvoiceConfig: EInvoiceConfiguration = {
  type: "zugferd",
  profile: "xrechnung", // Verfügbare Profile: 'minimum' | 'basic-wl' | 'basic' | 'en-16931' | 'extended' | 'xrechnung'
};

Die verfügbaren ZUGFeRD-Profile:

  • minimum: Grundlegende Rechnungsdaten, minimaler Funktionsumfang (entspricht nicht § 14 UStG und daher in Deutschland ungültig)
  • basic-wl: Basic-Profil ohne Leitweg-ID (entspricht nicht § 14 UStG und daher in Deutschland ungültig)
  • basic: Standard-B2B-Funktionen, ausreichend für die meisten Anwendungsfälle
  • en-16931: Europäischer Standard, kompatibel mit EU-Richtlinien
  • extended: Vollständiger Funktionsumfang für komplexe B2B-Szenarien
  • xrechnung: Kompatibel mit XRechnung-Standard, ideal für öffentliche Auftraggeber

Empfehlung für B2B: Verwenden Sie "xrechnung" für maximale Kompatibilität oder "en-16931" für europäische Standards.

Schritt 6: Die vollständige ZUGFeRD-Rechnung zusammenbauen

Jetzt fügen wir alle Komponenten zu einer vollständigen ZUGFeRD-Rechnung zusammen. B2B-Rechnungen benötigen oft zusätzliche Referenzen und Texte:

typescript
import type { DocumentCreateRequest } from "@rechnungs-api/client";

const documentRequest: DocumentCreateRequest = {
  type: "invoice",
  locale: "de-DE",
  number: "RE-2024-001",
  issueDate: "2024-02-28",
  dueDate: "2024-03-29", // 30 Tage später
  
  sender,
  recipient,
  payment,
  lines,
  
  // ZUGFeRD aktivieren
  eInvoice: eInvoiceConfig,
  
  // Projekt oder Auftragsnummer für die automatisierte Zuordnung in ERP-Systemen.
  // Falls nicht zutreffend wird hier auch "00" verwendet.
  buyerReference: "00",
  
  // Professionelles Anschreiben
  preTableText: "Sehr geehrte Damen und Herren,\n\nfür die erbrachten Leistungen stellen wir Ihnen folgende Positionen in Rechnung:",
  postTableText: "Vielen Dank für Ihren Auftrag! Bitte überweisen Sie den Betrag bis zum Fälligkeitsdatum auf das angegebene Konto.",
  
  // Corporate Design
  theme: {
    fontFamily: "Open Sans",
    // logo: `data:image/png;base64,${logoBase64}`, // Base64-kodiertes Logo
  },
};

Schritt 7: ZUGFeRD-Rechnung erstellen und verwenden

Der finale Schritt - die Erstellung der ZUGFeRD-Datei. Das Besondere: Sie erhalten eine PDF-Datei, die die strukturierten Daten unsichtbar enthält:

typescript
import * as fs from "node:fs/promises";

try {
  // Dokument erstellen
  console.log("Erstelle ZUGFeRD-Rechnung...");
  const document = await client.createDocument(documentRequest);
  console.log(`ZUGFeRD-Rechnung erstellt! ID: ${document.id}`);
  
  // ZUGFeRD PDF herunterladen (enthält eingebettete XML-Daten)
  console.log("Lade ZUGFeRD PDF herunter...");
  const pdfBuffer = await client.readDocument(document.id, "pdf");
  await fs.writeFile("zugferd-rechnung.pdf", Buffer.from(pdfBuffer));
  console.log("ZUGFeRD PDF gespeichert als zugferd-rechnung.pdf");
  
  return document;
} catch (error) {
  console.error("Fehler beim Erstellen der ZUGFeRD-Rechnung:", error);
  throw error;
}

Die resultierende PDF sieht wie folgt aus:

RechnungsAPI ZUGFeRD PDF

Vollständiges ZUGFeRD-Arbeitsbeispiel

Hier ist das komplette, lauffähige Beispiel für ZUGFeRD:

typescript
import * as fs from "node:fs/promises";
import type {
  DocumentCreateRequest,
  RecipientParty, 
  SenderParty,
} from "@rechnungs-api/client";
import { Client } from "@rechnungs-api/client";

// Client initialisieren
const client = new Client({
  apiKey: process.env.RECHNUNGS_API_KEY || "YOUR_API_KEY",
});

// Absender definieren
const sender: SenderParty = {
  name: "Muster GmbH",
  address: {
    line1: "Musterstraße 55a",
    postalCode: "12345",
    city: "Hamburg", 
    country: "DE",
  },
  electronicAddress: {
    scheme: "EM",
    value: "info@example.com",
  },
  contact: {
    name: "Max Mustermann",
    email: "max.mustermann@example.com",
    phone: "+49123456789",
    website: "https://www.example.com",
  },
  vatId: "DE123456789",
  taxId: "12/345/67890", 
  owner: "Max Mustermann",
  registration: {
    office: "Amtsgericht Hamburg",
    number: "HRB 123456",
  },
};

// Empfänger definieren
const recipient: RecipientParty = {
  name: "Beispiel UG (haftungsbeschränkt)",
  address: {
    line1: "Musterweg 3c",
    postalCode: "54321",
    city: "Berlin",
    country: "DE",
  },
  electronicAddress: {
    scheme: "EM", 
    value: "buchhaltung@beispiel-ug.de",
  },
  contact: {
    name: "Erika Musterfrau",
    email: "erika.musterfrau@beispiel-ug.de",
    phone: "+49987654321",
  },
  vatId: "DE987654321",
};

// ZUGFeRD-Rechnung erstellen
const documentRequest: DocumentCreateRequest = {
  type: "invoice",
  locale: "de-DE", 
  number: "RE-2024-001",
  issueDate: "2024-02-28",
  dueDate: "2024-03-29",
  
  sender,
  recipient,
  buyerReference: "PROJEKT-2024-ZF",
  
  preTableText: "Sehr geehrte Damen und Herren,\n\nfür die erbrachten Leistungen stellen wir Ihnen folgende Positionen in Rechnung:",
  postTableText: "Vielen Dank für Ihren Auftrag! Bitte überweisen Sie den Betrag bis zum Fälligkeitsdatum auf das angegebene Konto.",
  
  // Rechnungspositionen
  lines: [
    {
      unitPrice: { value: "95.00", currency: "EUR" },
      item: {
        name: "Beratung und Konzeption", 
        description: "Analyse und Erarbeitung eines Konzepts für die Digitalisierung",
        vat: { code: "S", rate: "19.00" },
      },
      quantity: { value: "3", unit: "HUR" },
    },
    {
      unitPrice: { value: "500.00", currency: "EUR" },
      item: {
        name: "Logo-Design",
        description: "Entwicklung eines Corporate Designs inkl. Logo", 
        vat: { code: "S", rate: "19.00" },
      },
      quantity: { value: "1", unit: "H87" },
    },
  ],
  
  // Zahlungsinformationen
  payment: {
    means: [{
      code: "30",
      bankAccount: {
        bankName: "Muster Bank",
        iban: "DE12345678901234567890", 
        bic: "MUSTDE12XXX",
      },
    }],
    terms: "Zahlbar innerhalb von 30 Tagen netto",
  },
  
  // ZUGFeRD aktivieren - der wichtige Unterschied zu XRechnung
  eInvoice: {
    type: "zugferd",
    profile: "xrechnung", // Für maximale Kompatibilität
  },
  
  // Theme anpassen (optional)
  theme: {
    fontFamily: "Open Sans",
  },
};

// Hauptfunktion
async function main() {
  try {
    console.log("Erstelle ZUGFeRD-Rechnung...");
    const document = await client.createDocument(documentRequest);
    console.log(`✅ ZUGFeRD-Rechnung erstellt! ID: ${document.id}`);
    
    // ZUGFeRD PDF herunterladen
    const pdfBuffer = await client.readDocument(document.id, "pdf");
    await fs.writeFile("zugferd-rechnung.pdf", Buffer.from(pdfBuffer)); 
    console.log("✅ ZUGFeRD PDF gespeichert als zugferd-rechnung.pdf");
    
    // Dokumentdetails ausgeben
    console.log("\n📋 Rechnungsdetails:");
    console.log(`Nummer: ${document.number}`);
    console.log(`Nettobetrag: ${document.netAmount.value} ${document.netAmount.currency}`);
    console.log(`Bruttobetrag: ${document.grossAmount.value} ${document.grossAmount.currency}`);
    console.log(`Erstellt: ${document.createdAt}`);
    console.log(`Verfällt: ${document.expiresAt}`);
    
  } catch (error) {
    console.error("❌ Fehler:", error);
  }
}

main();

Erweiterte B2B-Features für ZUGFeRD

Lieferanten-Integration optimieren

ZUGFeRD-Rechnungen werden oft automatisiert in ERP-Systeme importiert. Zusätzliche Felder helfen dabei:

typescript
const documentRequest: DocumentCreateRequest = {
  // ... andere Felder
  
  // Lieferadresse für komplexe B2B-Szenarien
  delivery: {
    address: {
      line1: "Lieferstraße 10",
      postalCode: "98765",
      city: "München", 
      country: "DE",
    },
  },
  
  // Leistungszeitraum für Dienstleistungen
  deliveryPeriod: {
    startDate: "2024-02-01",
    endDate: "2024-02-28",
    vatDate: "35",
  },
};

Branding für B2B-Professionalität

typescript
import * as fs from "node:fs/promises";

// Corporate Logo einbetten
const logoBuffer = await fs.readFile("./company-logo.png");
const logoBase64 = logoBuffer.toString("base64");

const documentRequest: DocumentCreateRequest = {
  // ... andere Felder
  theme: {
    logo: `data:image/png;base64,${logoBase64}`,
    fontFamily: "Open Sans",
  },
};

Fazit

Die RechnungsAPI macht die Erstellung von ZUGFeRD Dokumenten möglich, ohne sich mit den technischen Details auseinanderzusetzen. Statt sich mit komplexen CII-Strukturen und PDF Standards, arbeiten Sie mit einem intuitiven JSON-API.

Die wichtigsten Vorteile im Überblick:

  • Einfach: JSON statt XML
  • Sicher: Automatische Validierung
  • Komplett: Sie müssen keine eigene PDF liefern
  • Flexibel: Unterstützung für viele ZUGFeRD-Features
  • Developer-friendly: TypeScript-Support, gute Dokumentation

Mit diesem Tutorial können Sie sofort anfangen, ZUGFeRD Rechnungen zu erstellen. Die RechnungsAPI übernimmt die komplexe PDF-Generierung und Sie können sich auf Ihre Geschäftslogik konzentrieren.

Für weitere Informationen und erweiterte Features besuchen Sie die offizielle Dokumentation. Dort lässt sich die API auch direkt mit Beispielen ausprobieren.