1. Übersicht

Der Server stellt drei Tools und eine Resource bereit, die von KI-Agenten über das Model Context Protocol aufgerufen werden können.

SzenarioTool / Resource
Rechnung vor dem Versand prüfenvalidate_invoice
XML-Daten aus ZUGFeRD-PDF lesenextract_xml
Prüfen ob PDF und XML übereinstimmencheck_consistency
Verfügbare Formate und Regelsets abfragenvalidation://rules
Alle Antworten sind strukturiertes JSON. Kein Freitext — jeder Fehler enthält Regel-ID, betroffenes Feld, Fehlermeldung, umsetzbaren Korrekturvorschlag und XPath.

2. Architektur & Datenfluss

KI-Agent │ │ MCP-Nachricht (JSON-RPC über stdio) ▼ ┌─────────────────────────────────────────┐ │ zugferd-mcp-client (npm, lokal) │ │ │ │ validate_invoice · extract_xml │ │ check_consistency │ │ │ │ │ HTTPS REST-Request │ │ ▼ │ │ ZUGFeRD Validator API (gehostet) │ │ api.zugferd-validator.de │ │ Auth: X-Api-Key │ │ │ │ │ Base64 → Mustang + KoSIT (parallel) │ │ ▼ │ │ Report-Parser + Fix-Suggestions-DB │ └─────────────────┬───────────────────────┘ │ Strukturiertes JSON ▼ KI-Agent erhält Ergebnis

Ablauf für validate_invoice

1
MCP-Client empfängt Aufruf — Base64-Datei + Parameter vom KI-Agenten
2
HTTPS-Request an API — POST /v1/validate mit X-Api-Key Header
3
Auth & Rate-Limit — API prüft Key, Wochenkontingent und Stundenlimit; über dem Stundenlimit läuft der Aufruf in der langsamen Spur
4
Dual-Validierung (parallel) — Mustang CLI (ZUGFeRD/Factur-X) und KoSIT Validierungstool (XRechnung + EN 16931 CII) laufen gleichzeitig (max. 30s)
5
Ergebnisse konsolidieren — Fehler beider Engines werden zusammengeführt und dedupliziert; Fix-Suggestions angereichert
6
JSON-Ergebnis — API antwortet, MCP-Client leitet an KI-Agenten weiter

3. Validierungsengines

Jede Anfrage wird von zwei Engines gleichzeitig geprüft. Die Ergebnisse werden konsolidiert und dedupliziert zurückgegeben.

EngineVersionAbgedeckte Formate
Mustang Project v2.26.0 ZUGFeRD 1.0–2.4, Factur-X 1.0, XRechnung (allgemein)
KoSIT Validierungstool v1.6.3 XRechnung 3.0.2 (CII + UBL), EN 16931 CII/UBL (plain), Factur-X 1.07.3 / 1.08 (alle Profile)
Konsolidierte Antwort: Fehler aus beiden Engines erscheinen in derselben errors-Liste. Duplikate (gleiche Regel-ID + Meldung) werden automatisch entfernt. KoSIT-Fehler fließen nur ein, wenn ein passendes Validierungsszenario erkannt wurde — bei unbekannten GuidelineIDs übernimmt Mustang allein.

4. Installation & Konfiguration

1
API-Key holen
# Kostenlos registrieren:
https://zugferd-validator.de/portal
2
MCP-Client installieren
npm install -g zugferd-mcp-client
3
In MCP-Client konfigurieren
// mcp_config.json (je nach Client abweichender Pfad)
{
  "mcpServers": {
    "zugferd": {
      "command": "zugferd-mcp-client",
      "env": {
        "ZUGFERD_API_KEY": "zv_dein_key_hier"
      }
    }
  }
}

Direkte REST API

Die API kann auch direkt ohne MCP-Client angesprochen werden:

# Beispiel: Validierung per curl
curl -X POST https://api.zugferd-validator.de/v1/validate \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: zv_dein_key_hier" \
  -d '{"file_content":"<base64>","file_type":"xml"}'

Umgebungsvariablen (MCP-Client)

VariableBeschreibungStandard
ZUGFERD_API_KEYAPI-Key vom Portal– (Pflicht)
ZUGFERD_API_URLAPI-Basis-URLhttps://api.zugferd-validator.de

5. Tools

validate_invoice TOOL

Validiert eine E-Rechnung (ZUGFeRD/Factur-X PDF oder XRechnung XML) gegen EN 16931 und die deutschen Erweiterungen. Gibt strukturierte Fehler, Warnungen und Korrekturvorschläge zurück.

Input-Schema

file_content * string Base64-kodierter Inhalt der PDF- oder XML-Datei. Data-URL-Präfixe (data:application/pdf;base64,…) werden automatisch entfernt.
file_type * enum "pdf" für ZUGFeRD/Factur-X-PDFs · "xml" für reine XRechnung/CII-XML-Dateien
profile enum? Optionale Profilangabe. Bei Abweichung zum erkannten Profil wird eine warning mit rule_id: "PROFILE_MISMATCH" erzeugt.
Werte: minimum · basic_wl · basic · en16931 · extended · xrechnung

Output-Schema

{
  "valid":    true,
  "format":   "ZUGFeRD 2.x",
  "profile":  "EN16931",
  "container": "pdf",
  "syntax":   "CII",
  "summary":  "Keine Probleme gefunden",
  "errors":   [],
  "warnings": [],
  "notices":  [],
  "metadata": {
    "invoice_number": "RE-2026-001",
    "issue_date":     "2026-04-01",
    "seller":         "Musterfirma GmbH",
    "buyer":          "Beispiel AG",
    "total_amount":   "1190.00",
    "currency":       "EUR"
  }
}
{
  "valid":   false,
  "format":  "XRechnung 3.0",
  "profile": "XRECHNUNG",
  "summary": "1 Fehler, 1 Warnung gefunden",
  "errors": [
    {
      "severity":       "error",
      "rule_id":        "BR-DE-15",
      "field":          "BT-10",
      "message":        "Die Käuferreferenz / Leitweg-ID (BT-10) muss übermittelt werden.",
      "fix_suggestion": "Füge die Käuferreferenz / Leitweg-ID (BT-10) in 'ram:ApplicableHeaderTradeAgreement/ram:BuyerReference' ein.",
      "xpath":          "//ram:ApplicableHeaderTradeAgreement/ram:BuyerReference"
    }
  ],
  "warnings": [
    {
      "severity":       "warning",
      "rule_id":        "BR-DE-17",
      "field":          "BT-3",
      "message":        "Mit dem Element \"Invoice type code\" (BT-3) sollen ausschließlich folgende Codes aus der Codeliste UNTDID 1001 übermittelt werden: 326, 380, 384, 389 …",
      "fix_suggestion": "Bei einer Vorschussrechnung (386) ist die Warnung hinnehmbar: XRechnung empfiehlt den Code nicht, lässt ihn aber zu. Wer sie vermeiden will, nimmt 326 (Teilrechnung).",
      "xpath":          "//rsm:ExchangedDocument/ram:TypeCode"
    }
  ]
}

Ausgabefelder

FeldTypPflichtBeschreibung
validbooleanJatrue wenn kein einziger Fehler gefunden wurde
not_an_einvoicebooleanNeintrue, wenn die Datei gar keine erkannte E-Rechnung ist, etwa eine PDF ohne eingebettetes XML. Dann ist valid false, und der einzige Fehler heißt NO_EMBEDDED_XML oder NOT_AN_EINVOICE
formatstringNeinErkanntes Format: "ZUGFeRD 2.x" oder "ZUGFeRD 1.0" bei PDF, sonst etwa "XRechnung 3.0", "Peppol BIS Billing 3.0" oder "EN 16931"; fehlt, wenn die Datei keine erkannte E-Rechnung ist
profilestringNeinErkanntes Profil in Großbuchstaben, etwa "EN16931", "EXTENDED" oder "XRECHNUNG"; fehlt, wenn die Datei keine erkannte E-Rechnung ist
containerstringNeinpdf (PDF mit eingebetteter E-Rechnung) oder xml (reine XML-Datei)
syntaxstringNeinCII oder UBL
summarystringJaLesbare Zusammenfassung, z.B. "2 Fehler, 1 Warnung gefunden"
errorsarrayJaValidierungsfehler — machen die Rechnung ungültig
warningsarrayJaWarnungen — Rechnung kann trotzdem gültig sein
noticesarrayNeinInformative Hinweise ohne Einfluss auf die Gültigkeit
metadataobjectNeinExtrahierte Rechnungsdaten — bei PDFs vorhanden wenn eingebettetes XML erfolgreich gelesen wurde, bei file_type: "xml" immer
extract_xml TOOL

Extrahiert das eingebettete XML-Dokument aus einer ZUGFeRD/Factur-X-PDF. Gibt den vollständigen XML-Inhalt als String zurück, zusammen mit erkanntem Format und Profil.

Input

pdf_content * string Base64-kodierter Inhalt der ZUGFeRD/Factur-X-PDF-Datei.

Output

{
  "xml_content": "<?xml version='1.0' encoding='UTF-8'?>\n<rsm:CrossIndustryInvoice ...>...</rsm:CrossIndustryInvoice>",
  "format":      "ZUGFeRD 2.x",
  "profile":     "EN16931"
}
Das extrahierte xml_content kann direkt (nach Base64-Kodierung) an validate_invoice mit file_type: "xml" weitergegeben werden.
check_consistency TOOL

