E-Rechnungs·Validator REST API v1

REST API v1 — Dokumentation

Programmatische Validierung von XRechnung, ZUGFeRD und Factur-X. Alle Endpunkte liegen unter /api/v1/ relativ zur Basis-URL der Installation. v1 ist deprecated — neue Integrationen nutzen /api/v2/ (gleiche Endpunkte und Payloads, gehärtete Auth mit Scopes).

Authentifizierung

Alle Endpunkte erfordern einen API-Key im Request-Header:

X-API-Key: erv_<ihr-key>

API-Keys werden im Admin-Panel unter Admin → API-Keys erstellt und verwaltet. Der Klartext-Key wird nur einmalig bei der Erstellung angezeigt. Ein fehlender, ungültiger oder widerrufener Key führt zu 401 INVALID_API_KEY.

Key-Scoping: Validierungsergebnisse sind nur mit demselben API-Key abrufbar, mit dem sie eingereicht wurden. Fremde oder unbekannte Validation-Keys liefern 404.

API v2 — Auth & Scopes NEU

/api/v2/ bietet dieselben sieben Endpunkte mit identischen Payloads wie v1 — es ändert sich nur die Basis-URL — und zusätzlich den v2-exklusiven Endpunkt POST /zugferd. Die Authentifizierung läuft weiterhin über den X-API-Key-Header, ist aber gehärtet: Jeder Key erhält Scopes sowie optional ein Ablaufdatum, eine IP-Allowlist und ein eigenes Rate-Limit. v1 ist deprecated (alle v1-Responses tragen den Header Deprecation: true), läuft aber unverändert weiter.

Scopes

Scopes sind pro Key frei kombinierbar und werden im Admin-Panel unter Admin → API-Keys vergeben (beim Erstellen und beim Bearbeiten). Ein v2-Aufruf ohne den passenden Scope liefert 403 SCOPE_MISSING.

ScopeErlaubte Endpunkte
validatePOST /validate (sync & async), POST /visualize, POST /validate-and-visualize, POST /batch
results:readGET /results/{validation_key}, GET /results
results:ackPOST /results/ack
zugferd:createPOST /zugferd NEU

Neue Fehlercodes

HTTPCodeBedeutung
401KEY_EXPIREDAblaufdatum überschritten — der Key gilt bis einschließlich des Ablaufdatums (greift auch in v1)
403IP_NOT_ALLOWEDAufrufer-IP steht nicht in der IP-Allowlist des Keys
403SCOPE_MISSINGKey besitzt den für diesen Endpunkt nötigen Scope nicht
429RATE_LIMITEDRate-Limit überschritten — der Retry-After-Header nennt die Wartezeit in Sekunden

Das Rate-Limit beträgt standardmäßig 120 Requests pro Minute und ist pro Key übersteuerbar. Zusätzlich greift eine Brute-Force-Bremse: maximal 10 fehlgeschlagene Auth-Versuche pro Minute und IP. Ablaufdatum, IP-Allowlist und individuelles Rate-Limit werden je Key im Admin-Panel konfiguriert.

Migration v1 → v2

  1. Scopes vergeben: Bestands-Keys haben zunächst keine Scopes (secure-by-default) — im Admin-Panel die benötigten Scopes zuweisen.
  2. Basis-URL umstellen: /api/v1//api/v2/ in der Integration.
  3. Fertig — Endpunkte und Payloads sind unverändert, es ist keine weitere Anpassung nötig.

Key-Rotation: Neuen Key mit denselben Scopes anlegen und dem alten Key ein Ablaufdatum setzen — so läuft der alte Key kontrolliert aus, ohne die Integration zu unterbrechen.

Fehlerformat & Statuswerte

Alle Fehler haben dieses Format:

{ "error": "Beschreibung", "code": "ERROR_CODE" }
HTTPCodeBedeutung
401INVALID_API_KEYKey fehlt, ungültig oder widerrufen
400MISSING_FILEKein file-Feld im Request
400INVALID_MODEmode nicht in mustang, kosit, both
400TOO_MANY_FILESMehr als 50 Dateien im Batch-Request
413FILE_TOO_LARGEDatei > 10 MB
400MISSING_FILTERSammelabruf ohne unfetched und ohne from/to
400INVALID_DATEfrom/to ist kein gültiges ISO-Datum
400INVALID_PAGINATIONlimit/offset nicht numerisch
400MISSING_KEYSAck ohne keys-Liste
400TOO_MANY_KEYSMehr als 200 Keys pro Ack-Request
404UNKNOWN_KEYValidation-Key unbekannt oder gehört zu einem anderen API-Key
500VALIDATOR_ERRORJava-Fehler oder Validator-JAR nicht gefunden
500VISUALIZATION_ERRORVisualisierung fehlgeschlagen

