Mit KI erstellt

XRechnung per API erstellen: Ein Tutorial mit RechnungsAPI

XRechnung ist der deutsche Standard für elektronische Rechnungen an die öffentliche Verwaltung. Lieferanten des Bundes müssen ihre Rechnungen seit dem 27.11.2020 elektronisch einreichen (§ 3 E-Rechnungsverordnung), und seit dem 01.01.2025 ist die E-Rechnung nach § 14 UStG auch zwischen inländischen Unternehmen der Standard. Wer Rechnungsstellung in eigene Software integriert, steht damit vor der Frage: Wie erstellt man konforme XRechnungen, ohne die XML-Spezifikation im Detail zu implementieren?

Dieses Tutorial zeigt Schritt für Schritt, wie Sie mit RechnungsAPI XRechnungen erstellen – ohne UBL-Kenntnisse.

Was ist eine XRechnung?

XRechnung ist eine nationale Ausprägung (CIUS) der europäischen Norm EN 16931 und wird von der KoSIT im Auftrag des IT-Planungsrats betrieben. Eine XRechnung ist ein strukturiertes XML-Dokument in der Syntax UBL oder UN/CEFACT CII. Die technischen Details erklärt unser Artikel XRechnung für Entwickler.

Das Problem mit nativem UBL/XML

Eine einfache Adresse erfordert in UBL eine verschachtelte XML-Struktur mit Namespace-Präfixen:

xml
<cac:PostalAddress>
    <cbc:StreetName>Musterstraße 55a</cbc:StreetName>
    <cbc:CityName>Hamburg</cbc:CityName>
    <cbc:PostalZone>12345</cbc:PostalZone>
    <cac:Country>
        <cbc:IdentificationCode>DE</cbc:IdentificationCode>
    </cac:Country>
</cac:PostalAddress>

Dieselbe Adresse in RechnungsAPI ist schlichtes JSON:

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

Bei Rechnungspositionen wird der Unterschied noch deutlicher. Eine einzelne Position in UBL:

xml
<cac:InvoiceLine>
    <cbc:ID>1</cbc:ID>
    <cbc:InvoicedQuantity unitCode="HUR">3</cbc:InvoicedQuantity>
    <cbc:LineExtensionAmount currencyID="EUR">285.00</cbc:LineExtensionAmount>
    <cac:Item>
        <cbc:Name>Beratung und Konzeption</cbc:Name>
        <cbc:Description>Analyse und Erarbeitung eines Konzepts</cbc:Description>
        <cac:ClassifiedTaxCategory>
            <cbc:ID>S</cbc:ID>
            <cbc:Percent>19</cbc:Percent>
            <cac:TaxScheme>
                <cbc:ID>VAT</cbc:ID>
            </cac:TaxScheme>
        </cac:ClassifiedTaxCategory>
    </cac:Item>
    <cac:Price>
        <cbc:PriceAmount currencyID="EUR">95.00</cbc:PriceAmount>
    </cac:Price>
</cac:InvoiceLine>

Die gleiche Information im RechnungsAPI-Format:

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" }
  }
}

Positionsnummern, Summenbildung und das konforme UBL-XML erzeugt die API im Hintergrund – inklusive Validierung.

Setup und Installation

Die Beispiele verwenden das offizielle Node.js-SDK. Da RechnungsAPI eine REST-API mit OpenAPI-Spezifikation ist, funktionieren die gleichen Aufrufe aus jeder Programmiersprache, auch mit generierten OpenAPI-Clients.

bash
npm install @rechnungs-api/client

Das SDK ist vollständig in TypeScript typisiert.

Client-Initialisierung

Den API-Key erhalten Sie nach der Registrierung auf rechnungs-api.de. Test-Keys beginnen mit test_, Produktions-Keys mit prod_.

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

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

Schritt für Schritt zur XRechnung

Schritt 1: Absender definieren

Der Absender ist in der Regel Ihr eigenes Unternehmen:

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",
  },
  // Elektronische Adresse - erforderlich für XRechnung
  electronicAddress: {
    scheme: "EM", // EM = E-Mail
    value: "info@example.com",
  },
  contact: {
    name: "Max Mustermann",
    email: "max.mustermann@example.com",
    phone: "+49123456789",
  },
  vatId: "DE123456789", // USt-IdNr.
  taxId: "12/345/67890", // Steuernummer
  owner: "Max Mustermann", // Inhaber bzw. Geschäftsführung
  registration: {
    office: "Amtsgericht Hamburg",
    number: "HRB 123456",
  },
};