Prüft, ob die für Menschen sichtbaren Daten im PDF (Rechnungsnummer, Datum, Gesamtbetrag) mit den im XML-Anhang gespeicherten Maschinendaten übereinstimmen. Deckt Manipulationen oder Erfassungsfehler auf.

Input

pdf_content * string Base64-kodierter Inhalt der ZUGFeRD/Factur-X-PDF-Datei.

Output

{
  "consistent":    true,
  "discrepancies": []
}
{
  "consistent":    false,
  "discrepancies": [
    {
      "field":     "total_amount",
      "pdf_value": "1190.00",
      "xml_value": "1180.00",
      "message":   "Gesamtbetrag im PDF ('1190.00') unterscheidet sich von dem im XML ('1180.00')."
    }
  ]
}

Geprüfte Felder & Toleranzen

FeldErkennungsmuster im PDFToleranz
invoice_numberRechnungsnummer:, Rechnung Nr., Invoice No.Groß-/Kleinschreibung, Leerzeichen, Bindestriche ignoriert
issue_dateRechnungsdatum:, Datum:, Invoice Date:Keiner — Datumformate werden auf ISO 8601 normiert
total_amountGesamtbetrag:, Rechnungsbetrag:, Total:±0,01 EUR (Rundungsdifferenzen)
Die Texterkennung basiert auf dem maschinenlesbaren PDF-Text. Gescannte PDFs ohne OCR-Schicht werden nicht unterstützt.
create_invoice TOOL

Erzeugt aus strukturierten Daten eine gültige E-Rechnung, wahlweise als ZUGFeRD-PDF (PDF/A-3 mit eingebettetem EN-16931-CII-XML) oder als reine XRechnung-XML (CII mit XRechnung-3.0-CIUS und Leitweg-ID für B2G). Unterstützt mehrere Positionen, die USt-Kategorien S, Z, E, AE, K und G, durchlaufende Posten, Rabatte und Zuschläge, Leistungsdatum oder Leistungszeitraum, schon gezahlte Beträge, Bezüge auf frühere Rechnungen, Anlagen sowie Überweisung oder SEPA-Lastschrift. Zustandslos: Wir speichern nichts, auch keine Anlagen. REST: POST /v1/create. Beträge in Cent, Datum als YYYY-MM-DD.

Input (Auszug)

format *stringzugferd (PDF) oder xrechnung (XML)
document_typestringRechnungsart (BT-3), Standard invoice: credit_note (Rechnungskorrektur, Storno; Beträge positiv, die PDF nennt dann keinen Zahlbetrag), corrected (korrigierte Rechnung), self_billed (Gutschriftverfahren), prepayment (Vorschuss- oder Anzahlungsrechnung; XRechnung warnt bei 386 mit BR-DE-17, die Rechnung bleibt gültig), partial (Teilrechnung), debit_note (Belastungsanzeige). Roh über type_code.
preceding_invoicesarrayBezüge auf frühere Rechnungen (BG-3), etwa Vorschussrechnungen: [{ number, date }], höchstens 20. Ins XML kommt in beiden Formaten nur der erste als BG-3, denn das Schema der EN 16931 erlaubt nur einen; alle weiteren nennt der Hinweis (BT-22).
preceding_invoiceobjectKurzform für einen einzigen Bezug { number, date } (BT-25/26), etwa bei einer Rechnungskorrektur. Nicht zusammen mit preceding_invoices.
number, date *stringBelegnummer und -datum (YYYY-MM-DD)
sellerobjectVerkäufer (name, street, zip, city, country, vat_id, tax_number, email, phone, contact_name). Pflicht ohne Absenderprofil; fehlt seller, setzen wir das Absenderprofil deines Kontos ein.
seller.tax_numberstringSteuernummer (BT-32), wenn keine USt-IdNr. vorliegt. Ohne USt-IdNr. schreiben wir die Steuernummer zusätzlich als Kennung des Verkäufers (BT-29), denn EN 16931 verlangt eine solche Kennung (BR-CO-26).
buyer *objectKäufer (name, street, zip, city, country, vat_id, email …)
lines *arrayPositionen: name, quantity, unit_net_price_cents, vat_category, vat_rate, pass_through. vat_category: Standard S; Z = 0 %, E = befreit, AE = Reverse Charge, K = innergemeinschaftliche Lieferung, G = Ausfuhr.
lines[].pass_throughbooleantrue = durchlaufender Posten (§ 10 Abs. 1 UStG): ohne Umsatzsteuer, im XML Kategorie E mit Grund und Code VATEX-EU-79-C. Dazu vat_rate: 0, vat_category lässt du weg oder setzt E. Geht nicht zusammen mit anderen Positionen der Kategorie E (BR-E-01). Im XML zählt der Posten zum Netto (BT-109), aber nicht zur Umsatzsteuer; so verlangt es EN 16931. Zeigt deine eigene Rechnung die Summe netto ohne ihn, weichen die beiden Zahlen ab; der Gesamtbetrag stimmt.
delivery_date, billing_periodstring, objectLeistungsdatum (BT-72) oder Leistungszeitraum (BG-14), der Zeitraum als { start, end }, das Ende nicht vor dem Anfang. Mit Zeitraum und ohne Leistungsdatum setzen wir BT-72 auf das Ende des Zeitraums. Bei einer Vorschussrechnung (prepayment) ohne beides lassen wir BT-72 weg; die ZUGFeRD-Prüfung warnt dann (PEPPOL-EN16931-R008), das ist richtig. Sonst gilt ohne beides das Rechnungsdatum.
prepaid_amount_cents, prepaid_vat_centsintegerSchon gezahlt, etwa ein Vorschuss, brutto in Cent (BT-113). Wir ziehen es vom Zahlbetrag ab (BR-CO-16). Höher als der Gesamtbetrag geht nicht. Dazu schreiben wir einen Hinweis (BT-22) mit dem Abzug und den Bezügen; mit prepaid_vat_cents nennt er auch Entgelt und Steuer des Abzugs, wie § 14 Abs. 5 Satz 2 UStG es für Schlussrechnungen verlangt.
allowances_chargesarrayRabatte/Zuschläge auf Dokumentebene
buyer_referencestringLeitweg-ID (BT-10) — bei xrechnung Pflicht
payment_terms, due_date, payment_meansstring/objectZahlungsbedingungen, Fälligkeit, IBAN/BIC
direct_debitobjectSEPA-Lastschrift: mandate_reference (BT-89), creditor_id (BT-90, sonst aus dem Absenderprofil), debtor_iban (BT-91). Setzt die Zahlungsart 59. Bei ZUGFeRD darf die IBAN gekürzt sein (DE89**************3000), bei XRechnung nicht. Ein Empfängerkonto schreiben wir dann nicht (BR-DE-25-b). Schickst du direct_debit, brauchen wir alle drei Angaben; payment_means.type_code lässt du dann weg oder setzt 59.
attachmentsarrayAnlagen (BG-24): [{ filename, content_base64, description? }]. Erlaubt sind PDF, PNG, JPEG, CSV, XLSX und ODS (BR-CL-24), höchstens 10 Dateien, zusammen höchstens 5 MB. Dateinamen müssen eindeutig sein. Ab zwei Anlagen meldet die Prüfung die Warnung CII-SR-476 (bei zwei Beschreibungen auch CII-SR-475); die Regel zählt alle Anlagen zusammen, die Rechnung bleibt gültig. Über MCP enthält die Antwort einer ZUGFeRD-Rechnung mit Anlagen nur pdf_base64; das XML steckt in der PDF.
seller_logostringLogo Base64 (PNG/JPG) — nur Automatisieren/Skalieren
self_checkbooleanSofort gegen EN 16931 prüfen (langsamer). Standard false.

Output

{
  "format":     "zugferd",
  "profile":    "EN16931",
  "pdf_base64": "JVBERi0xLjc…",
  "xml":        "<?xml version=\"1.0\"…",
  "branding":   "free_footer"
}

xml kommt immer mit, außer über MCP bei einer ZUGFeRD-Rechnung mit Anlagen. Dann holt extract_xml es aus der PDF.

Anlagen

Anlagen stehen nur im XML der E-Rechnung (BT-125), nicht zusätzlich als Anhang der PDF. Jede Datei hat höchstens 5 MB, alle zusammen ebenfalls höchstens 5 MB, gemessen nach dem Dekodieren. Den Typ lesen wir an der Endung (.pdf, .png, .jpg, .jpeg, .csv, .xlsx, .ods) und prüfen ihn an den ersten Bytes der Datei; passen sie nicht zusammen, kommt 400. Ein Dateiname hat bis zu 100 Zeichen, ohne / und \ und ohne Punkt am Anfang; zwei Namen, die sich nur in Groß- und Kleinschreibung unterscheiden, gelten als gleich. description (BT-123) hat höchstens 200 Zeichen. In content_base64 steht reines Base64; einen Vorsatz data:…;base64, schneiden wir ab. Base64 macht die Dateien um ein Drittel größer: 5 MB Anlagen ergeben rund 7 MB im Aufruf, und die Antwort trägt sie noch einmal in der Rechnung. Über MCP schickst du deshalb besser nur kleine Anlagen.

