Error Handling
Verstehe API-Fehler und lerne, wie du sie in deiner Anwendung behandelst. Alle Fehler folgen einem einheitlichen Format mit hilfreichen Details.
Error Response Format
Alle API-Fehler werden im folgenden JSON-Format zurückgegeben:
1{2 "error": "VALIDATION_FAILED",3 "message": "Die Rechnung entspricht nicht dem XRechnung 3.0.2 Schema"4}error
Maschinenlesbarer Fehlercode zum programmatischen Handling
message
Menschenlesbare Fehlerbeschreibung
429 Quota Exceeded Response
Bei überschrittenem Quota enthält die Response ein zusätzliches quota-Objekt mit Verbrauchsinformationen.
1{2 "error": "QUOTA_EXCEEDED",3 "message": "Usage limit reached for pdf:de:generate",4 "quota": {5 "current": 100,6 "limit": 100,7 "period": "monthly"8 }9}quota.current
Aktueller Verbrauch
quota.limit
Maximales Limit
quota.period
Typ des Kontingentzeitraums, z. B. monthly oder yearly — kein Kalenderlabel. Fehlt, wenn die Ablehnung zu keinem Plan-Kontingent gehört.
HTTP Status Codes
Ungültige Request-Daten (JSON, XML, fehlende Pflichtfelder)
Fehlender oder ungültiger API Key
API Key hat keine Berechtigung für diese Ressource
Ressource (z.B. Invoice) nicht gefunden
Compliance-Prüfung fehlgeschlagen — das Request-JSON war gültig, aber eine EN-16931-/KoSIT-Regel schlägt an. Die Regel (z. B. BR-61) steht in errors[].
Rate Limit erreicht — die Antwort trägt **kein** `quota`-Objekt. Mit Backoff wiederholen.
Monatliches Kontingent aufgebraucht (`error: "QUOTA_EXCEEDED"`). Nur dieser Fall trägt ein `quota`-Objekt; ein Retry hilft nicht.
Serverfehler, bitte erneut versuchen
API Error Codes
| Code | HTTP | Beschreibung | Lösung |
|---|---|---|---|
| Bad Request | 400 | Request-Body oder Pfadparameter sind ungültig — der häufigste Fehlerstring der API. | `message` lesen — sie nennt das Feld. Bei Format-Fehlern die Pfad-Token prüfen: `xrechnung`, `zugferd`, `pdf`, `facturx`, `ubl`, `peppol-ubl`, `qr-bill`, `fatturapa`, `facturae`, `ebinterface`, `isdoc`, `nav`, `mydata`, `ksef` — klein und ohne Versionsnummer. |
| INVALID_REQUEST | 400 | Ungültige Eingabe auf der VeriFactu-Route (`POST /api/v1/invoice/es/verifactu-qr`) — der einzige Endpoint, der diesen Code sendet. | Die VeriFactu-Felder gegen `/docs/api/verifactu-qr` prüfen. Auf allen anderen Routen heißt derselbe Fall `Bad Request`. |
| FORMAT_NOT_DETECTED | 400 | Die Formaterkennung war nicht sicher genug (Konfidenz < 50) — betrifft `POST /api/v1/invoice/parse` und `POST /api/v1/invoice/convert`. | Den expliziten Parse-Endpoint mit Land und Format aufrufen: `POST /api/v1/invoice/{countryCode}/{format}/parse`. Die Antwort trägt zusätzlich `detection` mit dem Zwischenergebnis. |
| COUNTRY_NOT_DETECTED | 400 | Das Format wurde erkannt, das Land nicht (`POST /api/v1/invoice/parse`). | Den Parse-Endpoint mit Länderpfad aufrufen: `POST /api/v1/invoice/{countryCode}/{format}/parse`. |
| TOO_MANY_LINE_ITEMS | 400 | Die Rechnung hat mehr Positionen in `items[]`, als der Tarif erlaubt — betrifft das Erzeugen, beide Prüf-Routen und das Umwandeln, nicht das Auslesen. `lineItems.count` und `lineItems.limit` nennen gezählte Anzahl und Grenze. | Den Beleg auf mehrere Rechnungen aufteilen oder den Tarif wechseln (Free 25, Starter 100, Premium 1.000, Enterprise individuell). Über allen Tarifen liegt eine absolute Obergrenze von 15.000 Positionen je Rechnung. Abgelehnt heißt: keine Rechnung erzeugt, kein Call abgerechnet. |
| UNAUTHORIZED | 401 | API Key fehlt oder ist ungültig | Prüfe den Authorization: Bearer <key> Header |
| FORBIDDEN | 403 | API Key hat keine Berechtigung | Prüfe, ob der Key für diesen Endpoint freigeschaltet ist |
| Not Found | 404 | Ressource nicht gefunden | Prüfe die ID oder den Endpoint-Pfad |
| PARSE_FAILED | 422 | Das Quelldokument wurde erkannt, ließ sich aber nicht lesen (HTTP 422). Die Einzelfehler stehen in `errors[]`. | `errors[]` auswerten. Häufigste Ursache: ein PDF ohne eingebettetes XML oder ein abgeschnittener Upload. |
| CONVERSION_FAILED | 422 | Die Quelle ließ sich nicht in ein konformes Zieldokument überführen (HTTP 422). `complianceErrors` nennt die verletzten Regeln. | `complianceErrors` und `conversionWarnings` lesen — meist fehlt ein Feld, das das Zielformat verlangt und die Quelle nicht führt. |
| QUOTA_EXCEEDED | 429 | Monatliches Kontingent aufgebraucht | Upgrade deinen Plan oder warte bis zum Monatsende |
| Internal Server Error | 500 | Interner Serverfehler | Versuche es erneut, kontaktiere Support bei Wiederholung |
XRechnung Validierungsfehler (BR-DE)
Diese Fehler stammen aus der KoSIT-Schematron-Validierung und werden in errors[] / warnings[]zurückgegeben:
Ein Eintrag in `errors[]` bzw. `warnings[]` hat genau drei Felder: code, message, field. Ein `details`-Objekt gibt es nicht. `MISSING_FIELD` und `MISSING_OPTIONAL` sind Werte von `errors[].code` bzw. `warnings[].code`, keine Top-Level-Fehlercodes.
Zahlungsangaben fehlen: Eine Rechnung muss Angaben zur Zahlung (BG-16) enthalten
Feld: paymentMethods
Käuferreferenz fehlt: Das Element Buyer reference (BT-10) muss übermittelt werden — bei Rechnungen an öffentliche Auftraggeber steht dort die Leitweg-ID. Deren Aufbau: 2–12 Stellen Grobadressierung, optional 0–30 Stellen Feinadressierung, 2 Prüfziffern, mit `-` getrennt (Beispiel: `04011000-12345-34`).
Feld: countrySpecific.leitwegId
Unzulässiger Rechnungstyp: Der Rechnungstyp-Code (BT-3) muss 326, 380, 384, 389, 381, 875, 876 oder 877 sein
Feld: type
Keine Rechnungsposition: Eine Rechnung muss mindestens eine Position (BG-25) enthalten
Feld: items
Summe der Positionen ungleich Rechnungsnetto
Feld: items[].quantity * items[].unitPrice
Rechnungsnetto falsch berechnet: BT-109 = Summe der Positionen minus Nachlässe plus Zuschläge
Feld: totals
Steuerbasisbetrag der Kategorie S falsch: BT-116 stimmt nicht mit der Summe der Positionen dieses Steuersatzes überein
Feld: totals / items[].taxRate
Ungültige Schema-Kennung: Der schemeID einer Kennung muss aus der ISO-6523-Liste stammen
Feld: PartyIdentification/ID/@schemeID
Zahlungsart Überweisung ohne Empfängerkonto: Ist paymentMethods eine Überweisung (BT-81 = 30/58), muss die Zahlungskonto-Kennung (BT-84, IBAN) vorhanden sein
Feld: seller.bankAccount.iban
Mengeneinheit nicht als Code: items[].unit muss ein UN/ECE-Rec-20-Code sein (C62 Stück, HUR Stunde, KGM, MTR, DAY) — kein Klartext
Feld: items[].unit
Vollständige Liste: KoSIT XRechnung Spezifikation
Retry-Strategie
Implementiere Retries für temporäre Fehler (429, 5xx):
1async function createInvoice(data, retries = 3) {2 for (let i = 0; i < retries; i++) {3 try {4 const response = await fetch('https://service.invoice-api.xhub.io/api/v1/invoice/de/xrechnung/generate', {5 method: 'POST',6 headers: {7 'Authorization': 'Bearer sk_live_...',8 'Content-Type': 'application/json'9 },10 body: JSON.stringify(data)11 });12 13 if (response.status === 429) {14 const body = await response.json();15 // Only the quota rejection carries a `quota` object. Retrying it is16 // pointless — the monthly allowance is gone until the period rolls over.17 if (body.quota) throw new Error(body.message);18 // Rate limit: no Retry-After header is sent, so back off exponentially.19 await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));20 continue;21 }22 23 if (response.status >= 500) {24 // Server error - retry with exponential backoff25 await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));26 continue;27 }28 29 const result = await response.json();30 31 if (!response.ok) {32 // Client error - don't retry, handle the error33 throw new Error(result.message);34 }35 36 return result;37 } catch (error) {38 if (i === retries - 1) throw error;39 }40 }41}Best Practices
Fehler loggen
Logge immer den Response-Header x-request-id. Ein Feld `requestId` im Antwort-Body gibt es nicht. Mit dem Header können wir bei Support-Anfragen den Request nachvollziehen.
Retry nur bei 5xx/429
Client-Fehler (4xx) werden durch Retries nicht behoben. Nur bei Server- oder Rate-Limit-Fehlern macht Retry Sinn.
Exponential Backoff
Bei Retries: Verdopple die Wartezeit nach jedem Fehlversuch (1s, 2s, 4s, ...), um den Server nicht zu überlasten.
429 unterscheiden
Ein `Retry-After`-Header wird nicht gesendet. Prüfe statt seiner, ob die 429-Antwort ein quota -Objekt trägt: mit Objekt ist das Kontingent aufgebraucht (Upgrade oder Periodenwechsel abwarten), ohne Objekt greift das Rate Limit (mit exponentiellem Backoff wiederholen).
Support
Bei wiederkehrenden Fehlern oder unklaren Fehlermeldungen kontaktiere uns unter support@xhub.io mit dem Response-Header x-request-id.