Validierungsstatus

status im Ergebnis: valid | warn | invalid | error

Job-Status (Async)

Beim Abruf per Validation-Key kann das status-Feld zusätzlich den Lebenszyklus abbilden: pending (in Verarbeitung, HTTP 202) und error mit Feld error (Job fehlgeschlagen, z. B. nach Server-Neustart).

Validierung (sync & async)

POST/api/v1/validate

Validiert eine einzelne E-Rechnungs-Datei (XML oder ZUGFeRD-/Factur-X-PDF).

FeldTypPflichtDefaultBeschreibung
fileDateiJaRechnung (multipart/form-data)
modeStringNeinmustangmustang | kosit | both
asyncStringNeintrue: sofortige Annahme, Validierung im Hintergrund NEU

Synchron

curl -X POST https://einvoice-validator.de/api/v1/validate \
  -H "X-API-Key: erv_abc123..." \
  -F "file=@rechnung.xml" \
  -F "mode=both"

Response 200:

{
  "validation_id":  42,
  "validation_key": "3f9c2a7b1e8d4c6a9b0f2e5d8c1a4b7e",
  "filename":       "rechnung.xml",
  "mode":           "both",
  "status":         "valid",
  "format":         "XRechnung",
  "profile":        "urn:cen.eu:en16931:2017",
  "issues":         [],
  "mustang_result": { "status": "valid", "issues": [] },
  "kosit_result":   { "status": "valid", "issues": [] }
}

validation_key ist der eindeutige Schlüssel dieser Validierung — damit lässt sich das Ergebnis jederzeit erneut abrufen (siehe Einzelabruf).

Asynchron

curl -X POST https://einvoice-validator.de/api/v1/validate \
  -H "X-API-Key: erv_abc123..." \
  -F "file=@rechnung.xml" \
  -F "async=true"

Response 202:

{
  "validation_key": "3f9c2a7b1e8d4c6a9b0f2e5d8c1a4b7e",
  "status":         "pending",
  "filename":       "rechnung.xml",
  "mode":           "mustang"
}

Die Datei wird sofort angenommen, die Validierung läuft im Hintergrund. Das Ergebnis holen Sie später per Key ab. Ungültige Requests (Datei fehlt, falscher Modus, Datei zu groß) werden auch im Async-Fall sofort synchron abgelehnt.

HTML-Visualisierung

POST/api/v1/visualize

Erzeugt eine HTML-Visualisierung der Rechnung, ohne zu validieren.

FeldTypPflichtDefault
fileDateiJa
styleStringNeincompact (compact | full)

Response 200: { "filename": "...", "style": "compact", "visualization_html": "<Base64>" }

visualization_html ist Base64-kodiertes HTML. Dekodierung: Python base64.b64decode(...), JavaScript atob(...), Shell base64 -d.

Validieren + Visualisieren

POST/api/v1/validate-and-visualize

Kombiniert beide Aufrufe. Parameter: file (Pflicht), mode, style. Response wie /validate (inkl. validation_key) plus visualization_html; schlägt nur die Visualisierung fehl, ist stattdessen visualization_error gesetzt.

Massenvalidierung

POST/api/v1/batch

Validiert bis zu 50 Dateien in einem Request (synchron).

FeldTypPflichtDefault
files[]DateienJa
modeStringNeinmustang
curl -X POST https://einvoice-validator.de/api/v1/batch \
  -H "X-API-Key: erv_abc123..." \
  -F "files[]=@rechnung1.xml" \
  -F "files[]=@rechnung2.xml"

Response 200:

{
  "summary": { "total": 2, "valid": 1, "warn": 0, "invalid": 1 },
  "results": [
    { "validation_id": 43, "validation_key": "a1b2...", "filename": "rechnung1.xml",
      "status": "valid",   "issues": [] },
    { "validation_id": 44, "validation_key": "c3d4...", "filename": "rechnung2.xml",
      "status": "invalid", "issues": [{ "type": "error", "msg": "...", "rule": "BR-01" }] }
  ]
}