Absenderprofil

Was im Aufruf fehlt, ergänzen wir aus dem Absenderprofil deines Kontos: den ganzen Verkäufer, wenn seller fehlt (die Steuernummer daraus nur, wenn das Profil keine USt-IdNr. hat), dazu IBAN, BIC, Kontoinhaber, Zahlungsbedingungen, Logo und bei einer Lastschrift die Gläubiger-ID. Was im Aufruf steht, gilt immer. Du pflegst das Profil im Konto unter „Absender“ oder über die API: GET /v1/keys/issuer liest es, PUT /v1/keys/issuer speichert es, DELETE /v1/keys/issuer löscht es. Felder: name, street, zip, city, country, vat_id, tax_number, email, phone, contact_name, iban, bic, account_name, payment_terms, logo_b64, creditor_id. PUT ändert nur die Felder, die im Aufruf stehen; eine leere Zeichenkette oder null löscht ein Feld. Eine USt-IdNr. ohne Ländercode, eine Steuernummer in falscher Form und eine ungültige Gläubiger-ID lehnt PUT mit 400 ab.

Fehler

Ungültige Eingaben beantworten wir mit 400 und { "error": "Ungültige Eingabe", "details": "…" }. Fehler im Schema nennen Pfad und Grund, mehrere durch Semikolon getrennt. Inhaltliche Probleme, etwa eine Lastschrift ohne Mandatsreferenz, stehen als ganze Sätze in details, mehrere durch Leerzeichen getrennt. Ein Datum, das nicht die Form YYYY-MM-DD hat, nennt sein Feld, etwa preceding_invoices[0].date (gezählt ab 0). Über MCP kommt bei inhaltlichen Problemen und beim Datum dasselbe Objekt als Ergebnis. Fehler im Schema meldet über MCP schon das Protokoll: Du bekommst einen Werkzeugfehler (isError: true) mit dem Text der Schemaprüfung, kein Objekt mit error und details.

Self-Check entkoppelt: Standardmäßig kommt die Rechnung sofort zurück. Den Konformitätsnachweis holst du im zweiten Schritt über validate_invoice auf das zurückgegebene pdf_base64 bzw. xml, oder du setzt self_check: true für einen einzigen Roundtrip. Branding (Footer im Prüfen-Tarif, White-Label ab Automatisieren) bestimmt der Server aus dem Tarif. Eigenes Kontingent fürs Erstellen: Prüfen 1 Rechnung im Monat; Automatisieren und Skalieren unbegrenzt, Fair Use über das Stundenlimit.
Sandbox-Key (zv_test_…, über GET /v1/keys/me): zum Testen der Rechnungserstellung. Erstellte Rechnungen tragen ein gekacheltes „DEMO"-Wasserzeichen auf jeder Seite plus XML-Marker — voll testbar, aber als echte Rechnung wertlos. 100/Tag, 3/Minute, getrennt vom Live-Kontingent. Prüfungen über den Sandbox-Key zählen dagegen auf dein normales Wochenkontingent: Sie rechnen dasselbe und liefern dasselbe Ergebnis.
POST /v1/convert REST

Macht aus einer bestehenden Rechnung als PDF, Word oder Excel eine E-Rechnung: ZUGFeRD in deinem bisherigen Layout oder XRechnung. Ein Sprachmodell auf unserem eigenen Server liest die Werte aus, wir rechnen sie nach und legen sie als Entwurf vor. Die E-Rechnung entsteht erst, wenn du den Entwurf bestätigst, und kommt nur zurück, wenn sie unsere Prüfung gegen EN 16931 besteht. In Automatisieren sind 20 Umwandlungen im Monat enthalten, in Skalieren 100, in Enterprise und im Testzugang so viele wie vereinbart. Darüber kostet jede weitere 3,99 € zzgl. USt mit accept_overage: true. Im Testzugang gibt es keine Mehrnutzung; ist das Kontingent aufgebraucht, antwortet /confirm mit 402 ohne overage_required. Ohne Tarif kostet jede 3,99 € zzgl. USt, bezahlt wird erst eine Datei, die die Prüfung bestanden hat. Ein vereinbartes Kontingent gilt auch im kostenlosen Tarif: Dann kommt die Datei sofort, wie mit Tarif. Ist es aufgebraucht, gibt es dort weder Mehrnutzung noch Einzelkauf: /confirm antwortet mit 402 ohne overage_required und ohne payment_required. Umwandlungen zählen nicht auf das Prüf- oder Erstellkontingent.

Ablauf

  1. POST /v1/convert mit einer oder mehreren Dateien. Antwort sofort mit 202 und je Datei einem Auftrag im Status queued.
  2. GET /v1/convert/{id} abfragen, bis der Status draft ist, meist nach ein bis drei Minuten je Rechnung; mehrere lesen wir nacheinander. Bei mehreren Dateien oder längerer Erkennung kommt zusätzlich eine E-Mail an die Adresse des Kontos.
  3. Den Entwurf prüfen. Was in uncertain steht, sollte ein Mensch ansehen; reasons sagt je Feld, warum.
  4. POST /v1/convert/{id}/confirm mit den bestätigten Werten. Die Antwort kann länger dauern: Lass dein Programm bis zu drei Minuten warten. Mit Tarif kommt die fertige Datei als Base64 zurück, danach sind Upload und Entwurf gelöscht. Ohne Tarif erzeugen und prüfen wir sie ebenso, antworten aber mit 402 und payment_required: true; der Auftrag steht dann auf awaiting_payment.
  5. Nur ohne Tarif: POST /v1/convert/{id}/checkout gibt die Adresse der Zahlung zurück. Nach der Zahlung holt GET /v1/convert/{id}/file die Datei.

Voraussetzungen

X-Api-KeyHeaderLive-Key, auch im kostenlosen Tarif. Sandbox-Keys werden mit 403 abgelehnt. Ohne Umwandlungen im Monat, etwa im kostenlosen Tarif ohne vereinbartes Kontingent, höchstens 5 Entwürfe am Tag.
ohne SchlüsselGastGastmodus wie auf der Website. Die erste Antwort enthält guest_token; es geht danach als Header X-Guest-Token mit. Je Internetanschluss eine offene Rechnung zur Zeit (sonst 429 mit guest_limit), höchstens drei am Tag ohne Kauf (sonst 429 mit guest_daily_limit). replace: "{id}" im Upload ersetzt die eigene offene Rechnung. Für die Anbindung an eigene Software ist ein kostenloser Schlüssel der bessere Weg.
AbsenderprofilKontoAls Verkäufer steht deine Firma auf der E-Rechnung. Ist dein Absenderprofil vollständig (Name, Straße, PLZ, Ort), kommt er von dort; du pflegst es im Konto unter „Absender“. Sonst ist im Entwurf seller_from_invoice: true, und seller enthält den Absender zum Prüfen: vorbelegt aus der Rechnungsanschrift deines Kontos, sonst aus der Rechnung gelesen. Bei der Bestätigung schickst du ihn als seller mit (name, street, postal_code, city, dazu vat_id oder tax_number). Pflicht ist das nur, wenn im Konto weder ein vollständiges Absenderprofil noch eine vollständige Rechnungsanschrift steht. Für XRechnung braucht der Absender dazu Ansprechpartner, Telefon und E-Mail, bei Überweisung auch die IBAN: im Absenderprofil, wenn er von dort kommt, sonst im seller (contact, phone, email, iban). Bei einer Lastschrift (direct_debit) entfällt die IBAN. Kommt der Absender aus deinem seller, gehört own_invoice: true dazu: Damit bestätigst du, dass das deine Firma ist. Kommt er aus dem Absenderprofil oder der Rechnungsanschrift, brauchst du own_invoice: true nur, wenn er nicht auf der Rechnung steht.

Hochladen: POST /v1/convert

files *arraySo viele Dateien je Aufruf, wie dein Kontingent im Monat hat (Automatisieren 20, Skalieren 100, Enterprise und Testzugang wie vereinbart), ohne Kontingent eine; je höchstens 10 MB: [{ name, content }], content als Base64. Erlaubt sind PDF, DOCX, XLSX, ODT, ODS, DOC und XLS. Gescannte Rechnungen werden mit Status rejected abgelehnt. Alle Dateien einer Anfrage zusammen dürfen rund 35 MB groß sein: Base64 macht sie um ein Drittel größer, und eine Anfrage darf als JSON höchstens 50 MB haben, sonst kommt 413. Mehr Dateien verteilst du auf mehrere Aufrufe.
{
  "batch": "lomvuvzjtl5y94syeglx",
  "jobs": [
    { "id": "5113py49uqxfatik47rn", "filename": "RE-2026-121.pdf", "format": "pdf",
      "status": "queued", "reason": null,
      "created_at": "2026-10-12T11:17:58.000Z", "expires_at": "2026-10-13T11:17:58.000Z" }
  ],
  "note": "Sobald ein Entwurf zur Prüfung bereitliegt, hat er den Status „draft“. …"
}

Status eines Auftrags: queued, processing, draft, awaiting_payment (erzeugt und geprüft, wartet auf die Zahlung; dazu payment: { price_cents, paid }), done, rejected (Datei nicht umwandelbar, Grund in reason), failed. batch ist bei einer einzelnen Datei null.