Die wichtigsten Felder:

  • electronicAddress: Die elektronische Adresse ist für E-Rechnungen erforderlich. Der Scheme-Code stammt aus der EAS-Codeliste; EM steht für eine E-Mail-Adresse.
  • vatId: Die Umsatzsteuer-Identifikationsnummer (in Deutschland „DE“ + 9 Ziffern).
  • taxId: Alternativ oder zusätzlich die Steuernummer des Finanzamts.
  • registration / owner: Handelsregistereintrag und vertretungsberechtigte Person – sie erscheinen auf der PDF-Darstellung der Rechnung.

Schritt 2: Empfänger definieren

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",
};

Rechnungen an Behörden: Öffentliche Auftraggeber werden über ihre Leitweg-ID adressiert. Dafür ist in der EAS-Codeliste der Code 0204 vorgesehen:

typescript
electronicAddress: {
  scheme: "0204", // EAS-Code für die deutsche Leitweg-ID
  value: "991-12345-67890-12", // Die Leitweg-ID der Behörde
}

Die Leitweg-ID teilt Ihnen der Auftraggeber mit, üblicherweise bei der Beauftragung. Sie muss außerdem als Käuferreferenz (buyerReference, siehe Schritt 6) angegeben werden.

Schritt 3: Zahlungsinformationen festlegen

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

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

Die Zahlungsart folgt der Codeliste UNTDID 4461. Die wichtigsten Codes:

  • 58 – SEPA-Überweisung (SEPA credit transfer)
  • 30 – Überweisung allgemein (Credit transfer)
  • 59 – SEPA-Lastschrift (SEPA direct debit)
  • 48 – Kartenzahlung (Bank card)
  • 10 – Barzahlung (In cash)

Schritt 4: Rechnungspositionen erstellen

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" },
    },
    quantity: { value: "3", unit: "HUR" }, // HUR = 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 = Stück
  },
];

Einheiten-Codes stammen aus UN/ECE Recommendation N°20 und N°21, zum Beispiel:

  • H87 = Stück, C62 = Einheit „Eins“
  • HUR = Stunde, DAY = Tag
  • KGM = Kilogramm, MTR = Meter, MTK = Quadratmeter, LTR = Liter

Umsatzsteuer-Kategorien folgen der Codeliste UNTDID 5305:

  • S = regulär besteuert – gilt für jeden Steuersatz über 0 %, also sowohl 19 % als auch den ermäßigten Satz von 7 % (jeweils mit passendem rate)
  • Z = Steuersatz 0 %
  • E = steuerbefreit; ein Befreiungsgrund ist erforderlich, z. B. bei Kleinunternehmern nach § 19 UStG
  • AE = Reverse Charge (§ 13b UStG)
  • K = innergemeinschaftliche Lieferung, G = Ausfuhr, O = nicht steuerbar

Schritt 5: XRechnung-Konfiguration

Das Feld eInvoice macht aus der Rechnung eine E-Rechnung:

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

const eInvoiceConfig: EInvoiceConfiguration = {
  type: "xrechnung", // Alternativ "zugferd"
  syntax: "ubl", // Alternativ "cii"
  embedPdf: true, // PDF-Darstellung in das XML einbetten
};
  • syntax: XRechnung erlaubt die Syntaxen UBL und CII; wir empfehlen ubl.
  • embedPdf: RechnungsAPI erzeugt aus den Rechnungsdaten auch eine menschenlesbare PDF-Datei und kann sie als Anhang in das XML einbetten.

Schritt 6: Die vollständige Rechnung zusammenbauen

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

