Fehlerkennungen der API
Der Katalog ist geschlossen und endlich: eine Kennung behält ihre Bedeutung. Verzweigen Sie auf error, nie auf message — der Wortlaut ist für Menschen und darf sich jederzeit ändern.
400 — die Anfrage ist falsch gebaut
Ihr Programm muss etwas ändern. Wiederholen ohne Änderung ergibt denselben Fehler.
validationHTTP 400Die Anfrage verletzt das SchemaEin Wert hat den falschen Typ, fehlt, ist zu lang, zu klein — oder der Rumpf enthält ein Feld, das die Route nicht kennt. Die Schemata dieser API sind durchgehend streng: ein unbekanntes Feld wird ABGELEHNT und nicht stillschweigend abgestreift. Das ist der Unterschied zwischen einem Tippfehler, der eine Minute kostet, und einem, den man drei Monate später im Datenbestand findet.read_only_fieldHTTP 400Die Anfrage setzt ein Feld, das der Server bestimmtDer Rumpf enthält ein Feld, das entweder der Datenbank gehört (`id`, `createdAt`, `updatedAt`) oder aus anderen Werten ABGELEITET wird (`packagePriceGrossCents` aus Netto und Satz, `totalCostCents` aus den Zutaten, `stockTotal` aus allen Lagern). Solche Felder werden abgelehnt statt ignoriert — sonst glaubte der Aufrufer, er habe den Bruttopreis gesetzt, und zwei Quellen für dieselbe Zahl liefen auseinander, ohne dass es jemandem auffällt.nothing_to_writeHTTP 400Es gibt nichts zu schreibenDer Rumpf ist leer oder enthält kein einziges Feld. Das ist bewusst ein Fehler und kein stilles 200: eine Anfrage, die nichts ändert, ist fast immer ein Fehler im aufrufenden Code — eine Schleife, die ein leeres Änderungsobjekt gebaut hat.
401 — der Aufrufer ist nicht erkannt
Es liegt am Schlüssel, nicht am Recht. Ein neuer Schlüssel löst es.
unauthorizedHTTP 401Es wurde kein Schlüssel mitgeschicktDie Kopfzeile `Authorization` fehlt ganz oder trägt kein `Bearer`-Schema. Die API kennt keine Sitzung und kein Cookie: ohne Schlüssel gibt es keinen Aufrufer und damit auch keinen Betrieb, auf den sich die Anfrage beziehen könnte.key_malformedHTTP 401Der Schlüssel hat nicht die richtige FormDer Wert nach `Bearer` passt nicht auf das Format `tt_<modus>_<kennung>_<geheimnis>`. Das wird geprüft, BEVOR irgendetwas nachgeschlagen wird — eine unbrauchbare Zeichenkette soll keine Datenbankabfrage kosten.key_unknownHTTP 401Diesen Schlüssel gibt es nichtDie Form stimmt, aber es gibt keinen Schlüssel mit dieser Kennung — oder das Geheimnis passt nicht zu ihr. Beides ergibt dieselbe Antwort: welcher der beiden Fälle vorliegt, wird bewusst nicht verraten.key_revokedHTTP 401Der Schlüssel wurde widerrufenEs gibt ihn, aber jemand im Betrieb hat ihn zurückgezogen. Das wirkt sofort und für jeden laufenden Aufruf — es gibt kein Zeitfenster, in dem ein widerrufener Schlüssel noch arbeitet.key_expiredHTTP 401Der Schlüssel ist abgelaufenBeim Anlegen wurde eine Gültigkeitsdauer gewählt, und die ist vorbei. Ablauf ist 401 und nicht 403, und das ist kein Geschmack: 403 heisst „du, aber nicht das". Ein abgelaufener Schlüssel ist niemand mehr.key_in_queryHTTP 401Der Schlüssel stand in der Adresse — er ist jetzt entwertetEin Schlüssel wurde als Abfrageparameter oder in einem Cookie mitgeschickt. Das wird nicht nur abgelehnt: der Schlüssel gilt damit als offengelegt. Eine Adresse steht in Server-Protokollen, im Browserverlauf, in jedem Verweis und in jedem Zwischenspeicher — sie ist kein Ort für ein Geheimnis.
402 — der Plan des Betriebs deckt das nicht
Weder Ihr Schlüssel noch Ihre Anfrage sind kaputt. Nur der Inhaber des Betriebs kann es lösen.
plan_upgrade_requiredHTTP 402Der Plan des Betriebs enthält diesen Datenbereich nichtDer Schlüssel ist gültig und trägt das nötige Recht — aber der Betrieb hat diesen Bereich nicht gebucht. Die Schnittstelle selbst ist in jedem Plan enthalten; bezahlt werden die Datenbereiche dahinter. Der Schlüssel bleibt gültig, gesperrt ist nur dieser eine Bereich.trial_expiredHTTP 402Der Testzeitraum ist abgelaufenDer Betrieb war in der 30-tägigen Testphase, sie ist vorbei, und es wurde kein Plan gewählt. Anders als beim fehlenden Merkmal liegt es hier nicht am Umfang des Plans, sondern daran, dass es noch keinen gibt.
403 — erkannt, aber nicht befugt
Der Schlüssel ist gültig, ihm fehlt ein Recht. Das lässt sich am Schlüssel nachsetzen.
forbiddenHTTP 403Dem Schlüssel fehlt das Recht auf diesem ModulDer Schlüssel ist gültig, trägt aber nicht `<modul>:read` bzw. `<modul>:write` für den angefragten Bereich. Die Rechte eines Schlüssels sind zu jeder Zeit höchstens die Rechte der Person, die ihn angelegt hat — verliert sie ein Recht, verliert der Schlüssel es im selben Moment mit. Geprüft wird bei jedem Aufruf neu.forbidden_actionHTTP 403Dem Schlüssel fehlt die gesondert zu vergebende HandlungZwei Handlungen sind gefährlicher als gewöhnliches Schreiben auf ihrem Modul und brauchen deshalb einen eigenen, ausdrücklich gesetzten Haken: `reservations.status` (ein gemeldeter No-Show zieht eine hinterlegte Gebühr ein und belastet das betriebsübergreifende Risikoprofil eines echten Menschen) und `guests.personal` (gibt `isBlocked`, `blockReason`, `noShowCount` und `notes` frei). Das Modulrecht allein reicht für beide nicht.key_owner_lost_accessHTTP 403Wer den Schlüssel angelegt hat, gehört nicht mehr zum BetriebEin Schlüssel leiht sich seine Rechte von der Person, die ihn erstellt hat. Ist diese Person nicht mehr Mitglied des Betriebs, hat der Schlüssel keine Grundlage mehr — unabhängig davon, welche Haken an ihm gesetzt sind. Das ist die Schranke, die verhindert, dass ein ausgeschiedener Mitarbeiter über einen zurückgelassenen Schlüssel weiter mitliest.origin_not_allowedHTTP 403Die Anfrage kam aus einem BrowserDie Anfrage trug eine `Origin`-Kopfzeile, wie sie ein Browser setzt. Diese API ist für Server gedacht: ein Schlüssel im Code einer Webseite ist für jeden Besucher lesbar. Es gibt deshalb bewusst keine `Access-Control-Allow-Origin`-Zeile — CORS später einzuschalten ist eine Zutat, es später abzuschalten bricht jede bestehende Einbindung.
404, 409, 412 — der Zustand passt nicht
Die Anfrage ist richtig gebaut, widerspricht aber dem, was gerade da ist. Lesen, entscheiden, neu schicken.
not_foundHTTP 404Diese Zeile gibt es für diesen Betrieb nichtEntweder existiert die Kennung nicht, oder sie gehört einem ANDEREN Betrieb. Beides ergibt dieselbe Antwort, und das ist Absicht: ein 403 für eine fremde Kennung verriete, dass es die Zeile gibt — und damit liesse sich durch Probieren herausfinden, welche Kennungen im System vergeben sind.conflictHTTP 409Der Zustand lässt das gerade nicht zuDie Anfrage ist gültig gebaut, aber sie widerspricht dem, was schon da ist. Anders als 400 liegt es nicht an der Form der Anfrage, sondern am Zustand des Betriebs — dieselbe Anfrage kann eine Stunde später richtig sein.precondition_failedHTTP 412Ihr `If-Match` passt nicht mehrSie haben mit `If-Match` gesagt, auf welchem Stand Sie zu schreiben glauben, und dieser Stand ist nicht mehr aktuell: jemand anders hat die Zeile inzwischen geändert. Der Schreibvorgang wurde NICHT ausgeführt. Genau dafür ist die Kopfzeile da — ohne sie hätten Sie die fremde Änderung stillschweigend überschrieben.idempotency_key_reuseHTTP 409Dieser `Idempotency-Key` gehört zu einer anderen AnfrageDerselbe Schlüssel wurde schon einmal benutzt, aber mit einem anderen Inhalt — andere Methode, andere Adresse oder ein anderer Rumpf. Die Arbeit wurde NICHT ausgeführt. Der Server merkt sich zu jedem Idempotenzschlüssel einen Fingerabdruck der Anfrage; ohne diese Prüfung bekäme eine inhaltlich neue Anfrage die alte Antwort zurück und niemand merkte es.idempotency_in_progressHTTP 409Eine Anfrage mit demselben Schlüssel läuft gerade nochEin erster Aufruf mit diesem `Idempotency-Key` ist noch in Arbeit. Die zweite Anfrage wird abgewiesen statt parallel ausgeführt — sonst liefen zwei Transaktionen für denselben Vorgang nebeneinander, und genau das soll der Schlüssel verhindern.
429, 500, 503 — Betrieb
Vorübergehend. Wiederholen ist hier die richtige Antwort — mit Abstand.
rate_limitedHTTP 429Zu viele AnfragenDas Kontingent ist erschöpft. Es gibt zwei getrennte Bremsen: eine je Schlüssel und eine je Betrieb. `scope` sagt, welche gegriffen hat — bei `tenant` hilft es nicht, den Schlüssel zu wechseln, denn dann verbrauchen mehrere Ihrer Schlüssel gemeinsam das Kontingent des Betriebs.rate_limit_unavailableHTTP 503Die Ratenbremse selbst ist gerade nicht prüfbarDer geteilte Speicher, in dem die Kontingente gezählt werden, antwortet nicht. Die API lässt die Anfrage dann NICHT ungezählt durch, sondern weist sie ab. Andersherum wäre der Ausfall der Bremse der Moment, in dem jede Grenze offensteht.internalHTTP 500Bei uns ist etwas schiefgegangenEin unerwarteter Fehler auf unserer Seite. In `message` steht nie eine rohe Fehlermeldung — kein Tabellenname, keine Anbietermeldung, kein Stapelverlauf. Was wirklich passiert ist, steht im Serverprotokoll; verbunden sind beide über `requestId`.