Aufträge und Kontingent: GET /v1/convert

{
  "jobs": [ … ],
  "quota": { "limit": 20, "used": 11, "overage_cents": 399, "overage_possible": true,
             "trial_until": null, "individual": false },
  "blocked_reason": null,
  "batch_limit": 20,
  "pays_per_conversion": false,
  "price_cents": 399,
  "seller": { "source": "billing_details", "name": "Holzbau Weber GmbH", "street": "Lindenweg 3",
              "postal_code": "72070", "city": "Tübingen", "country": "DE", "vat_id": "DE123456789",
              "tax_number": null, "profile_incomplete": false }
}

Gezählt wird je Kalendermonat, in einem Firmenkonto über die Firma. overage_possible sagt, ob du über das Kontingent hinaus mit accept_overage weiter umwandeln kannst; im Testzugang nie. trial_until ist der letzte Tag eines Testzugangs (YYYY-MM-DD), sonst null. individual ist true, wenn ein vereinbartes Kontingent statt dem des Tarifs gilt. batch_limit sagt, wie viele Dateien ein Aufruf tragen darf. pays_per_conversion ist true, wenn dein Konto keine Umwandlungen im Monat hat, weder im Tarif noch vereinbart; dann kostet jede price_cents. blocked_reason ist bei Sandbox-Keys gesetzt. seller sagt, welcher Absender gilt, bevor es einen Entwurf gibt: Mit source profile ist es dein vollständiges Absenderprofil, und der Entwurf fragt nicht nach ihm. Mit billing_details ist es die Rechnungsanschrift deines Kontos, mit ihr ist der Absender im Entwurf vorbelegt. Mit invoice lesen wir ihn aus der Rechnung, die Anschrift ist dann leer. tax_number ist die Steuernummer aus dem Absenderprofil; die Rechnungsanschrift kennt keine. profile_incomplete ist true, wenn es ein Absenderprofil gibt, dem Straße, PLZ oder Ort fehlen; es zählt erst, wenn es vollständig ist.

Entwurf: GET /v1/convert/{id}

Der Auftrag wie oben, im Status draft zusätzlich mit draft. Beträge in Euro, nicht in Cent. in_progress ist true, solange eine Bestätigung für ihn läuft. GET /v1/convert/{id}/pdf liefert die hochgeladene Rechnung als PDF, solange der Entwurf besteht; danach 404.

invoice_number, issue_datestringRechnungsnummer und -datum (YYYY-MM-DD)
buyer, sellerobjectname, street, postal_code, city, country, vat_id; seller dazu tax_number, email, phone, contact, iban, soweit bekannt. Ist dein Absenderprofil vollständig, kommt der Verkäufer von dort. Sonst ist seller_from_invoice true, und seller ist vorbelegt aus der Rechnungsanschrift deines Kontos oder, ohne sie, aus der Rechnung gelesen.
seller_from_invoice, seller_sourceboolean, stringseller_from_invoice ist true, wenn du den Absender im Entwurf mit bestätigst (Feld seller beim Bestätigen). seller_source sagt, woher er kommt: profile (Absenderprofil), billing_details (Rechnungsanschrift des Kontos) oder invoice (aus der Rechnung gelesen)
linesarraydescription, quantity, unit, unit_code, unit_unknown, unit_price, net_amount, tax_rate, pass_through. unit ist der Text auf der Rechnung, unit_code der Code nach UN/ECE Rec. 20. pass_through ist true bei einem durchlaufenden Posten.
allowances_chargesarrayRabatte und Zuschläge auf die ganze Rechnung: type (allowance oder charge), reason, amount netto
net_total, tax_total, gross_total, tax_ratenumberSummen, wie sie auf der Rechnung stehen. tax_rate ist der Steuersatz der Rechnung; er kann null sein.
tax_exemptionstringBei 0 %: AE (Reverse Charge) oder E (steuerbefreit), wenn der Grund im Text steht, sonst null. Sind alle Positionen mit 0 % durchlaufende Posten, ist es null.
document_type, preceding_invoicesstring, arrayRechnungsart: invoice, prepayment (Vorschussrechnung), partial (Teilrechnung) oder credit_note (Rechnungskorrektur, Storno). Bezüge auf frühere Rechnungen: [{ number, date }], sonst []
delivery_date, billing_periodstring, objectLeistungsdatum oder Leistungszeitraum { start, end }, sonst null
prepaid_amount, prepaid_vat, amount_duenumberSchon gezahlt (brutto), die Umsatzsteuer darin und der Zahlbetrag laut Rechnung, sonst null
direct_debitobjectBei einer Lastschrift { mandate_reference, creditor_id, debtor_iban, debtor_iban_masked }, sonst null. debtor_iban_masked ist true, wenn die IBAN deines Kunden auf der Rechnung gekürzt steht: Für ZUGFeRD reicht das, für XRechnung brauchst du sie ganz.
checksobjecttotals_add_up, totals_in_text, lines_match_net, buyer_is_not_seller, seller_on_invoice
uncertainarrayFelder, die ein Mensch prüfen sollte: invoice_number, issue_date, net_total, tax_total, gross_total, buyer, lines, units, quantities, tax_exemption, pass_through, seller, seller_details, allowances_charges, document_type, preceding_invoices, billing_period, prepaid_amount, direct_debit
reasonsobjectJe unsicherem Feld ein Satz, warum, etwa "units": "Die Einheit „Fass“ kennen wir nicht …"

Bestätigen: POST /v1/convert/{id}/confirm

format *stringzugferd (deine PDF mit eingebettetem XML) oder xrechnung (nur XML)
invoice_number, issue_date *stringRechnungsnummer und -datum
buyer *objectWie im Entwurf, dazu email (bei XRechnung Pflicht). vat_id ist bei Reverse Charge Pflicht.
lines *arraydescription, quantity, unit_code, unit_price, net_amount, tax_rate, pass_through. Fehlt tax_rate, gilt der Satz der Rechnung, bei pass_through: true 0 %. Ein durchlaufender Posten zählt im XML zum Netto (BT-109), auf deiner PDF oft nicht. Das ist normgerecht; der Gesamtbetrag stimmt in beiden.
net_total, tax_total, gross_total, tax_rate *numberDie Summen müssen centgenau zu den Positionen passen, sonst 422 mit problems. tax_rate gilt für Positionen ohne eigenen Satz; fehlt er, nennt missing ihn. War er im Entwurf null, nimm den höchsten Satz der Positionen, so macht es die Website.
tax_exemptionstringPflicht, sobald eine Position 0 % hat, die kein durchlaufender Posten ist: AE oder E
allowances_chargesarrayRabatte und Zuschläge; wir verteilen sie anteilig auf die Steuersätze
delivery_date, buyer_reference, payment_termsstringLeistungsdatum, Leitweg-ID (BT-10), Zahlungsbedingungen
billing_periodobjectLeistungszeitraum { start, end }, beide als YYYY-MM-DD. Ohne Leistungsdatum gilt das Ende als Leistungsdatum (BT-72).
document_type, preceding_invoicesstring, arrayWie im Entwurf. Eine Rechnungskorrektur (credit_note) braucht einen Bezug, und ihre Beträge trägst du positiv ein. Ins XML kommt nur der erste Bezug, alle weiteren nennt der Hinweis (BT-22).
prepaid_amount, prepaid_vat, amount_duenumberIn Euro. prepaid_amount (schon gezahlt, brutto) ziehen wir vom Zahlbetrag ab (BT-113); prepaid_vat nennt der Hinweis zum Abzug. amount_due gleichen wir mit Gesamtbetrag minus schon gezahlt ab; ins XML kommt es nicht.
direct_debitobjectSEPA-Lastschrift (Zahlungsart 59): { mandate_reference, creditor_id, debtor_iban }. Fehlt creditor_id, nehmen wir die Gläubiger-ID aus deinem Absenderprofil, wenn es vollständig ist. Die IBAN deines Kunden darf bei ZUGFeRD gekürzt sein, bei XRechnung nicht. Ohne direct_debit gilt Überweisung.
attachmentsarrayAnlagen für die E-Rechnung (BG-24): [{ filename, content_base64 }], Grenzen wie bei create_invoice. Eine Beschreibung schreiben wir beim Umwandeln nicht ins XML. Anlagen gehen nur mit der Bestätigung mit, nie mit dem Upload. Den Sammeldownload baut die Website im Browser; über die API holst du jede Datei einzeln. Kamen Anlagen mit, nennt note sie mit.
sellerobjectname, street, postal_code, city, dazu vat_id oder tax_number und country, email, phone, contact, iban. Nicht nötig, wenn im Konto ein vollständiges Absenderprofil (Name, Straße, PLZ, Ort) oder eine vollständige Rechnungsanschrift steht, sonst Pflicht. Bei XRechnung sind contact, phone und email Pflicht, bei Überweisung auch iban; ohne vollständiges Absenderprofil brauchst du seller dann immer, denn in der Rechnungsanschrift stehen sie nicht.
save_profilebooleanStandard true: Ohne vollständiges Absenderprofil übernehmen wir den bestätigten seller in dein Profil. Das geschieht, sobald die E-Rechnung die Prüfung besteht, ohne Tarif also schon vor dem Kauf. Er ersetzt Name, Anschrift, USt-IdNr., Steuernummer, Kontakt und IBAN, bei einer Lastschrift auch die Gläubiger-ID aus direct_debit.creditor_id; was er nicht mitbringt, bleibt, ebenso Logo und Zahlungsbedingungen. Kommt eine andere IBAN, leeren wir BIC und Kontoinhaber. Mit false bleibt das Profil, wie es ist
own_invoicebooleanBestätigt, dass die Rechnung von dir stammt. Kommt der Absender aus seller, immer nötig; kommt er aus dem Absenderprofil oder der Rechnungsanschrift, nur, wenn er nicht auf der Rechnung steht. Fehlt es, kommt 422 mit own_invoice_required: true
accept_overagebooleanBestätigt die kostenpflichtige Mehrnutzung, wenn das Monatskontingent aufgebraucht ist, sonst 402 mit overage_required: true
{
  "ok":       true,
  "filename": "RE-2026-121.pdf",
  "mime":     "application/pdf",
  "content":  "JVBERi0xLjc…",
  "note":     "Die E-Rechnung ist formal gültig und von dir bestätigt. …"
}

