Reference

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:

json
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.

json
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

400
Bad Request

Ungültige Request-Daten (JSON, XML, fehlende Pflichtfelder)

401
Unauthorized

Fehlender oder ungültiger API Key

403
Forbidden

API Key hat keine Berechtigung für diese Ressource

404
Not Found

Ressource (z.B. Invoice) nicht gefunden

422
Unprocessable Entity

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[].

429
Too Many Requests

Rate Limit erreicht — die Antwort trägt **kein** `quota`-Objekt. Mit Backoff wiederholen.

429
Too Many Requests (QUOTA_EXCEEDED)

Monatliches Kontingent aufgebraucht (`error: "QUOTA_EXCEEDED"`). Nur dieser Fall trägt ein `quota`-Objekt; ein Retry hilft nicht.

500
Internal Server Error

Serverfehler, bitte erneut versuchen

API Error Codes

CodeHTTPBeschreibungLösung
Bad Request400Request-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_REQUEST400Ungü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_DETECTED400Die 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_DETECTED400Das 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_ITEMS400Die 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.
UNAUTHORIZED401API Key fehlt oder ist ungültigPrüfe den Authorization: Bearer <key> Header
FORBIDDEN403API Key hat keine BerechtigungPrüfe, ob der Key für diesen Endpoint freigeschaltet ist
Not Found404Ressource nicht gefundenPrüfe die ID oder den Endpoint-Pfad
PARSE_FAILED422Das 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_FAILED422Die 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_EXCEEDED429Monatliches Kontingent aufgebrauchtUpgrade deinen Plan oder warte bis zum Monatsende
Internal Server Error500Interner ServerfehlerVersuche 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.

BR-DE-1

Zahlungsangaben fehlen: Eine Rechnung muss Angaben zur Zahlung (BG-16) enthalten

Feld: paymentMethods

BR-DE-15

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

BR-DE-17

Unzulässiger Rechnungstyp: Der Rechnungstyp-Code (BT-3) muss 326, 380, 384, 389, 381, 875, 876 oder 877 sein

Feld: type

BR-16

Keine Rechnungsposition: Eine Rechnung muss mindestens eine Position (BG-25) enthalten

Feld: items

BR-CO-10

Summe der Positionen ungleich Rechnungsnetto

Feld: items[].quantity * items[].unitPrice

BR-CO-13

Rechnungsnetto falsch berechnet: BT-109 = Summe der Positionen minus Nachlässe plus Zuschläge

Feld: totals

BR-S-08

Steuerbasisbetrag der Kategorie S falsch: BT-116 stimmt nicht mit der Summe der Positionen dieses Steuersatzes überein

Feld: totals / items[].taxRate

BR-CL-10

Ungültige Schema-Kennung: Der schemeID einer Kennung muss aus der ISO-6523-Liste stammen

Feld: PartyIdentification/ID/@schemeID

BR-61

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

BR-CL-23

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):

typescript
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 is
16 // 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 backoff
25 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 error
33 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.