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 exakt dieselben sieben Endpunkte mit identischen Payloads wie v1 — es ändert sich nur die Basis-URL. 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

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.

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