Eine Bestätigung dauert meist nur Sekunden, kann aber länger dauern, denn wir erzeugen und prüfen die E-Rechnung dabei. Lass dein Programm bis zu drei Minuten auf die Antwort warten. Bricht die Verbindung vorher ab, schick dieselbe Bestätigung noch einmal: Wir zählen die Umwandlung und löschen Upload und Entwurf erst, wenn die Antwort hinausging. Doppelt zählt nichts. Kommt darauf 409 mit in_progress: true, läuft die erste Bestätigung noch. Warte ein paar Sekunden und frag mit GET /v1/convert/{id} den Stand ab. Steht dort in_progress: true, läuft sie noch, und du wartest weiter. Sonst gilt der Status: Bei draft schickst du die Bestätigung noch einmal, bei awaiting_payment geht es mit dem Kauf weiter. Bei done ist die Umwandlung fertig und gezählt; kam die Datei nicht bei dir an, schreib uns.

Ohne Tarif: kaufen und abholen

Ohne Tarif antwortet /confirm mit 402, sobald die Datei erzeugt ist und die Prüfung bestanden hat: { payment_required: true, price_cents: 399, prefill: { email, company, country, vat_id } }. Sie liegt dann bis zu 25 Stunden bereit.

POST /{id}/checkoutobject{ plan, interval, company, country, vat_id, save_profile }. plan: single (diese Umwandlung, 3,99 € zzgl. USt) oder pro (Automatisieren, interval year oder month). Antwort { url }, die Seite der Zahlung bei Stripe. Im Gastmodus dazu email; gehört sie schon zu einem Konto, kommt 409 mit login_required: true, und du kaufst angemeldet in deinem Konto
GET /{id}/purchasequeryNach der Zahlung mit ?session_id=…: { paid, plan }
GET /{id}/fileDie bezahlte Datei: { filename, mime, content, note }. Bezahlt liegt sie sieben Tage bereit; nach dem ersten Abholen noch eine Stunde, falls der Download nicht ankam

Fehler

400missingPflichtangaben fehlen; missing nennt sie, etwa seller.vat_id oder seller.tax_number. Kommt ein Feld in falscher Form, etwa prepaid_amount als Text, steht es auch dort
402quotaMonatskontingent aufgebraucht. Mit overage_required: true per accept_overage bestätigen; ohne dieses Feld ist Mehrnutzung für das Konto nicht eingerichtet oder, wie im Testzugang, ausgeschlossen, und details sagt, wie es weitergeht. Ohne Tarif: payment_required: true, die Datei ist fertig und wartet auf den Kauf
404Auftrag unbekannt, oder seine Frist ist abgelaufen und er ist gelöscht; /pdf auch, sobald die hochgeladene Datei weg ist
409in_progressAuftrag noch nicht im Status draft, oder schon umgewandelt. Mit in_progress: true läuft gerade eine Bestätigung für ihn; Verwerfen und Ersetzen warten, bis sie fertig ist. Mit paid: true ist die Umwandlung bezahlt, hol die Datei mit /file ab
410expiredMit expired: true ist der Entwurf abgelaufen, aber noch nicht gelöscht. Sonst ist die Datei weg, oder die Zahlung passt nicht mehr in die Frist. Dann lädst du die Rechnung neu hoch. Bei /file heißt 410: Die bezahlte Datei ist nicht mehr da. Schreib uns, wir helfen dir weiter.
413Anfrage größer als 50 MB; Dateien auf mehrere Aufrufe verteilen
422problemsSummen passen nicht, eine Pflichtangabe der Norm fehlt oder die Prüfung schlägt fehl; problems erklärt es auf Deutsch
429Ohne Umwandlungen im Monat, etwa im kostenlosen Tarif ohne vereinbartes Kontingent, sind 5 Entwürfe am Tag erreicht

Ist eine Frist abgelaufen, löschen wir Auftrag und Datei, spätestens zehn Minuten danach. Bis dahin antwortet /confirm mit 410 und expired: true, ab dann jeder Aufruf mit 404, wie bei einem unbekannten Auftrag.

Preise: Ohne Tarif kostet jede Umwandlung 3,99 € zzgl. USt, bezahlt beim Abholen. Mit Tarif kostet jede Umwandlung über dem Monatskontingent 3,99 € zzgl. USt und kommt auf die nächste Rechnung. Gezählt wird erst bei der erfolgreichen Bestätigung; ein Upload, aus dem nichts wird, kostet nichts. DELETE /v1/convert/{id} verwirft einen Auftrag und löscht die Datei sofort.
Datenschutz: Hochgeladene Dateien, gelesener Text und Entwurf liegen verschlüsselt (AES-256) auf unserem Server in Deutschland, bis die fertige E-Rechnung an dich hinausging oder du verwirfst, höchstens 24 Stunden nach dem Hochladen. Bricht die Verbindung vorher ab, bleibt der Entwurf für einen neuen Versuch. Wartet eine fertige Datei auf die Zahlung, höchstens 25 Stunden ab dem Erzeugen; bezahlt bis zum Abholen, höchstens sieben Tage. Die Erkennung läuft auf demselben Server; die Inhalte gehen an keinen weiteren Dienst. Anlagen kommen erst mit der Bestätigung; wir legen sie nie einzeln ab, sie stecken in der fertigen E-Rechnung und haben deren Fristen.
POST /v1/stapel REST

Stapel-Prüfung auf dem Server. Für viele Rechnungen auf einmal, ohne dass dein Programm oder Browser die ganze Zeit verbunden bleibt: Dateien hochladen, starten, später die Ergebnisse abholen. Ab Automatisieren. Die Rechnungen liegen verschlüsselt auf unserem Server in Deutschland, bis sie geprüft sind, und werden direkt danach gelöscht. Die Ergebnisse bleiben sieben Tage, danach löschen wir sie. Wenn alles fertig ist, kommt eine E-Mail an die Adresse des Kontos. In einem Firmenkonto kann der Inhaber das für die anderen Zugänge abschalten (PATCH /v1/org/settings mit { "stapel_ablage": false }).

Aufrufe

POST /v1/stapelAuftrag anlegen. Body { sprache?: "de" | "en" | "fr" } (Sprache der Mail). Antwort 201 mit { id, max_dateien, max_bytes }. Höchstens 3 offene Aufträge je Schlüssel.
POST /v1/stapel/{id}/dateienEine Datei: { name, file_type: "pdf" | "xml", file_content } (Base64, höchstens 15 MB). Höchstens 500 Dateien je Auftrag.
POST /v1/stapel/{id}/startHochladen fertig, die Prüfung beginnt. Ein nicht gestarteter Auftrag wird nach zwei Stunden gelöscht.
GET /v1/stapel/{id}?seit=…Stand: status (hochladen, wartend, fertig, angehalten), anzahl, fertig, ok, mit_fehlern, neu, grund, ablauf. In ergebnisse stehen die Prüfergebnisse wie bei validate_invoice, mit nr und reference (Dateiname). Mit seit=0 kommen alle und die Liste namen; danach seit = der zuletzt gelieferte stand, dann kommen nur neue (das letzte kann doppelt kommen).
GET /v1/stapelEigene Aufträge.
DELETE /v1/stapel/{id}Sofort löschen, samt allen Dateien und Ergebnissen.

Jede Datei zählt aufs Wochenkontingent. Ist es aufgebraucht, hält der Auftrag an (status: "angehalten", grund: "kontingent"); die übrigen Dateien bleiben ungeprüft und werden gelöscht. Der Server prüft solche Aufträge nur auf Plätzen, die gerade frei sind; wer direkt prüft, geht vor.

GET /v1/invoices REST

Listet die Rechnungen, die wir dir gestellt haben, mit Nummer, Datum und Beträgen. GET /v1/invoices/{nummer}/pdf liefert das Dokument als PDF. Die Rechnungen sind selbst gültige E-Rechnungen im ZUGFeRD-Format nach EN 16931, deine Buchhaltung kann sie also einlesen. Dieselbe Liste steht im Konto unter „Rechnungen". Der Aufruf kostet kein Kontingent.

Anmeldung

X-Api-Key *HeaderDein API-Key. Alternativ als ?api_key=… an die Adresse.