Einzelabruf per Validation-Key NEU

GET/api/v1/results/{validation_key}

Ruft das gespeicherte Ergebnis einer Validierung erneut ab. Der Abruf verändert den Abhol-Status nicht.

HTTPBedeutung
200Ergebnis fertig (status = Validierungsstatus) oder Job fehlgeschlagen (status: "error" + Feld error)
202Job läuft noch (status: "pending") — später erneut abrufen
404Key unbekannt oder gehört zu einem anderen API-Key
curl https://einvoice-validator.de/api/v1/results/3f9c2a7b1e8d4c6a9b0f2e5d8c1a4b7e \
  -H "X-API-Key: erv_abc123..."

Response 200 (fertig):

{
  "validation_key": "3f9c2a7b1e8d4c6a9b0f2e5d8c1a4b7e",
  "validation_id":  42,
  "filename":       "rechnung.xml",
  "mode":           "mustang",
  "created_at":     "2026-07-30T09:15:22",
  "status":         "valid",
  "format":         "XRechnung",
  "profile":        "urn:cen.eu:en16931:2017",
  "issues":         [],
  "report_xml":     "...",
  "returncode":     0,
  "abgeholt_am":    null,
  "mustang_result": { "status": "valid", "issues": [] },
  "kosit_result":   null
}

abgeholt_am ist der Zeitstempel der Bestätigung (siehe Ack) — null, solange das Ergebnis nicht bestätigt wurde.

Sammelabruf NEU

GET/api/v1/results

Ruft mehrere Ergebnisse des eigenen API-Keys ab. Mindestens ein Filter ist Pflicht.

ParameterBeschreibung
unfetched=trueNur fertige (done/error), noch nicht bestätigte Ergebnisse
from / toISO-Datum (2026-07-30) oder Datum+Zeit; auch einzeln nutzbar. to ohne Zeitanteil zählt bis Tagesende
limit / offsetPagination — Default 50, Maximum 200

unfetched und from/to sind kombinierbar. Beim reinen Zeitraum-Abruf erscheinen auch laufende Jobs (als status: "pending"-Objekt); bei unfetched=true nicht.

# Alle noch nicht abgeholten Ergebnisse
curl "https://einvoice-validator.de/api/v1/results?unfetched=true" \
  -H "X-API-Key: erv_abc123..."

# Alle Ergebnisse eines Zeitraums
curl "https://einvoice-validator.de/api/v1/results?from=2026-07-01&to=2026-07-31" \
  -H "X-API-Key: erv_abc123..."

Response 200:

{
  "results": [ { ...volles Ergebnis wie beim Einzelabruf... } ],
  "count":   1,
  "limit":   50,
  "offset":  0
}

Ergebnisse bestätigen (Ack) NEU

POST/api/v1/results/ack

Markiert Ergebnisse als abgeholt. Bestätigte Ergebnisse erscheinen nicht mehr im unfetched-Abruf, bleiben aber per Key und Zeitraum abrufbar. Der Aufruf ist idempotent — bereits bestätigte Keys zählen erneut als acked.

curl -X POST https://einvoice-validator.de/api/v1/results/ack \
  -H "X-API-Key: erv_abc123..." \
  -H "Content-Type: application/json" \
  -d '{ "keys": ["3f9c2a7b1e8d4c6a9b0f2e5d8c1a4b7e"] }'

Response 200: { "acked": 1, "unknown": [] }

unknown enthält Keys, die unbekannt sind, einem anderen API-Key gehören oder deren Job noch läuft. Maximal 200 Keys pro Request.

POST /api/v2/zugferd NEU

POST/api/v2/zugferd

Erzeugt aus einer CII-XML-Rechnung eine gültige ZUGFeRD-/Factur-X-Datei (PDF/A-3 mit eingebettetem XML). Ohne mitgeliefertes PDF wird die Sichtkomponente aus dem XML gerendert; ein mitgeliefertes PDF wird eingebettet und — falls es kein PDF/A ist — vorher automatisch nach PDF/A-3 konvertiert.

