E-Rechnungs·Validator
REST API v1
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).
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/ 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 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.
| Scope | Erlaubte Endpunkte |
|---|---|
validate | POST /validate (sync & async), POST /visualize, POST /validate-and-visualize, POST /batch |
results:read | GET /results/{validation_key}, GET /results |
results:ack | POST /results/ack |
zugferd:create | POST /zugferd NEU |
| HTTP | Code | Bedeutung |
|---|---|---|
| 401 | KEY_EXPIRED | Ablaufdatum überschritten — der Key gilt bis einschließlich des Ablaufdatums (greift auch in v1) |
| 403 | IP_NOT_ALLOWED | Aufrufer-IP steht nicht in der IP-Allowlist des Keys |
| 403 | SCOPE_MISSING | Key besitzt den für diesen Endpunkt nötigen Scope nicht |
| 429 | RATE_LIMITED | Rate-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.
/api/v1/ → /api/v2/ in der Integration.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.
Alle Fehler haben dieses Format:
{ "error": "Beschreibung", "code": "ERROR_CODE" }
| HTTP | Code | Bedeutung |
|---|---|---|
| 401 | INVALID_API_KEY | Key fehlt, ungültig oder widerrufen |
| 400 | MISSING_FILE | Kein file-Feld im Request |
| 400 | INVALID_MODE | mode nicht in mustang, kosit, both |
| 400 | TOO_MANY_FILES | Mehr als 50 Dateien im Batch-Request |
| 413 | FILE_TOO_LARGE | Datei > 10 MB |
| 400 | MISSING_FILTER | Sammelabruf ohne unfetched und ohne from/to |
| 400 | INVALID_DATE | from/to ist kein gültiges ISO-Datum |
| 400 | INVALID_PAGINATION | limit/offset nicht numerisch |
| 400 | MISSING_KEYS | Ack ohne keys-Liste |
| 400 | TOO_MANY_KEYS | Mehr als 200 Keys pro Ack-Request |
| 404 | UNKNOWN_KEY | Validation-Key unbekannt oder gehört zu einem anderen API-Key |
| 500 | VALIDATOR_ERROR | Java-Fehler oder Validator-JAR nicht gefunden |
| 500 | VISUALIZATION_ERROR | Visualisierung fehlgeschlagen |
status im Ergebnis: valid | warn | invalid | error
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).
Validiert eine einzelne E-Rechnungs-Datei (XML oder ZUGFeRD-/Factur-X-PDF).
| Feld | Typ | Pflicht | Default | Beschreibung |
|---|---|---|---|---|
file | Datei | Ja | — | Rechnung (multipart/form-data) |
mode | String | Nein | mustang | mustang | kosit | both |
async | String | Nein | — | true: sofortige Annahme, Validierung im Hintergrund NEU |
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).
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.
Erzeugt eine HTML-Visualisierung der Rechnung, ohne zu validieren.
| Feld | Typ | Pflicht | Default |
|---|---|---|---|
file | Datei | Ja | — |
style | String | Nein | compact (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.
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.
Validiert bis zu 50 Dateien in einem Request (synchron).
| Feld | Typ | Pflicht | Default |
|---|---|---|---|
files[] | Dateien | Ja | — |
mode | String | Nein | mustang |
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" }] }
]
}
Ruft das gespeicherte Ergebnis einer Validierung erneut ab. Der Abruf verändert den Abhol-Status nicht.
| HTTP | Bedeutung |
|---|---|
| 200 | Ergebnis fertig (status = Validierungsstatus) oder Job fehlgeschlagen (status: "error" + Feld error) |
| 202 | Job läuft noch (status: "pending") — später erneut abrufen |
| 404 | Key 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.
Ruft mehrere Ergebnisse des eigenen API-Keys ab. Mindestens ein Filter ist Pflicht.
| Parameter | Beschreibung |
|---|---|
unfetched=true | Nur fertige (done/error), noch nicht bestätigte Ergebnisse |
from / to | ISO-Datum (2026-07-30) oder Datum+Zeit; auch einzeln nutzbar. to ohne Zeitanteil zählt bis Tagesende |
limit / offset | Pagination — 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
}
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.
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.
| Feld | Typ | Pflicht | Default | Beschreibung |
|---|---|---|---|---|
xml | Datei | Ja | — | CII-Rechnung (multipart/form-data). SBDH-verpackte Rechnungen werden automatisch ausgepackt. |
pdf | Datei | Nein | — | Sichtkomponente. Fehlt sie, wird sie aus dem XML erzeugt. |
profile | String | Nein | aus XML | MINIMUM | BASICWL | BASIC | EN16931 | EXTENDED | XRECHNUNG. Ohne Angabe wird das Profil aus der Guideline-ID des XML abgeleitet (Fallback EN16931). |
version | String | Nein | 2 | 1 | 2 — ZUGFeRD-Hauptversion |
format | String | Nein | zf | zf (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:
| Header | Werte | Bedeutung |
|---|---|---|
X-ZUGFeRD-Profile | EN16931, EXTENDED, … | tatsächlich verwendetes Profil |
X-ZUGFeRD-Profile-Source | xml | fallback | override | woher das Profil stammt: aus dem XML abgeleitet, Standardwert, oder per profile vorgegeben |
X-ZUGFeRD-PDF-Source | rendered | original | converted | Sichtkomponente aus dem XML erzeugt, unverändert übernommen, oder nach PDF/A konvertiert |
| HTTP | Code | Bedeutung |
|---|---|---|
| 400 | MISSING_FILE | Kein xml-Feld im Request |
| 400 | UBL_NOT_SUPPORTED | UBL-Rechnung übergeben — ZUGFeRD bettet ausschließlich CII ein |
| 400 | UNKNOWN_SYNTAX | Datei ist kein gültiges CII-XML |
| 400 | INVALID_PROFILE | profile ist kein bekanntes Profil |
| 400 | INVALID_VERSION | version ist nicht 1 oder 2 |
| 400 | INVALID_FORMAT | format ist nicht zf oder fx |
| 413 | FILE_TOO_LARGE | xml oder pdf > 10 MB |
| 503 | PDFA_CONVERSION_UNAVAILABLE | Das PDF ist kein PDF/A und Ghostscript steht auf dem Server nicht bereit — PDF als PDF/A exportieren oder ohne pdf senden |
| 500 | JAVA_MISSING | Java nicht gefunden |
| 500 | JAR_MISSING | Mustang-JAR nicht gefunden |
| 500 | ZUGFERD_ERROR | Erzeugung 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.
Empfohlener Ablauf für die Hintergrund-Validierung:
POST /api/v1/validate mit async=true → 202 mit validation_key speichern.GET /api/v1/results/{key} pollen, bis statt 202 ein 200 kommt.GET /api/v1/results?unfetched=true aufrufen — liefert alle fertigen, noch nicht bestätigten Ergebnisse (auch fehlgeschlagene Jobs).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.
| Limit | Wert |
|---|---|
| Maximale Dateigröße | 10 MB pro Datei |
| Batch-Größe | 50 Dateien pro Request |
| Sammelabruf | limit maximal 200 pro Seite |
| Ack | 200 Keys pro Request |