Beispiel

curl -H "X-Api-Key: dein_key" https://api.zugferd-validator.de/v1/invoices

curl -H "X-Api-Key: dein_key" \
  -o ZF-2026-007.pdf \
  https://api.zugferd-validator.de/v1/invoices/ZF-2026-007/pdf

Antwort

{
  "invoices": [
    { "number": "ZF-2026-007", "date": "2026-08-17T09:12:03.000Z",
      "net": 2000, "vat": 380, "gross": 2380, "currency": "eur" }
  ]
}

Beträge in Cent. Es werden nur eigene Rechnungen ausgeliefert; die Nummer eines anderen Kontos beantworten wir mit 404.

validation_report TOOL

Liefert das Prüfergebnis als Validierungsbericht im Format PDF/A-3b, zum Ablegen und Weitergeben. Der Bericht nennt das Ergebnis, alle Meldungen mit Regel-ID und Feld, den Prüfzeitpunkt, das erkannte Format samt Profil, die SHA-256-Prüfsumme der geprüften Datei und den Regelstand, also die Versionen der eingesetzten Prüfwerkzeuge. Jeder Aufruf prüft die Datei neu und zählt wie eine Validierung gegen das Wochenkontingent. Als MCP-Werkzeug validation_report und über REST als POST /v1/report.

Input

file_content *stringBase64-Inhalt der Rechnung
file_type *stringpdf oder xml
attach_sourcebooleanGeprüfte Rechnung sichtbar anhängen: Bei einer PDF kommen ihre Seiten hinten an den Bericht, bei einer XML wird der Inhalt abgedruckt. Zusätzlich liegt die Datei unverändert eingebettet bei (PDF/A-3, AFRelationship /Source), damit sie maschinell lesbar bleibt. Standard false.
langstringde, en, fr — Standard de
file_namestringDateiname für den Bericht. Ohne Angabe rechnung.pdf bzw. rechnung.xml.

Output

Content-Type: application/pdf
Content-Disposition: attachment; filename="validierungsbericht-rechnung.pdf"
X-Validation-Result: valid | invalid

Beispiel

curl -X POST https://api.zugferd-validator.de/v1/report \
  -H "X-Api-Key: $ZV_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"file_content\": \"$(base64 -w0 rechnung.pdf)\", \"file_type\": \"pdf\", \"attach_source\": true}" \
  -o bericht.pdf
Es wird nichts gespeichert. Die Datei wird für den Aufruf geprüft und danach verworfen; damit Bericht und archivierte Rechnung später zusammenfinden, steht die SHA-256-Prüfsumme im Bericht. Der Bericht ist keine rechtsverbindliche Bescheinigung, sondern dokumentiert eine technische Prüfung zu einem Zeitpunkt gegen einen benannten Regelstand, und er ersetzt nicht die Aufbewahrung der Rechnung im Originalformat.
Ab dem Tarif Automatisieren, Sandbox-Keys eingeschlossen. Im kostenlosen Tarif antwortet der Endpunkt mit 403; einen Beispielbericht mit Wasserzeichen gibt es ohne Schlüssel unter GET /v1/report/sample?lang=de|en|fr.

6. Resource: validation_rules

Gibt das aktuelle Regelwerk des Servers zurück: unterstützte Formate, Profile und die verwendete Validierungsengine.

URI: validation://rules

{
  "supported_formats": [
    "ZUGFeRD 1.0", "ZUGFeRD 2.0", "ZUGFeRD 2.1", "ZUGFeRD 2.2",
    "ZUGFeRD 2.3", "ZUGFeRD 2.x / Factur-X 1.0x",
    "XRechnung 1.2", "XRechnung 2.0", "XRechnung 2.3", "XRechnung 3.0"
  ],
  "supported_profiles": [
    "minimum", "basic_wl", "basic", "en16931", "extended", "xrechnung"
  ],
  "validation_engines": [
    "Mustang Project v2.26.0",
    "KoSIT Validierungstool v1.6.3 (XRechnung 3.0.2 + EN 16931 CII)"
  ],
  "last_updated":      "2026-04-08"
}

7. Fehlerbehandlung

Alle Tools geben bei Fehlern ein JSON-Objekt mit einem error-Feld zurück. Über MCP gibt es eine Ausnahme: Passt die Eingabe nicht zum Schema des Tools (falscher Typ, fehlendes Pflichtfeld, Wert nicht in der Liste), meldet das schon das Protokoll als Werkzeugfehler (isError: true) mit dem Text der Schemaprüfung, nicht als JSON-Objekt.

{
  "error":   "Kurze Fehlerkategorie",
  "details": "Ausführliche Beschreibung mit konkretem Hinweis zur Behebung."
}
errorAuslöser
"Fehlender Parameter"Pflichtfeld nicht übergeben
"Ungültiger Parameter"Falscher Wert für file_type
"Ungültiger Base64-Input"Kein gültiger Base64-String übergeben
"Validierungsfehler"Mustang CLI konnte nicht starten (z.B. Java fehlt) oder Timeout nach 30s
"XML-Extraktion fehlgeschlagen"Keine XML-Datei im PDF-Anhang gefunden (bei extract / consistency). Bei validate kommt stattdessen valid: false mit not_an_einvoice: true und dem einzigen Fehler NO_EMBEDDED_XML (Beispiel 5)
"PDF-Lesefehler"PDF kann nicht gelesen werden
"XML-Parsing-Fehler"Extrahiertes XML ist kein valides XML
"Interner Fehler"Unerwarteter Laufzeitfehler
Timeout: Beide Validierungsengines laufen parallel und werden nach jeweils 30 Sekunden abgebrochen. Schlägt Mustang fehl, wird "error": "Validierungsfehler" zurückgegeben. Ein KoSIT-Timeout wird still ignoriert — Mustang-Ergebnisse bleiben erhalten.

8. Gemeinsame Datentypen

ValidationIssue

Wird in den Arrays errors, warnings und notices von validate_invoice verwendet.

FeldTypPflichtBeschreibung
severity"error" | "warning" | "notice"JaSchweregrad des Befundes
rule_idstringJaRegel-ID, z.B. "BR-DE-1", "BR-CL-10". Wert "UNKNOWN" bei XSD-Schema-Verletzungen (KoSIT) oder wenn keine Regel-ID ermittelbar ist
fieldstringNeinBetroffenes BT-Feld, z.B. "BT-10"
messagestringJaFehlermeldung auf Deutsch
fix_suggestionstringNeinKonkreter Korrekturhinweis mit XML-Beispiel
xpathstringNeinXPath zum betroffenen XML-Element

InvoiceMetadata

Optionales Feld im Ergebnis von validate_invoice (nur bei file_type: "xml").

FeldTypBeschreibung
invoice_numberstring?Rechnungsnummer (BT-1)
issue_datestring?Rechnungsdatum ISO 8601, z.B. "2026-04-01"
sellerstring?Name des Verkäufers (BT-27)
buyerstring?Name des Käufers (BT-44)
total_amountstring?Gesamtbetrag als Dezimalstring, z.B. "1190.00"
currencystring?ISO 4217 Währungscode, z.B. "EUR"

9. Unterstützte Formate & Profile

Formate

FormatStandardAnmerkung
ZUGFeRD 1.0ZUGFeRD 1.0 / GEFEGÄltere Version, eingeschränkte Profilunterstützung
ZUGFeRD 2.0FeRD 2.0Erste CII-basierte Version
ZUGFeRD 2.1Factur-X 1.0 (FR)Technisch identisch mit Factur-X 1.0
ZUGFeRD 2.2Factur-X 1.0.05Kleinere Korrekturen
ZUGFeRD 2.3Factur-X 1.0.06Version vor 2.4
ZUGFeRD 2.x / Factur-X 1.0xEN 16931 + D16BAktuelle Hauptversion (2026) — API gibt "ZUGFeRD 2.x" zurück
XRechnung 1.2XRechnung 1.2Ältere XRechnung-Version
XRechnung 2.xXRechnung 2.0–2.3Verschiedene Zwischenversionen
XRechnung 3.0XRechnung 3.0Aktuelle XRechnung-Version

Profile (ZUGFeRD/Factur-X)

Profil-IDAnzeigenameTypischer Einsatz
minimumMINIMUMEinfache maschinelle Verarbeitung
basic_wlBASIC-WLOhne Positionsdetails
basicBASICStandard-Rechnungen
en16931EN 16931 (COMFORT)Öffentliche Auftraggeber, PEPPOL
extendedEXTENDEDKomplexe B2B-Prozesse
xrechnungXRECHNUNGDeutsche öffentliche Verwaltung

10. Regelwerk & Korrekturvorschläge

Der Server enthält eine Datenbank mit Korrekturvorschlägen für 160 Regelkategorien. Fehler beider Validierungsengines (Mustang + KoSIT) werden konsolidiert — wenn eine bekannte Regel-ID gefunden wird, wird der Korrekturvorschlag automatisch angehängt.

rule_id nennt die Regel in Großbuchstaben und ohne führende Null: BR-6 statt BR-06, BR-DE-25-B statt BR-DE-25-b. Teilt XRechnung eine Regel in zwei, steht die ganze Kennung da. Feldkennungen wie BT-89 sind nie eine Regel-ID.