Erforderlicher Scope: zugferd:create. Der Endpunkt existiert nur in v2 — es gibt keine v1-Entsprechung.

FeldTypPflichtDefaultBeschreibung
xmlDateiJaCII-Rechnung (multipart/form-data). SBDH-verpackte Rechnungen werden automatisch ausgepackt.
pdfDateiNeinSichtkomponente. Fehlt sie, wird sie aus dem XML erzeugt.
profileStringNeinaus XMLMINIMUM | BASICWL | BASIC | EN16931 | EXTENDED | XRECHNUNG. Ohne Angabe wird das Profil aus der Guideline-ID des XML abgeleitet (Fallback EN16931).
versionStringNein21 | 2 — ZUGFeRD-Hauptversion
formatStringNeinzfzf (ZUGFeRD) | fx (Factur-X)
curl -X POST https://einvoice-validator.de/api/v2/zugferd \
  -H "X-API-Key: erv_abc123..." \
  -F "xml=@rechnung.xml" \
  -F "pdf=@sichtkomponente.pdf" \
  -F "profile=EN16931" \
  -o rechnung_zugferd.pdf

Response 200: die PDF-Bytes selbst (Content-Type: application/pdf), nicht JSON. Der Dateiname steht im Content-Disposition-Header. Wie die Datei entstanden ist, verraten drei zusätzliche Header:

HeaderWerteBedeutung
X-ZUGFeRD-ProfileEN16931, EXTENDED, …tatsächlich verwendetes Profil
X-ZUGFeRD-Profile-Sourcexml | fallback | overridewoher das Profil stammt: aus dem XML abgeleitet, Standardwert, oder per profile vorgegeben
X-ZUGFeRD-PDF-Sourcerendered | original | convertedSichtkomponente aus dem XML erzeugt, unverändert übernommen, oder nach PDF/A konvertiert

Fehlercodes

HTTPCodeBedeutung
400MISSING_FILEKein xml-Feld im Request
400UBL_NOT_SUPPORTEDUBL-Rechnung übergeben — ZUGFeRD bettet ausschließlich CII ein
400UNKNOWN_SYNTAXDatei ist kein gültiges CII-XML
400INVALID_PROFILEprofile ist kein bekanntes Profil
400INVALID_VERSIONversion ist nicht 1 oder 2
400INVALID_FORMATformat ist nicht zf oder fx
413FILE_TOO_LARGExml oder pdf > 10 MB
503PDFA_CONVERSION_UNAVAILABLEDas PDF ist kein PDF/A und Ghostscript steht auf dem Server nicht bereit — PDF als PDF/A exportieren oder ohne pdf senden
500JAVA_MISSINGJava nicht gefunden
500JAR_MISSINGMustang-JAR nicht gefunden
500ZUGFERD_ERRORErzeugung fehlgeschlagen (Fehler des Validator-Werkzeugs)

Nur CII: ZUGFeRD und Factur-X betten ausschließlich CII-XML ein. UBL-Rechnungen — auch UBL-XRechnungen — werden mit UBL_NOT_SUPPORTED abgelehnt; sie müssen vorher nach CII konvertiert werden.

Async-Workflow (Ablauf)

Empfohlener Ablauf für die Hintergrund-Validierung:

  1. Einreichen: POST /api/v1/validate mit async=true202 mit validation_key speichern.
  2. Abholen — zwei Varianten:
    • Gezielt: GET /api/v1/results/{key} pollen, bis statt 202 ein 200 kommt.
    • Gesammelt: Periodisch GET /api/v1/results?unfetched=true aufrufen — liefert alle fertigen, noch nicht bestätigten Ergebnisse (auch fehlgeschlagene Jobs).
  3. Bestätigen: Verarbeitete Ergebnisse per POST /api/v1/results/ack quittieren, damit sie beim nächsten unfetched-Abruf nicht erneut erscheinen.

Server-Neustart: Wird der Server während der Verarbeitung neu gestartet, erhalten offene Jobs status: "error" mit dem Hinweis „Server-Neustart während der Verarbeitung" — die Rechnung dann einfach neu einreichen.

Limits & Hinweise

LimitWert
Maximale Dateigröße10 MB pro Datei
Batch-Größe50 Dateien pro Request
Sammelabruflimit maximal 200 pro Seite
Ack200 Keys pro Request

← Zurück zur Anwendung