const documentRequest: DocumentCreateRequest = {
  type: "invoice", // Auch andere Dokumenttypen wie "self-billing-invoice" sind möglich
  locale: "de-DE",
  number: "RE-2026-001",
  issueDate: "2026-06-15",
  dueDate: "2026-07-15",

  sender,
  recipient,
  payment,
  lines,

  // XRechnung aktivieren
  eInvoice: eInvoiceConfig,

  // Käuferreferenz (BT-10) - bei Behörden die Leitweg-ID
  buyerReference: "PROJEKT-2026-XR",

  // Texte für die PDF-Darstellung
  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.",

  // Optional: Gestaltung der PDF
  theme: {
    fontFamily: "Open Sans",
  },
};
  • buyerReference: Eine Referenz des Käufers, etwa eine Auftrags- oder Projektnummer. Bei Rechnungen an deutsche Behörden gehört hier die Leitweg-ID hinein.
  • issueDate / dueDate: Datumsangaben im ISO-Format YYYY-MM-DD.

Schritt 7: Rechnung erstellen und herunterladen

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

const document = await client.createDocument(documentRequest);
console.log(`XRechnung erstellt! ID: ${document.id}`);

// XRechnung-XML herunterladen
const xml = await client.readDocument(document.id, "xml");
await fs.writeFile("xrechnung.xml", xml);

Die in das XML eingebettete PDF-Darstellung sieht so aus:

RechnungsAPI XRechnung PDF

Vollständiges Arbeitsbeispiel

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

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

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",
  },
  vatId: "DE123456789",
  taxId: "12/345/67890",
  owner: "Max Mustermann",
  registration: {
    office: "Amtsgericht Hamburg",
    number: "HRB 123456",
  },
};

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",
};

const documentRequest: DocumentCreateRequest = {
  type: "invoice",
  locale: "de-DE",
  number: "RE-2026-001",
  issueDate: "2026-06-15",
  dueDate: "2026-07-15",

  sender,
  recipient,
  buyerReference: "PROJEKT-2026-XR",

  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.",

  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" },
    },
  ],

  payment: {
    means: [{
      code: "58",
      bankAccount: {
        bankName: "Muster Bank",
        iban: "DE12345678901234567890",
        bic: "MUSTDE12XXX",
      },
    }],
    terms: "Zahlbar innerhalb von 30 Tagen netto",
  },

  eInvoice: {
    type: "xrechnung",
    syntax: "ubl",
    embedPdf: true,
  },

  theme: {
    fontFamily: "Open Sans",
  },
};

async function main() {
  const document = await client.createDocument(documentRequest);
  console.log(`XRechnung erstellt! ID: ${document.id}`);

  const xml = await client.readDocument(document.id, "xml");
  await fs.writeFile("xrechnung.xml", xml);

  console.log(`Nummer: ${document.number}`);
  console.log(`Nettobetrag: ${document.netAmount.value} ${document.netAmount.currency}`);
  console.log(`Bruttobetrag: ${document.grossAmount.value} ${document.grossAmount.currency}`);
}

main();

Erweiterte Features

Logo hinzufügen

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

const logoBuffer = await fs.readFile("./logo.png");
const logoBase64 = logoBuffer.toString("base64");

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

Lieferadresse und Leistungszeitraum

typescript
const documentRequest: DocumentCreateRequest = {
  // ... andere Felder
  delivery: {
    address: {
      line1: "Lieferstraße 10",
      postalCode: "98765",
      city: "München",
      country: "DE",
    },
  },
  deliveryPeriod: {
    startDate: "2026-06-01",
    endDate: "2026-06-30",
    vatDate: "35",
  },
};

Das optionale Feld vatDate gibt an, wann die Umsatzsteuer entsteht. Zulässig ist die von der EN 16931 vorgegebene Teilmenge der Codeliste UNTDID 2005:

  • 3 – mit dem Rechnungsdatum
  • 35 – mit dem tatsächlichen Lieferdatum
  • 432 – mit der Zahlung

Error Handling

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

try {
  const document = await client.createDocument(documentRequest);
  // ...
} catch (error) {
  if (error instanceof ApiError) {
    console.error(`API Fehler [${error.status}]:`, error.body);
  }
  throw error;
}

Fazit

Mit RechnungsAPI erstellen Sie XRechnungen über eine JSON-API, ohne UBL-Strukturen, Codelisten-Feinheiten und Validierungsregeln selbst zu implementieren. PDF-Darstellung und konformes XML entstehen in einem einzigen API-Aufruf.

Alle Details und weitere Funktionen finden Sie in der Dokumentation, wo sich die API auch direkt ausprobieren lässt.

Quellen