BR-DE-Regeln 32 Regeln

Deutsche Erweiterungen, Pflicht für XRechnung-konforme Rechnungen. Stand XRechnung 3.0.2; Warnungen und Hinweise machen die Rechnung nicht ungültig.

Regel-IDFeldKurzbeschreibung
BR-DE-1BG-16PAYMENT INSTRUCTIONS (BG-16) fehlen
BR-DE-2BG-6SELLER CONTACT (BG-6)
BR-DE-3BT-37Stadt des Verkäufers (BT-37)
BR-DE-4BT-38PLZ des Verkäufers (BT-38)
BR-DE-5BT-41Ansprechpartner des Verkäufers (BT-41)
BR-DE-6BT-42Telefonnummer des Verkäufers (BT-42)
BR-DE-7BT-43E-Mail des Verkäufers (BT-43)
BR-DE-8BT-52Stadt des Käufers (BT-52)
BR-DE-9BT-53PLZ des Käufers (BT-53)
BR-DE-10BT-77Lieferstadt (BT-77)
BR-DE-11BT-78Liefer-PLZ (BT-78)
BR-DE-12–in aktueller XRechnung-Fassung entfernt
BR-DE-13–in aktueller XRechnung-Fassung entfernt
BR-DE-14BT-119USt-Satz (BT-119)
BR-DE-15BT-10Käuferreferenz / Leitweg-ID (BT-10)
BR-DE-16BT-31USt-ID/Steuernummer des Verkäufers
BR-DE-17BT-3Rechnungsart, die XRechnung nicht empfiehlt, etwa 386 (Warnung)
BR-DE-18BT-20Skonto-Angaben (BT-20) im falschen Format
BR-DE-19BT-84IBAN des Empfängerkontos bei Überweisung (58) ungültig (Warnung)
BR-DE-20BT-91IBAN des Zahlers bei Lastschrift (59) ungültig (Warnung)
BR-DE-21BT-24Specification identifier (BT-24) nicht XRechnung-konform (Warnung)
BR-DE-22BT-125Dateinamen der Anlagen nicht eindeutig
BR-DE-23-ABG-17Überweisung (30, 58) ohne Kontoangaben
BR-DE-23-BBT-81Überweisung (30, 58) mit Karte oder Lastschrift
BR-DE-24-ABG-18Kartenzahlung (48, 54, 55) ohne Kartenangaben
BR-DE-24-BBT-81Kartenzahlung mit Konto oder Lastschrift
BR-DE-25-ABG-19Lastschrift (59) ohne Lastschriftangaben
BR-DE-25-BBT-81Lastschrift (59) mit Empfängerkonto oder Karte
BR-DE-26BT-3Korrigierte Rechnung (384) ohne Bezug auf die frühere Rechnung (Warnung)
BR-DE-27BT-42Telefon mit weniger als drei Ziffern (Warnung)
BR-DE-28BT-43E-Mail-Adresse in falscher Form (Warnung)
BR-DE-30BT-90Lastschrift ohne Gläubiger-ID
BR-DE-31BT-91Lastschrift ohne IBAN des Zahlers
BR-DE-TMP-32BT-72Kein Liefer- oder Leistungsdatum (Hinweis)

EN-16931-Kernregeln (BR-*) 10 Regeln

Regel-IDFeldKurzbeschreibung
BR-1BT-24Specification identifier (BT-24)
BR-2BT-1Rechnungsnummer (BT-1)
BR-3BT-2Rechnungsdatum (BT-2)
BR-4BT-3Rechnungstyp-Code (BT-3)
BR-5BT-5Währungscode (BT-5)
BR-6BT-27Verkäufername (BT-27)
BR-7BT-44Käufername (BT-44)
BR-8BG-5Postanschrift des Verkäufers (BG-5)
BR-9BG-5Ländercode des Verkäufers (BT-40)
BR-10BG-8Postanschrift des Käufers (BG-8)

Berechnungsregeln (BR-CO-*) & Codelistenregeln (BR-CL-*) 8 Regeln

Regel-IDKurzbeschreibung
BR-CO-3Steuerdatum und Steuerdatum-Code schließen sich aus
BR-CO-9USt-ID des Verkäufers ohne Länderpräfix
BR-CO-10Summe der Netto-Positionsbeträge stimmt nicht
BR-CO-13Gesamtbetrag ohne USt falsch berechnet
BR-CL-10Ungültige schemeID (nicht aus ISO 6523)
BR-CL-16Ungültiger Zahlungsart-Code (nicht aus UNTDID 4461)
BR-CL-17Ungültige USt-Kategorie bei Rabatt oder Zuschlag auf Belegebene (in UBL auch in der Steueraufschlüsselung)
BR-CL-18Ungültige USt-Kategorie auf Positionsebene (in CII auch in der Steueraufschlüsselung)

Steuerkategorieregeln (BR-S/Z/E/AE-*) 4 Regeln

Regel-IDKurzbeschreibung
BR-S-1Normalsatz (S) ohne Steueraufschlüsselung der Kategorie S
BR-Z-1Nullsatz (Z): genau eine Steueraufschlüsselung der Kategorie Z
BR-E-1Steuerbefreit (E): genau eine Steueraufschlüsselung der Kategorie E, also ein Grund für 0 % je Rechnung
BR-AE-1Reverse Charge (AE): genau eine Steueraufschlüsselung der Kategorie AE

11. Nutzungsbeispiele

Beispiel 1: XRechnung-XML validieren

{
  "tool": "validate_invoice",
  "arguments": {
    "file_content": "PD94bWwgdmVyc2lvbj0iMS4wIj8+...",
    "file_type":   "xml",
    "profile":     "xrechnung"
  }
}
{
  "valid":   true,
  "format":  "XRechnung 3.0",
  "profile": "XRECHNUNG",
  "summary": "Keine Probleme gefunden",
  "errors":  [],
  "warnings": [],
  "metadata": {
    "invoice_number": "INV-2026-0042",
    "issue_date":     "2026-04-07",
    "seller":         "Tech GmbH",
    "buyer":          "Bundesbehörde X",
    "total_amount":   "5950.00",
    "currency":       "EUR"
  }
}

Beispiel 2: Fehlerhafte Rechnung mit Korrekturvorschlag

{
  "tool": "validate_invoice",
  "arguments": {
    "file_content": "JVBERi0xLjQK...",
    "file_type":   "pdf"
  }
}
{
  "valid":   false,
  "format":  "ZUGFeRD 2.x",
  "profile": "XRECHNUNG",
  "summary": "1 Fehler gefunden",
  "errors": [{
    "severity":       "error",
    "rule_id":        "BR-DE-15",
    "field":          "BT-10",
    "message":        "Käuferreferenz (BT-10) muss für XRechnung angegeben werden.",
    "fix_suggestion": "Füge 'ram:BuyerReference' hinzu, z.B. '04011000-1234-34' für Behörden.",
    "xpath":          "//ram:ApplicableHeaderTradeAgreement/ram:BuyerReference"
  }],
  "warnings": []
}

Beispiel 3: XML extrahieren und validieren

// Schritt 1 — XML aus PDF extrahieren
{ "tool": "extract_xml", "arguments": { "pdf_content": "JVBERi0x..." } }

// Schritt 2 — Extrahiertes XML validieren (base64-kodiert übergeben)
{ "tool": "validate_invoice", "arguments": { "file_content": "<base64 des xml_content>", "file_type": "xml" } }

Beispiel 4: Konsistenzprüfung

{
  "tool": "check_consistency",
  "arguments": { "pdf_content": "JVBERi0xLjQK..." }
}
{
  "consistent":    false,
  "discrepancies": [{
    "field":     "total_amount",
    "pdf_value": "1190.00",
    "xml_value": "1180.00",
    "message":   "Gesamtbetrag im PDF ('1190.00') unterscheidet sich von dem im XML ('1180.00')."
  }]
}

Beispiel 5: PDF ohne eingebettetes XML

Eine normale PDF-Rechnung ohne eingebettetes XML ist keine E-Rechnung. Die Antwort sagt das mit valid: false und not_an_einvoice: true; der einzige Fehler NO_EMBEDDED_XML nennt, was fehlt. Format und Profil fehlen dann.

{
  "tool": "validate_invoice",
  "arguments": {
    "file_content": "JVBERi0xLjQK...",
    "file_type":   "pdf"
  }
}
{
  "valid":           false,
  "not_an_einvoice": true,
  "summary":         "1 Fehler gefunden",
  "errors": [{
    "severity":       "error",
    "rule_id":        "NO_EMBEDDED_XML",
    "message":        "Diese PDF enthält kein eingebettetes XML. …",
    "fix_suggestion": "Erzeuge die Rechnung mit E-Rechnungs-fähiger Software, die das strukturierte XML in die PDF einbettet (ZUGFeRD/Factur-X), oder validiere direkt die XRechnung-XML."
  }],
  "warnings": []
}

12. Grenzen & Bekannte Einschränkungen

