Zuletzt aktualisiert: Juli 2026 · .NET 8, funktioniert ab .NET 6 · Lesezeit: ca. 8 Minuten
In deutschen Unternehmen läuft die Fakturierung auffallend oft durch .NET-Code — gewachsene Warenwirtschaften, Branchenlösungen, interne Tools. Genau diese Systeme trifft die E-Rechnungs-Pflicht am härtesten: Ab Januar 2027 (Umsatz über 800.000 €) bzw. 2028 (alle) müssen ausgehende B2B-Rechnungen als XRechnung oder ZUGFeRD raus, und ein RechnungDrucken()-Modul aus 2012 hat dazu naturgemäß keine Meinung.
Der Weg mit dem besten Aufwand-Nutzen-Verhältnis: Das Altsystem liefert weiter seine Rechnungsdaten, ein API-Aufruf macht daraus normkonformes, validiertes XML. Was Sie dafür anfassen müssen, ist eine einzige Methode am Ende Ihrer Rechnungsstrecke.
Setup
Kein Paket nötig — HttpClient und System.Text.Json aus der Standardbibliothek reichen völlig, und genau so arbeitet dieser Artikel. (Ein offizielles NuGet-Paket FakturWire erscheint in Kürze; die API bleibt dieselbe.) API-Key kostenlos hier (50 Credits, keine Kreditkarte), abgelegt als Umgebungsvariable oder im Secret Store, nicht in appsettings.json im Repo.
Das Rechnungsobjekt
Ein POCO-Graph, der sich 1:1 nach JSON serialisiert. Beträge sind bewusst Strings — decimal wäre in .NET zwar sicher, aber auf dem Draht garantiert der String, dass nirgendwo ein Binary-Float dazwischenfunkt:
var invoice = new
{
number = "RE-2026-0815",
issueDate = "2026-07-20",
dueDate = "2026-08-19",
currency = "EUR",
buyerReference = "BESTELLUNG-4711", // BT-10 — Pflichtfeld in XRechnung
seller = new
{
name = "Beispiel GmbH",
vatId = "DE811128135",
electronicAddress = new { schemeId = "9930", value = "DE811128135" },
address = new { streetName = "Hauptstraße 5", city = "Berlin",
postalZone = "10115", countryCode = "DE" },
contact = new { name = "Erika Muster", phone = "+49 30 1234567",
email = "rechnung@beispiel.de" } // BR-DE-2: alle drei Pflicht
},
buyer = new
{
name = "Kunde AG",
vatId = "DE129273398",
electronicAddress = new { schemeId = "9930", value = "DE129273398" },
address = new { streetName = "Marktplatz 1", city = "München",
postalZone = "80331", countryCode = "DE" }
},
payment = new { meansCode = "58", iban = "DE02120300000000202051",
reference = "RE-2026-0815" }, // BR-DE-1
lines = new[]
{
new {
id = "1", name = "Wartungsvertrag Q3/2026",
quantity = "1", unitPrice = "2400.00",
vat = new { categoryCode = "S", rate = "19" }
}
}
};
Ihnen fehlen die Summenfelder? Richtig so. Endbetrag, Steuerbasis und Umsatzsteuer-Aufstellung werden serverseitig nach den BR-CO-Regeln berechnet und gegengeprüft — die Cent-Differenzen zwischen Altsystem-Summen und normgerechter Rundung sind der häufigste Ablehnungsgrund überhaupt, und so entstehen sie gar nicht erst.
Der Aufruf — HttpClient
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer",
Environment.GetEnvironmentVariable("FW_API_KEY"));
var response = await http.PostAsJsonAsync(
"https://fakturwire.com/v1/convert",
new { target = "xrechnung-ubl", invoice });
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
if ((int)response.StatusCode == 422)
{
foreach (var f in body.GetProperty("findings").EnumerateArray())
Console.Error.WriteLine(
$"[{f.GetProperty("rule")}] {f.GetProperty("message")}");
}
else if (response.IsSuccessStatusCode)
{
var xml = body.GetProperty("xml").GetString()!;
await File.WriteAllTextAsync("RE-2026-0815.xml", xml);
}
Zielformate: xrechnung-ubl (deutsche Empfänger), facturx-en16931 (hybrides ZUGFeRD-Szenario: menschenlesbares PDF, maschinenlesbarer Kern), peppol-bis-3 (EU-Versand über Peppol, in Belgien bereits Pflicht).
Integration ins Altsystem: die zwei bewährten Regeln
Erstens: validieren, bevor der Nutzer weg ist. Rufen Sie /v1/validate bereits beim Erfassen oder Freigeben der Rechnung auf und zeigen Sie die Befunde im UI. Eine fehlende Käuferreferenz korrigiert die Sachbearbeiterin in zehn Sekunden — dieselbe Meldung drei Tage später aus einem Batch-Log ist ein Support-Ticket.
Zweitens: das XML ist das Original. Speichern Sie die zurückgegebene Datei unverändert und GoBD-konform; die hübsche PDF-Ansicht ist nur eine Darstellung. Wer ZUGFeRD/Factur-X nutzt, archiviert die Hybrid-PDF — dort steckt das XML mit drin.
Und ein Betriebshinweis: Jede Antwort enthält den Credit-Reststand. Ein Gauge in Ihrem Monitoring plus Alarm unter Schwellwert erspart den 402 mitten im Monatsabschluss.
Die üblichen Verdächtigen
Beim ersten Lauf mit Bestandsdaten scheitern Rechnungen fast immer an denselben Regeln: BR-DE-15 (Käuferreferenz fehlt), BR-DE-2 (Ansprechpartner unvollständig), BR-CO-15 (Alt-Summen mit Cent-Differenz), BR-E-10 (Reverse Charge ohne Befreiungsgrund). Jede Regel-ID aus der API hat eine eigene Seite mit Ursache und Fix — verlinken Sie sie ruhig direkt aus Ihren Fehlermeldungen.
Zum Ausprobieren ohne eine Zeile Code: der kostenlose Validator — dieselbe Regel-Engine wie die API, direkt im Browser. Wenn's ernst wird: API-Key holen, 50 Credits gratis, Abrechnung pro Rechnung, kein Abo.