EinschränkungDetails
TimeoutMustang CLI wird serverseitig nach 30 Sekunden abgebrochen. Sehr große oder korrupte Dateien können diesen Timeout auslösen.
Rate-LimitValidierung: Prüfen 20/Woche · Automatisieren 500/Woche · Skalieren unbegrenzt. Erstellung (eigenes Kontingent): Prüfen 1 im Monat · Automatisieren und Skalieren unbegrenzt, Fair Use über das Stundenlimit. Sandbox-Erstellung: 100/Tag, max. 3/Minute. Sandbox-Prüfungen zählen auf das normale Wochenkontingent. Bei Überschreitung: HTTP 429.
DurchsatzAutomatisieren läuft bis 60, Skalieren bis 300 Aufrufe pro Stunde mit voller Geschwindigkeit. Darüber gibt es keine Sperre: Weitere Aufrufe laufen in einer langsamen Spur und bekommen nur Plätze, die gerade frei sind. Ist der Server voll, antwortet er nach bis zu 20 Sekunden Wartezeit mit HTTP 503, Retry-After: 15 und slow_lane: true. Dein Programm sendet die Rechnung dann einfach erneut. Das Wochenkontingent gilt weiter.
PDF-OCRcheck_consistency wertet nur maschinenlesbaren PDF-Text aus. Eingescannte PDFs ohne OCR-Schicht werden nicht unterstützt.
Metadaten bei PDFsvalidate_invoice extrahiert Metadaten auch bei PDFs, sofern ein eingebettetes XML vorhanden und lesbar ist. Bei Plain-PDFs ohne XML fehlt dieses Feld.
KonsistenzprüfungNur drei Felder werden verglichen (Nummer, Datum, Betrag). Adressen, Steuernummern und Positionsdetails werden nicht abgeglichen.
Unbekannte Regel-IDsFehlt eine Regel-ID in der Fix-Suggestions-DB, bleibt fix_suggestion leer. Die Meldung wird trotzdem zurückgegeben.
DateigrößeGehostete API: Eine Anfrage darf als JSON höchstens 50 MB groß sein, sonst kommt 413. Base64 macht Dateien um ein Drittel größer, also passen rund 35 MB an Dateien in eine Anfrage. Je Datei gilt dazu beim Umwandeln (/v1/convert) höchstens 10 MB, bei der Stapel-Prüfung (/v1/stapel) höchstens 15 MB. Der selbst betriebene MCP-Server hat keine feste Grenze, aber Mustang bricht nach 30 Sekunden ab; sehr große PDFs schaffen das womöglich nicht.
AnlagenBeim Erstellen und Umwandeln höchstens 10 Anlagen je Rechnung, jede und alle zusammen höchstens 5 MB, gemessen nach dem Dekodieren. Ab zwei Anlagen warnt die Prüfung mit CII-SR-476; die Rechnung bleibt gültig.

13. Changelog

Oktober 2026: Testzugang, Umwandlungskontingent und Enterprise

  • Neu: Testzugang mit Frist, eigenes Umwandlungskontingent, Enterprise-Angebot im Konto.
  • Neu beim Umwandeln: quota in GET /v1/convert nennt trial_until (letzter Tag des Testzugangs, sonst null) und individual (true bei einem vereinbarten Kontingent). Im Testzugang gibt es keine Mehrnutzung: Ist das Kontingent aufgebraucht, antwortet /confirm mit 402 ohne overage_required.

Oktober 2026: Steuernummer, Lastschrift und Anlagen

  • Neu: Steuernummer statt USt-IdNr., durchlaufende Posten, schon gezahlte Beträge, Bezug auf frühere Rechnungen, SEPA-Lastschrift, Anlagen (Erstellen und Umwandeln); Sammeldownload als ZIP auf der Website. In POST /v1/create und create_invoice heißen die Felder seller.tax_number, lines[].pass_through, prepaid_amount_cents, prepaid_vat_cents, preceding_invoices, direct_debit und attachments; beim Umwandeln stehen sie in Entwurf und Bestätigung, Beträge in Euro.
  • Neu: Das Absenderprofil (/v1/keys/issuer) trägt die Gläubiger-ID (creditor_id). PUT /v1/keys/issuer ändert nur die Felder, die im Aufruf stehen; eine leere Zeichenkette oder null löscht ein Feld. Eine USt-IdNr. ohne Ländercode und eine Steuernummer in falscher Form lehnt es mit 400 ab.
  • Geändert: Mit Leistungszeitraum und ohne Leistungsdatum steht als Leistungsdatum (BT-72) jetzt das Ende des Zeitraums, nicht mehr das Rechnungsdatum. Bei einer Vorschussrechnung ohne Leistungsdatum und Zeitraum fehlt BT-72; die ZUGFeRD-Prüfung warnt dann (PEPPOL-EN16931-R008).
  • Geändert: Eingaben, die bisher eine ungültige Datei ergaben (weder USt-IdNr. noch Steuernummer, USt-IdNr. ohne Ländercode, Zeitraum mit Ende vor Anfang, Lastschrift in der XRechnung ohne Mandat), beantworten wir jetzt mit 400.
  • Geändert: Die PDF einer Rechnungskorrektur (credit_note) nennt keinen Zahlbetrag mehr, sondern sagt: „Dieser Betrag wird Ihnen gutgeschrieben.“
  • Geändert: Über MCP enthält die Antwort einer ZUGFeRD-Rechnung mit Anlagen nur pdf_base64; das XML steckt in der PDF.
  • Geändert: Regelkennungen berichtigt: keine Scheinkennungen mehr (BT-89, TMP-32, EU-79), KoSIT-Regeln mit Buchstaben heißen ganz (BR-DE-25-B statt BR-DE-25), doppelte Meldungen entfallen. Der Korrekturvorschlag zu BR-DE-20 (IBAN des Zahlers) sagt nicht mehr, die Regel gebe es nicht; CII-SR-475 und CII-SR-476 bei mehreren Anlagen erklären wir als harmlos.
  • Geändert beim Umwandeln: missing nennt statt seller.vat_id jetzt seller.vat_id oder seller.tax_number. Ein Absenderprofil mit USt-IdNr. und gültiger Steuernummer ergibt beide im XML. XRechnung mit Lastschrift braucht keine eigene IBAN. save_profile übernimmt auch Steuernummer und Gläubiger-ID.
  • Geändert auf der Website, E-Rechnung erstellen: Der Absender hat jetzt Ansprechpartner und Telefon. XRechnung verlangt beide; ohne sie war jede XRechnung dieser Seite ungültig (BR-DE-5, BR-DE-6). Dazu kamen die Felder für Steuernummer, Rechnungsart, Bezüge, durchlaufende Posten, schon gezahlte Beträge, Lastschrift und Anlagen, auf Deutsch, Englisch und Französisch.

Oktober 2026: Formaterkennung

  • format und profile kommen jetzt aus der Kennung der Spezifikation (in CII das Feld der Guideline, in UBL CustomizationID), nicht mehr aus der ersten URN im Dokument. Vorher blieben viele XRechnungen und EN-16931-Rechnungen ohne Format.
  • Neue Felder container (pdf: PDF mit eingebetteter E-Rechnung, xml: reine XML-Datei) und syntax (CII, UBL).
  • Bezeichnungen: ZUGFeRD 2.x und ZUGFeRD 1.0 für PDF, sonst XRechnung 3.0, Peppol BIS Billing 3.0 oder EN 16931. Profile zusätzlich XRECHNUNG und PEPPOL; EXTENDED wird nicht mehr als EN16931 gemeldet.

Oktober 2026: Umwandeln

  • Neu: POST /v1/convert macht aus PDF, Word oder Excel eine E-Rechnung (ZUGFeRD im bisherigen Layout oder XRechnung), mit Entwurf, Bestätigung und Prüfung vor der Ausgabe.
  • Erkennt mehrere Steuersätze, Rabatte und Zuschläge, Reverse Charge und Einheiten; unsichere Felder mit Begründung.
  • Ab Automatisieren (20 im Monat) und Skalieren (100), Mehrnutzung 3,99 € je Umwandlung mit ausdrücklicher Bestätigung.

Juni 2026 — Rechnungserstellung

  • Neues Tool create_invoice / POST /v1/create: valide E-Rechnungen aus strukturierten Daten erzeugen — als ZUGFeRD-PDF (PDF/A-3) oder reine XRechnung-XML (mit Leitweg-ID für B2G).
  • Mehrere Positionen, alle USt-Kategorien (S/Z/E/AE), Rabatte/Zuschläge, Zahlungsangaben, Fälligkeit.
  • Belegtypen: Rechnung, Rechnungskorrektur (Storno), Korrektur, Gutschriftverfahren, Anzahlungs- und Teilrechnung, mit Bezug auf die Ursprungsrechnung.
  • Tier-abhängiges Branding (Footer im Prüfen-Tarif / White-Label ab Automatisieren mit optionalem Logo); eigenes Wochenkontingent, getrennt von der Validierung.
  • Sandbox-/Test-Keys (zv_test_…): freies Integrieren mit „DEMO"-Wasserzeichen, ohne das Live-Kontingent zu verbrauchen.

April 2026 — Erstveröffentlichung

  • Validierung gegen EN 16931 (validate_invoice) für ZUGFeRD/Factur-X-PDF und XRechnung-XML, mit konkreten Korrekturvorschlägen.
  • XML-Extraktion (extract_xml) und Konsistenzprüfung PDF↔XML (check_consistency).
  • MCP-Client für Claude & andere KI-Agenten; Mustang- und KoSIT-Engines.