Fehlerliste und bekannte API-Fallen
Diese Seite dokumentiert reale Fehlerbilder, die bei Integrationen aufgetreten sind.
Schema
| Error Code | Grund | Lösung / Bug |
|---|---|---|
404 Website not found | Einige Website-Unterendpunkte haben früher nur die alte numerische Website-ID akzeptiert, obwohl die Docs websitePublicId gezeigt haben. | Behoben. GET /api/websites/:websitePublicId/check-history und GET /api/websites/:websitePublicId/uptime-stats akzeptieren jetzt Public IDs und weiterhin Legacy-IDs. |
403 Forbidden auf GET /api/organizations/:organizationPublicId | Die Route hat die Pfad-ID früher direkt als Zahl interpretiert. Eine UUID wurde dadurch schon in der Berechtigungsprüfung verworfen. | Behoben. Die Organisations-Detail- und Billing-Routen lösen jetzt publicId korrekt auf. |
400 Bad Request mit ZodError bei customer-ips oder customer-domains | Query-Parameter wie organizationId=, page= oder perPage= wurden als leere Strings gesendet. Zod coerct leere Strings zu 0, was dann an den Mindestwerten scheitert. | Behoben für organizationId, page und perPage. Optional bedeutet trotzdem: ungenutzte Parameter komplett weglassen statt leere Strings zu senden. |
Betroffene Fälle
Website not found bei Website-Monitoring-Endpunkten
Betroffene Endpunkte:
GET /api/websites/:websitePublicId/check-historyGET /api/websites/:websitePublicId/uptime-stats
Früherer Effekt:
{
"error": true,
"statusCode": 404,
"statusMessage": "Website not found",
"message": "Website not found"
}Grund:
- Die Route hat nur die alte numerische
iddirekt geparsed. - Eine UUID wurde nicht in die interne numerische ID aufgelöst.
Aktueller Stand:
- Public IDs funktionieren jetzt wie in der Doku beschrieben.
- Legacy-Integer-IDs bleiben aus Kompatibilitätsgründen gültig.
Forbidden bei Organisations-Details mit Public ID
Betroffener Endpoint:
GET /api/organizations/:organizationPublicId
Grund:
- Die Berechtigungsprüfung lief früher gegen
Number(id). - Bei einer UUID wurde daraus
NaN, was zu403 Forbiddenführte.
Aktueller Stand:
- Die Route und die Billing-Routen lösen
organizationPublicIdjetzt korrekt in die interne ID auf.
ZodError bei optionalen Listen-Parametern
Betroffene Endpunkte:
GET /api/customer-ipsGET /api/customer-domains
Typisches Muster:
{
"error": true,
"statusCode": 400,
"statusMessage": "Bad Request"
}Grund:
- Nicht das Weglassen der Parameter war das Problem.
- Das Problem waren leere Query-Werte wie
?page=&perPage=.
Lösung:
- Unbenutzte optionale Parameter weglassen.
- Die API ignoriert für diese Felder jetzt leere Strings robuster.
Monitor-Ownership-Fehlercodes
Seit dem Managed-vs.-Self-Service-Modell können Monitor-Schreib-Endpunkte aller Typen (Website, DNS, ICMP, SMTP, SSH, FTP, IMAP/POP, Domain, DNSBL) diese 403-Codes in data.code liefern:
| Code | Bedeutung |
|---|---|
managed_by_organization | Ein kunden-gescopter Aufrufer wollte einen managed-Monitor ohne die canEditManaged-Ausnahme bearbeiten/löschen. Stattdessen einen Change Request eröffnen. |
selfServiceNotAllowed | Ein kunden-gescopter Aufrufer wollte einen Monitor anlegen, aber das aufgelöste allowSelfService des Kunden ist false. |
selfServiceQuotaReached | Das Anlegen (oder Umstellen auf) eines self_service-Monitors würde maxSelfServiceUrls des Kunden überschreiten: das Kontingent zählt alle Monitor-Typen zusammen. |
managementTypeOrgOnly | Ein Nicht-Org-Admin wollte den managementType eines Monitors ändern. Klassen-Wechsel sind Organisations-Admins vorbehalten, und seit dem 04.09.2026 gilt das auch gegenüber kunden-gescopten API-Tokens: Ein solcher Token führt zwar eine Admin-Rolle, aber einen eingeschränkten Scope, und der Wechsel verlangt jetzt einen uneingeschränkten. |
readonlyMayNotDelete | Ein Aufrufer mit der Rolle readonly wollte einen Monitor löschen. Löschen ist dieser Rolle bei jeder Besitzklasse verwehrt, auch bei self_service, und ein von einem Nur-Lese-Zugang ausgestellter API-Token erbt die Sperre. Das Bearbeiten eines self_service-Monitors bleibt erlaubt. |
tooManyOpenRequests | (429) Der Kunde hat bereits 10 offene Change Requests. |
Monitorkontingent-Fehlercodes
Eine Organisation hat ein Monitorkontingent. Ist es erschöpft, scheitert das Anlegen eines Monitors - oder es hebt die Organisation auf die nächste Kontingentstufe, wenn sie selbst hochstuft. Diese Stufe hat einen höheren Monatspreis, und die Differenz wird für den Rest des laufenden Zeitraums abgerechnet.
| Code | Bedeutung |
|---|---|
monitorQuotaReached | (403) Die Organisation steht an ihrem Monitorkontingent und stuft NICHT selbst hoch. Einen Monitor freigeben oder das Kontingent anheben. |
monitorQuotaNoHigherTier | (403) Die Organisation steht am Kontingent, stuft selbst hoch und ist bereits auf der höchsten vorgesehenen Stufe. Support ansprechen. |
paidQuotaUpgradeNotAuthorized | (403) Der Aufruf hätte eine KOSTENPFLICHTIGE Hochstufung ausgelöst und hat sie nicht erlaubt. Das kann nur ueber eine Agentenverbindung (MCP) passieren: ein Mensch im Dashboard sieht Stufe und Preis, ein Modell nicht - deshalb startet eine Abrechnungswirkung abgewählt. Die Meldung nennt Stufe und Monatspreis, die der Aufruf gekauft hätte. Sende allowPaidQuotaUpgrade: true, sobald ein Mensch der höheren Rechnung zugestimmt hat, oder gib vorher einen Monitor frei. Jeder andere Aufrufer, auch ein API-Token, bleibt unberührt und sieht diesen Code nie. |
Kundenpaket-Fehlercodes
POST /api/customers und PATCH /api/customers/:customerPublicId lösen das Paket eines Kunden auf. Beide weisen ein Paket zurück, das die Organisation nicht hat:
| Code | Bedeutung |
|---|---|
invalidPackageType | (400) Die packageId gehört nicht zu dieser Organisation, oder der Legacy-packageType passte weder auf einen konfigurierten Paketschlüssel noch auf einen Paket-Anzeigenamen dieser Organisation. |
PATCH hat einen unbekannten packageType früher unverändert gespeichert und packageId leer gelassen. Ein solcher Kunde hing an gar keiner Paketkonfiguration, wodurch sich seine Datenaufbewahrung nicht auflösen liess. Sende packageId oder einen packageType, den die Organisation hat.
Fehlercodes fürs Website-Prüfintervall
checkInterval hat je nach Monitor-Art eine andere Obergrenze, weil der Wert zweierlei bedeutet: bei aktiv getakteten Monitoren ist er die Abfragefrequenz und bleibt bei 1 bis 1440 Minuten (24 Stunden), bei heartbeat-Monitoren ist er das erwartete Ping-Intervall und reicht von 1 bis 43200 Minuten (30 Tage), ein monatlicher Cron-Job ist ein legitimer Monitor.
| Code | Bedeutung |
|---|---|
checkIntervalTooLow | (400) POST /api/websites: checkInterval liegt unter dem Minimum, das das Kundenpaket erlaubt. Die statusMessage nennt das Minimum. |
checkIntervalTooHigh | (400) PATCH /api/websites/:id: checkInterval überschreitet die Obergrenze der Monitor-Art. Die Art kommt aus monitoringType, wenn der Request es mitschickt, sonst vom gespeicherten Monitor, ein Intervall über 1440 auf einem Nicht-Heartbeat wird also auch dann abgewiesen, wenn monitoringType fehlt. |
invalidCheckInterval | (400) PATCH /api/websites/:id (Teil-Update ohne customerId, name und url): checkInterval ist keine ganze Zahl zwischen 1 und der Obergrenze der Art. Das Paketminimum gilt auch hier und antwortet mit checkIntervalTooLow. |
invalidTimeout | (400) PATCH /api/websites/:id (Teil-Update): timeoutSeconds ist keine ganze Zahl zwischen 1 und 60. |
invalidCheckConfig | (400) PATCH /api/websites/:id (Teil-Update): ein Prüf-Feld liegt ausserhalb seiner Grenzen, etwa sslNoticeDays über 365 oder minPageSize unter 0. Die sechs Prüf-Felder, checkDomainExpiryEnabled, die SSL- und Domain-Ablauf-Schwellen und die Seitengrössen übernimmt auch das Teil-Update; das Paket klemmt sie dabei genauso ab wie im Voll-Update. |
invalidUrl | (400) PATCH /api/websites/:id (Teil-Update): url zeigt auf eine private, Loopback-, Link-Local- oder sonst nicht öffentliche Adresse. Dieselbe Prüfung wie bei POST /api/websites. heartbeatToken wird auf diesem Pfad ignoriert; das Token entsteht nur serverseitig. |
POST /api/websites meldet dieselbe Obergrenze als Validierungsfehler auf dem Pfad checkInterval statt als data.code, weil die Art dort schon aus dem Request-Body feststeht.
Fehlercodes fürs Ersatzziel (connect target)
connectHost, connectPort und connectTlsInsecure verbinden die Prüfung mit einem anderen Host
als dem aus url, während url, der Host-Header und das TLS-SNI unverändert bleiben - die
Semantik von curl --resolve. Sowohl POST /api/websites als auch PATCH /api/websites/:id
(Teil-Update eingeschlossen) prüfen sie gleich.
| Code | Bedeutung |
|---|---|
connectHostInvalid | (400) connectHost liess sich nicht einlesen: es trägt ein Protokoll oder einen Pfad, enthält Leerzeichen, hat eine IPv6-Adresse ohne eckige Klammern, oder sein Port ist keine Zahl zwischen 1 und 65535. |
connectHostNotPublic | (400) connectHost löst auf eine private, Loopback-, Link-Local- oder sonst gesperrte Adresse auf - dieselbe SSRF-Sperre wie bei url. |
connectTlsInsecureWithoutHost | (400) connectTlsInsecure wurde als true gesendet, während das sich ergebende connectHost leer ist. Beim PATCH greift das auch, wenn ein Request connectHost löscht (null), aber trotzdem connectTlsInsecure: true mitschickt. |
Nicht verfügbar für playwright-Szenarien und heartbeat-Monitore: beide durchlaufen nicht die
HTTP/SSL-Prüfkette, in die connectHost eingreift - die Felder werden bei diesen Monitor-Arten
angenommen, wirken sich aber nicht aus.
Check-Mode-Fehlercodes (SSH-/SMTP-/FTP-/IMAP-POP-Monitore)
Die Monitor-Typen mit Zugangsdaten (SSH, SMTP, FTP, IMAP/POP) unterstützen ein Feld checkMode (protocol | tcp, Standard protocol). Im Modus tcp führt der Monitor nur eine reine TCP-Port-Erreichbarkeitsprüfung durch, Zugangsdaten sind weder erforderlich noch werden sie gespeichert. Die Create- und Update-Endpunkte dieser vier Typen können diese 400-Codes in data.code liefern:
| Code | Bedeutung |
|---|---|
portRequiredForTcp | IMAP/POP Create oder Update: checkMode ist (oder ergibt sich zu) tcp, aber es ist weder im Request noch bereits am Monitor gespeichert ein port vorhanden. Nur IMAP/POP benötigt für den Modus tcp einen expliziten port. SSH, SMTP und FTP nutzen dafür ihren protokollspezifischen Default-Port. |
invalidCheckMode | Nur bei Update-Endpunkten (PATCH): checkMode wurde übergeben, ist aber weder protocol noch tcp. |
Incident-Fehlercodes
Nur manuelle (Statusseiten-)Incidents können gelöscht werden. DELETE /api/incidents/:id liefert diesen data.code:
| Code | Bedeutung |
|---|---|
notManualIncident | (403) Der Incident wurde vom Monitoring erzeugt. Nur von einem Admin erstellte manuelle Incidents können gelöscht werden; automatische Incidents gehören den Workern und sind über die API niemals löschbar. |
Fehlercodes für Wartungsfenster
POST /api/maintenance-windows/preview-occurrences (Doku) kann diesen data.code liefern:
| Code | Bedeutung |
|---|---|
invalidWindow | (400) endTime liegt nicht nach startTime. |
Anlegen, Ändern und Löschen eines Fensters (POST /api/maintenance-windows, PATCH /api/maintenance-windows/{id}, DELETE /api/maintenance-windows/{id}) können diese liefern:
| Code | Bedeutung |
|---|---|
endTimeBeforeStartTime | (400, anlegen und ändern) endTime liegt nicht nach startTime. Dieselbe Bedingung, die der Vorschau-Endpunkt mit invalidWindow beantwortet; die beiden Codes sind verschieden, also auf beide verzweigen. |
missingTargetId | (400, anlegen) Die Anfrage nennt eine Zielart, aber keine Id dazu. Ein Fenster braucht mindestens einen Monitor, einen Kunden, eine Kunden-IP oder eine Kundendomain, die es abdeckt. |
maintenanceWindowNotFound | (404, ändern und löschen) Es gibt kein Fenster dieser Id, das dieser Aufrufer sehen darf. Ausserhalb des Geltungsbereichs und nicht vorhanden sind absichtlich dieselbe Antwort. |
customerNotFound, customerIpNotFound, customerDomainNotFound | (404, anlegen) Das genannte Ziel liegt in keiner Organisation, die dieser Aufrufer sehen darf. |
maintenanceWindowTargetMissing | (500, ändern und löschen) Dem gespeicherten Fenster fehlt die Zeile, die es abgedeckt hat. Das ist eine Dateninkonsistenz und kein Anfragefehler: melden statt wiederholen. |
Ein wiederholter Anlege-Aufruf ist ungefährlich: ein Fenster, dessen vollständiger Anfragekörper einem bereits vorhandenen entspricht, entsteht kein zweites Mal, das vorhandene kommt zurück, und seine Abonnenten werden nicht ein zweites Mal angekündigt.
Organisationsberichte-Fehlercodes
Endpunkte unter /api/organization/reports und /api/organization/report-runs können diese data.code-Werte liefern:
| Code | Bedeutung |
|---|---|
invalidReportSchedule | Wöchentlicher Bericht ohne weekday oder monatlicher Bericht ohne dayOfMonth (1-28). |
invalidReportScope | Scope-IDs gehören nicht zur Organisation, oder Schwellenwert-Modus ohne Schwellenwert. |
reportRunNotFound | Der angeforderte Bericht-Lauf existiert für diese Organisation nicht. |
reportRunNoPdf | Der Bericht-Lauf hat kein archiviertes PDF (Nur-E-Mail oder übersprungen). |
reportPdfNotFound | (404) Der Lauf-Datensatz hat einen PDF-Pfad, aber das Objekt konnte nicht aus dem Object Storage gelesen werden. |
Datenexport-Fehlercodes
Endpunkte unter /api/organization/data-export (siehe Datenexport anfordern) können diese data.code-Werte liefern:
| Code | Bedeutung |
|---|---|
invalidExportRecipient | (400) recipientEmails enthält eine Adresse, die kein Mitglied dieser Organisation ist. Die abgelehnten Adressen stehen in data.unknown. |
exportAlreadyRunning | (409) Für diese Organisation läuft bereits ein Export (queued oder generating). Der laufende Export steht in data.export. |
exportCooldown | (429) Innerhalb des Cooldown-Fensters (data.cooldownHours, Standard 6) wurde bereits ein Export angefordert. |
exportNotReady | (409) Download angefragt, während der Export in der Warteschlange, in Arbeit oder fehlgeschlagen ist. Aktueller Stand in data.status. |
exportExpired | (410) Das 7-Tage-Download-Fenster ist geschlossen, die Datei wurde gelöscht. |
missingExportId | (400) Die Route wurde ohne Export-ID aufgerufen. |
Agent-Auth-Fehlercodes
Agent-Zugriffstokens (wsma_, siehe Agent-Authentifizierung) sind standardmässig hart auf eine GET/HEAD-Allowlist beschränkt. Ein Token mit Schreib-Scope (api.write.monitors, api.write.incidents) wird stattdessen je Methode und Pfad geprüft, gegen eine feste Positivliste, die heute nur diese beiden Bereiche abdeckt -- alles andere, einschließlich Abrechnung, Zugangsdaten und jeder Pfad auf der gemeinsamen Schreib-Sperrliste, bleibt unabhängig vom Scope unerreichbar. Beide Token-Arten erreichen weiterhin nichts außerhalb ihrer eigenen Lese-Allowlist.
| Code | Bedeutung |
|---|---|
agentTokenReadOnly | (403) Antwort auf jeden Schreibversuch, während der plattformweite Schreib-Kill-Switch aus ist, unabhängig vom Scope des Tokens -- und, auch beim Lesen, wenn der Pfad außerhalb der eigenen Lese-Allowlist des Tokens liegt. |
agentTokenScopeForbidden | (403) Der Kill-Switch ist an, aber genau diese Methode und dieser Pfad stehen nicht auf der Schreib-Positivliste des Tokens -- eingeschlossen ein Token ganz ohne Schreib-Scope. Bewusst unterschieden von agentTokenReadOnly: Dieses Token darf schreiben, nur nicht dies -- ein anderer Schreibzugriff oder ein anderer Bereich kann trotzdem gelingen. |
API-Token-Fehlercodes
Ein kundengebundenes API-Token (wsm_ mit customerId) bleibt in seinem Kunden. Organisationsweite Pfade haben keine Kundendimension, das Token erreicht sie deshalb gar nicht: alles unter /api/organizations/**, /api/organization/** (außer GET /api/organization), /api/users/**, /api/admin/**, /api/platform-admin/**, /api/escalation-config/**, /api/custom-pricing, /api/custom-fields/**, /api/im/**, /api/notifications/send-alert, Schreibzugriffe auf /api/package-configs/**, Anlegen und Löschen von Kunden sowie /api/customers/bulk. Kein API-Token, ob kundengebunden oder nicht, kann weitere Tokens erzeugen.
| Code | Bedeutung |
|---|---|
customerScopedTokenForbidden | (403) Ein kundengebundener Aufrufer hat einen organisationsweiten Pfad oder eine organisationsweite Ressource erreicht. Seit dem 04.09.2026 gilt das nicht nur für kundengebundene API-Token, sondern für jede kundengebundene Sitzung: ein editor mit Kundenzuweisungen oder ein Plattform-Admin im Kundenkontext, der einen Kunden anlegt oder ein organisationsweites Wartungsfenster (customerId: null) ändert oder löscht. Für Organisationseinstellungen, Abrechnung, Nutzer, Reports und organisationsweite Fenster ein organisationsweites Token oder eine unbeschränkte Nutzersitzung verwenden. |
apiTokenCannotMintTokens | (403) POST /api/customer/tokens wurde mit einem API-Token aufgerufen. Tokens erzeugt ein angemeldeter Nutzer, nie ein anderes Token. |
apiTokenPathAmbiguous | (403) Der Pfad enthält einen kodierten Schrägstrich, ein Punktsegment oder einen doppelten Schrägstrich. Die Token-Sperren normalisieren solche Pfade nicht, sie lehnen sie ab. |
csrfOriginRejected | (403) Ein Schreibzugriff mit Cookie-Sitzung kam von einer fremden Seite: Sec-Fetch-Site: cross-site oder ein Origin, der weder zum Host der Anfrage noch zu einem vertrauten Origin passt. API-Tokens sind nie betroffen; Browser-Integrationen rufen vom Origin der App selbst auf. |
Claim-Flow-Fehlercodes
Die service_auth-Registrierung und die Claim-Zeremonie (siehe Claim-Flow) können zusätzlich zu den obigen Basis-Agent-Auth-Codes diese error-/data.code-Werte liefern:
| Code | Bedeutung |
|---|---|
service_auth_not_enabled | (400) POST /agent/identity: der Identitätstyp service_auth ist auf diesem Deployment nicht aktiviert. |
invalid_claim_token | (400 bei POST /agent/identity/claim und POST /api/agent-claim/confirm, 404 bei GET /api/agent-claim/context) Der Claim-Token, Claim-Attempt-Token oder user_code ist unbekannt/abgelaufen/vom falschen Typ, der angemeldete Nutzer ist nicht die gebundene E-Mail, oder der Zugriff des bestätigenden Nutzers lässt sich nicht auf einen einzelnen Organisations-/Kunden-Scope auflösen. |
claimed_or_in_flight | (409) POST /agent/identity/claim: die Registrierung ist bereits geclaimt. |
claim_expired | (400) POST /agent/identity/claim: das äußere 24h-Claim-Fenster (ab Erstellung der Registrierung) ist verstrichen. |
authorization_pending | (400) POST /oauth2/token mit dem Claim-Grant: der Nutzer hat noch nicht bestätigt; weiter pollen. |
slow_down | (400) POST /oauth2/token mit dem Claim-Grant: schneller als das zurückgegebene interval (5s) gepollt; drossle dich. |
expired_token | (400) POST /oauth2/token mit dem Claim-Grant: der Claim-Token/die Registrierung ist abgelaufen, wurde widerrufen oder bereits eingelöst. |
ID-JAG-Fehlercodes
Registrierungen vom Typ identity_assertion (siehe Identity Assertion (ID-JAG)) können zusätzlich liefern:
| Code | Bedeutung |
|---|---|
issuer_not_enabled | (400) POST /agent/identity: Uptimeify vertraut in dieser Installation keinem Identity-Provider, identity_assertion ist also aus. |
login_required | (401) POST /agent/identity: die auth_time des ID-JAG ist älter als max_age (3600 s, wird mitgeliefert); beim Identity-Provider neu anmelden. |
interaction_required | (401) POST /agent/identity: die bestätigte E-Mail gehört zu einem Uptimeify-Konto, und der Agent ist noch nicht freigegeben; schick die Person zu claim.verification_uri_complete und polle mit claim_token. |
invalid_grant | (400) POST /oauth2/token mit dem jwt-bearer-Grant für einen freigegebenen ID-JAG-Agenten: der verlangte Scope geht über die Freigabe hinaus (scope_exceeds_grant), die Kundenbindung der Person hat sich geändert (binding_changed), die freigebende Person gibt es nicht mehr (no_human), oder nach dem Streichen der Schreibrechte für einen Global Supporter bleibt nichts übrig (nothing_left). Der Grund steht in error_description. |
invalid_request (err) | (400) POST /agent/event/notify: falscher Content-Type, leerer Körper, oder das Security Event Token wurde nicht angenommen. Welche Prüfung gescheitert ist, wird bewusst nicht gesagt. |
Verbundene-Apps-Fehlercodes (OAuth-Verbindungen)
GET /api/oauth/connections und DELETE /api/oauth/connections/:clientId (siehe Verbundene Apps) können zusätzlich zum Standard-401 unauthorized diese data.code-Werte liefern:
| Code | Bedeutung |
|---|---|
missingClientId | (400) Nur DELETE: der Pfad-Parameter :clientId fehlt. |
unknownOauthConnection | (404) Nur DELETE: der Aufrufer hat keine OAuth-Verbindung mit dieser client_id (bereits widerrufen, abgelaufen oder nie verbunden). |
Fehlercodes der OAuth-Zustimmung
Diese Codes stammen von der Zustimmungsseite, über die jede OAuth-/MCP-Autorisierung jetzt läuft
(POST, auf /api/oauth/consent). Wie die Step-up- und die Kontolöschungs-Route ist sie
session-gebunden und nicht Teil der öffentlichen API-Oberfläche, und Methode und Pfad stehen hier
aus demselben Grund getrennt: eine METHOD /api/...-Routenzeile auf diesen Seiten ist es, was
einen Endpunkt in /openapi.json veröffentlicht. Sie stehen hier, weil die Codes das sind,
worüber die Seite und jede Supportanfrage sprechen. Der Scope-Katalog selbst steht unter
Verbundene Apps.
Die Prüfungen laufen in der Reihenfolge unten, du bekommst also den Code der ersten, die fällt. Die letzte Zeile ist die Ausnahme: sie kommt von Better Auth, nachdem jede Prüfung dieser Liste bestanden ist.
| Code | Status | Bedeutung |
|---|---|---|
unauthorized | 401 | Keine aktive Sitzung. Der Endpunkt löst die Sitzung selbst auf, statt hinter der gemeinsamen Middleware zu liegen, denn er schreibt in den Datensatz, auf den ein Autorisierungscode gemintet wird. |
invalidConsentDecision | 400 | accept fehlt oder ist kein echtes Boolean. Bewusst kein Rückfall auf false: Ablehnen löscht die offene Autorisierung, und das darf keine fehlerhafte Anfrage stellvertretend für den Nutzer tun. |
missingConsentCode | 400 | consent_code fehlt oder ist kein String. |
unknownConsentCode | 400 | consent_code ist unbekannt oder abgelaufen. Zustimmungscodes sind kurzlebig; starte die Autorisierung im Client neu. |
consentCodeNotOwned | 403 | Die offene Autorisierung gehört einem anderen Konto als der aktuellen Sitzung, oder gar keinem Konto (verification.userId ist null). Wird vor beiden Zweigen geprüft, vor dem Ja wie vor dem Nein, denn das Nein ist der zerstörende. |
consentAlreadySettled | 400 | Dieser Autorisierung wurde bereits zugestimmt. Ihr Scope steht damit fest, und ein nachträgliches Verengen ließe das ausgegebene Token von dem Eintrag abweichen, den der Nutzer bestätigt hat. Wird vor beiden Zweigen geprüft, vor dem Ja wie vor dem Nein: Better Auth prüft dieselbe Bedingung vor seiner eigenen Ja-Verzweigung, ein Nein, das daran vorbeisprang, kam deshalb als consentForwardRejected zurück, mit dem die Zustimmungsseite nichts anfangen kann. |
emptyConsentSelection | 400 | Nur beim Ja: es standen Lesebereiche zur Wahl und keiner wurde ausgewählt. Lehne die Autorisierung ab, statt sie leer anzunehmen. Ein Client, der gar keinen MCP-Scope angefragt hat, sieht keine Häkchen und läuft nie hierher. |
consentScopeEscalation | 400 | Nur beim Ja: die Auswahl nennt einen Scope, den der Client nicht angefragt hat, oder einen, der gar kein feiner Lese-Scope ist. Eine Auswahl kann das Angefragte immer nur verengen. |
consentForwardRejected | 4xx | Better Auth selbst hat den weitergereichten Aufruf abgelehnt; der Status ist der, den es geliefert hat. Neben dem Code steht ein reason. Da alle Prüfungen darüber vor beiden Zweigen laufen, ist dieser Code nur über ein echtes Rennen erreichbar: jemand hat denselben Zustimmungscode zwischen unserem Lesen und unserem Weiterreichen abgewickelt. Wie bei den Codes darüber hilft ein zweiter Versuch nicht; starte die Autorisierung im Client neu. |
Fehlercodes für Status-Seiten-Abonnenten
Die Endpunkte zum Auflisten, Löschen und Exportieren von Status-Seiten-Abonnenten können zusätzlich zum Standard-401 unauthorized und dem zweistufigen statusPageNotFound-Verhalten (auf jeder Seite beschrieben: eine fehlerhafte oder unbekannte :id ist ein code-loses 400/404 aus der Public-ID-Auflösung; nur eine :id, die sich auflöst, aber an der Organisations-/Kunden-Scope-Prüfung scheitert, trägt data.code: statusPageNotFound) diese data.code-Werte liefern:
| Code | Bedeutung |
|---|---|
invalidSubscriberId | (400, nur Löschen) :subscriberId lässt sich nicht als positive Ganzzahl parsen. |
subscriberNotFound | (404, nur Löschen) :subscriberId existiert nicht oder gehört zu einer anderen Status-Seite als :id. |
Public IDs in DNS- und DNSBL-Monitoren
Die folgenden Pfad-Endpunkte können in den Docs und in Clients mit Public IDs verwendet werden:
GET /api/customer-domains/:customerDomainPublicIdPATCH /api/customer-domains/:customerDomainPublicIdDELETE /api/customer-domains/:customerDomainPublicIdGET /api/customer-ips/:customerIpPublicIdPATCH /api/customer-ips/:customerIpPublicIdDELETE /api/customer-ips/:customerIpPublicId
Legacy-Integer-IDs bleiben auch dort weiterhin kompatibel, die Public ID ist aber die bevorzugte Form für Integrationen und Postman-Collections.
Incident-Management-(IM)-API-Fehlercodes
Die öffentliche Incident-Management-API (POST /api/im/events, GET/POST /api/im/incidents/**, GET /api/im/teams, GET/POST /api/im/schedules/**, GET /api/im/on-call) erfordert einen organisationsweiten API-Token und kann diese data.code-Werte liefern:
| Code | Bedeutung |
|---|---|
imAccessDenied | (403) Der Aufrufer hat keinen Zugriff auf Incident Management: keine Session/kein Token, ein kunden-gescopter API-Token, ein Kunden-Login (readonly) oder globalsupporter-Login, oder ein Org-Login ohne IM-berechtigte Rolle (admin, editor, responder). |
imNotEnabled | (403) Incident Management wurde für diese Organisation nicht aktiviert. |
payloadTooLarge | (413, nur Events Ingest) Der Request-Body überschreitet 256 KB. |
invalidJson | (422, nur Events Ingest) Der Request-Body ist kein gültiges JSON. |
payloadTooDeep | (422, nur Events Ingest) Das geparste JSON-Payload ist mehr als 20 Ebenen tief verschachtelt. |
imIncidentNotFound | (404) Der Incident existiert nicht oder gehört zu einer anderen Organisation. |
imIncidentInvalidStatusTransition | (422, Incident bestätigen / Status ändern) Der angeforderte status ist für diesen Endpunkt kein gültiges Ziel (resolved und merged sind es nicht, stattdessen Incident lösen nutzen), der aktuelle Status des Incidents kann über diesen Endpunkt nicht überführt werden, oder status entspricht dem aktuellen Status des Incidents. |
imIncidentAlreadyClosed | (422, Incident lösen) Der Incident ist bereits resolved oder merged. |
imIncidentStatusConflict | (409, Status-/Resolve-Endpunkte) Der Status des Incidents hat sich zwischen Lesen und Schreiben gleichzeitig geändert. Sicher wiederholbar. |
imTeamWriteDenied | (403, Incident erstellen, Schedules, Schedule-Overrides) Die Rolle des Aufrufers ist nicht admin, und er ist kein Team-Admin (im_team_member.imRole = 'admin') des Ziel-Teams. |
imTeamMembershipRequired | (403, Incident lösen, merge, escalate-now, snooze, Statuswechsel, add-responder, Massenaktionen) Der Aufrufer ist IM-Nutzer, aber kein Mitglied des Teams des Incidents (incident.teamId) und weder Organisations-admin noch Plattform-Admin. Das Bestätigen bleibt jedem IM-Nutzer offen. Massen-Endpunkte lehnen die ganze Anfrage ab und nennen die betroffenen Ids in deniedIncidentIds. |
imTeamAdminRequired | (403, add-responder mit notify: true) Einen weiteren Responder zu alarmieren verlangt einen Team-Admin (im_team_member.imRole = 'admin') des Incident-Teams oder einen Organisations-admin; ein einfaches Teammitglied darf einen Responder ohne Benachrichtigung hinzufügen. |
imUserNotEligible | (400, Teammitglieder, Team-Einladung eines bestehenden Kontos, Schedule-Overrides) Der Zielnutzer ist deaktiviert oder hat keine IM-fähige Rolle (admin, editor, responder). |
imSnoozeRangeInvalid | (400, snooze) snoozedUntil liegt nicht in der Zukunft oder mehr als 7 Tage voraus. |
imTeamNotFound | (404, Schedules erstellen) teamId existiert nicht oder gehört zu einer anderen Organisation. |
imScheduleNotFound | (404, Schedule-Overrides) :id existiert nicht oder gehört zu einer anderen Organisation. |
invalidTeamId | (422, Incident erstellen) teamId gehört nicht zu deiner Organisation. |
invalidCustomerId | (422, Incident erstellen) customerId ist angegeben, gehört aber nicht zu deiner Organisation. |
imRoutingInvalidPolicyId | (422, Incident erstellen) escalationPolicyId ist angegeben, gehört aber nicht zu deiner Organisation. |
userNotFound | (404, Schedule-Overrides hinzufügen) userId gehört nicht zu deiner Organisation. |
GET /api/im/incidents, Incident erstellen, die Status-/Resolve-Endpunkte, Schedules erstellen und Schedule-Overrides hinzufügen können außerdem den generischen 400 invalidRequestBody bei fehlerhafter Eingabe liefern (z. B. ein unbekannter status-Filterwert, eine nicht-ganzzahlige :id, eine fehlende oder fehlerhafte Rotationsschicht). POST /api/im/events kann zusätzlich 429 Too Many Requests liefern (600 Anfragen/Minute pro Organisation, mit einem retryAfter-Feld, aber ohne data.code), oder 503 mit data.code: unavailable, wenn das Event nicht eingereiht werden konnte, sicher wiederholbar.
Nutzerverwaltungs-Fehlercodes
PATCH /api/users/:id (Doku) und DELETE /api/users/:id (Doku) schützen die Organisation davor, sich selbst auszusperren:
| Code | Bedeutung |
|---|---|
userSelfChangeForbidden | (400, update) Du hast versucht, deine eigene Rolle zu ändern oder dein eigenes Konto auf isActive: false zu setzen. Ein anderer Organisations-Admin muss das tun. |
lastAdminProtected | (400, update und delete) Das Ziel ist der letzte aktive Organisations-admin; er kann weder herabgestuft noch deaktiviert noch gelöscht werden. Zuerst einen weiteren Nutzer befördern. |
globalUserProtected | (403, update und delete) Das Ziel trägt ein Plattform-Flag (isGlobalAdmin oder isGlobalSupporter) und darf nur von einem Plattform-Admin geändert werden. |
Etiketten-Fehlercodes
POST /api/tags hält in created_by fest, welcher Mensch ein Etikett angelegt hat. Diese Spalte ist NOT NULL und trägt keinen Fremdschlüssel. Ein Aufruf über eine Agenten-Sitzung (eine MCP-Verbindung, create_tag) wird deshalb abgewiesen, statt einen Ersatzwert zu bekommen: irgendetwas dort hineinzuschreiben würde den Zweig „der Urheber darf ändern“ der Rechteprüfung still und dauerhaft kaputt machen.
| Code | Bedeutung |
|---|---|
authorizingUserUnresolved | (403, anlegen) Der Mensch, der diese Agenten-Sitzung autorisiert hat, ist nicht mehr auflösbar, es gibt also keine Nutzer-Id, die als Urheber des Etiketts festgehalten werden könnte. Das Etikett entsteht nicht. Ein Etikett hält immer den autorisierenden Menschen fest, nie den Agenten. Autorisiere die Verbindung neu oder lege das Etikett mit einer Sitzung an, die zu einer Person gehört. |
Die übrige Etikettenfläche (POST /api/tags, PATCH /api/tags/{id}, DELETE /api/tags/{id} sowie Anhängen und Abnehmen mit POST /api/monitor-tags und DELETE /api/monitor-tags) kann diese liefern:
| Code | Bedeutung |
|---|---|
invalidTagColor | (400, anlegen und ändern) Die Farbe gehört nicht zur Palette. Die Palette ist eine feste Menge und kein freies Hex-Feld. |
tagCustomerRequired | (400, anlegen) Ein kundengebundener Aufrufer mit mehr als einem Kunden im Geltungsbereich hat nicht gesagt, zu welchem das Etikett gehört. Wer genau einen Kunden im Geltungsbereich hat, bekommt diesen als Vorgabe. |
invalidCustomer | (400, anlegen und ändern) Der genannte Kunde gehört zu einer anderen Organisation. |
tagNotFound | (404) Es gibt kein Etikett dieser Id, das dieser Aufrufer sehen darf. |
invalidMonitorType | (400, anhängen und abnehmen) monitorType gehört nicht zu den unterstützten Monitorfamilien. |
monitorNotFound | (404, anhängen und abnehmen) Es gibt keinen Monitor dieser Art und Id, den dieser Aufrufer sehen darf. |
tagMonitorOrgMismatch | (400, anhängen) Etikett und Monitor gehören zu verschiedenen Organisationen. |
tagLimitReached | (422, anhängen) Der Monitor trägt bereits die höchstens fünf erlaubten Etiketten. |
Das Anhängen ist wiederholbar: ein Etikett, das schon am Monitor hängt, wird kein zweites Mal angehängt, und der Aufruf gelingt trotzdem.
Fehlercodes für Änderungswünsche
POST /api/monitors/:type/:id/change-requests und die ältere, nur für Websites gedachte Route unter /api/websites/:id/change-requests halten den anfragenden Menschen in requested_by fest, einer NOT NULL-Spalte mit Fremdschlüssel auf user. Ein Aufruf über eine Agenten-Sitzung wird deshalb abgewiesen statt mit einem Ersatzwert versehen:
| Code | Bedeutung |
|---|---|
authorizingUserUnresolved | (403, anlegen) Der Mensch, der diese Agenten-Sitzung autorisiert hat, ist nicht mehr auflösbar. Es entsteht keine Anfrage. Das Entscheiden einer Anfrage (PATCH /api/change-requests/:id) ist nicht betroffen: resolved_by ist nullable und bleibt dann einfach leer. |
Benachrichtigungskanal-Fehlercodes
| Code | Bedeutung |
|---|---|
configRequiredForTypeChange | (400, Benachrichtigungskanal aktualisieren) type wurde geändert, aber die Anfrage enthält keine config. Ein Typwechsel baut die Konfiguration aus der Anfrage neu auf; Felder des alten Typs werden nie übernommen, also die vollständige Konfiguration des neuen Typs mitsenden. |
notificationChannelRecipientExists | (409, Benachrichtigungskanal erstellen) im selben Geltungsbereich stellt bereits ein AKTIVER Kanal desselben Typs an dieselbe Adresse zu; data.channelId nennt ihn. Ein wiederholter Anlege-Aufruf, dessen Antwort nie ankam, landet hier, statt einen zweiten Kanal anzulegen, der jeden Alarm doppelt zustellen würde. Deaktivierte Kanäle blockieren nie, und der Name gehört nicht zur Prüfung. |
invalidRequestBody | (400, Kanal erstellen und Kanal aktualisieren) Eine Ziel-URL in config ist nicht parsebar, nutzt ein anderes Schema als http/https oder zeigt auf eine private, Loopback- oder Link-Local-Adresse, auf localhost, auf einen reservierten Privatnamen (.local, .internal, .home.arpa) oder auf einen Hostnamen ohne Punkt. Als Ziel-URL gilt jede Zeichenkette der obersten config-Ebene, die mit http:// oder https:// beginnt; Felder mit Nachrichteninhalt (bodyTemplate, webhookBodyTemplate, headers) sind ausgenommen. Es wird kein DNS aufgelöst, den Namen, der erst später privat auflöst, fängt die Prüfung bei der Zustellung ab. |
sourceChannelIdRequired | (400, anlegen) Die Anfrage will eine website-spezifische Ausnahme, nennt aber keine sourceChannelId. Eine Ausnahme leitet sich immer von einem vorhandenen Kanal der Organisation ab. |
invalidSourceChannelId | (400, anlegen) sourceChannelId ist keine brauchbare Id für eine Website-Ausnahme. |
sourceChannelNotFound | (404, anlegen) Der genannte Ausgangskanal liegt in keiner Organisation, die dieser Aufrufer sehen darf. |
websiteChannelOverrideExists | (409, anlegen) Für diese Website gibt es zu diesem Ausgangskanal schon eine Ausnahme. Die vorhandene ändern statt eine zweite anzulegen. |
channelIdRequired, invalidChannelId | (400, ändern und löschen) Der Pfad trägt keine Kanal-Id oder eine, die keine brauchbare Id ist. |
channelNotFound | (404, ändern und löschen) Es gibt keinen Kanal dieser Id, den dieser Aufrufer sehen darf. |
Ein Matrix-Kanal ist ein Sonderfall, den man kennen sollte: seine homeserverUrl wird beim Speichern auf ihre Form geprüft und bei der Zustellung noch einmal aufgelöst. Ein Homeserver, der nur auf eine private oder reservierte Adresse auflöst, wird bei der Zustellung abgewiesen, und die Abweisung steht an diesem Alarm, nicht als Fehler an diesem Endpunkt.
Fehlercodes für eigene Domains
Die Verify- und Activate-Endpunkte der White-Label-Domains und der Statusseiten-Domains beanspruchen einen Hostnamen für deine Organisation. Seit dem 04.09.2026 ist ein Hostname nur unter beanspruchten Einträgen (verified/active) eindeutig; ein pending-Eintrag blockiert niemanden und verfällt nach 7 Tagen.
| Code | Bedeutung |
|---|---|
domainAlreadyClaimed | (409) Eine andere Organisation hat diesen Hostnamen bereits verifiziert oder aktiviert. Die Antwort nennt diese Organisation nie. |
hostnameReserved | (400, Domain hinzufügen) Der Hostname ist einer der Plattform-Hosts, eine Subdomain davon oder liegt unter uptimeify.io. |
404 für Ressourcen außerhalb des Scopes
Seit dem 04.09.2026 antwortet jeder Ressourcen-Endpunkt mit 404 und seinem üblichen Not-found-Code (websiteNotFound, monitorNotFound, customerNotFound, customerDomainNotFound, customerIpNotFound, statusPageNotFound), sowohl wenn die Ressource nicht existiert als auch wenn sie existiert, aber nicht in deiner Organisation oder deinem Kundenscope liegt. Ein 403 bleibt rollenbasierten Ablehnungen vorbehalten (falsche Rolle, kundengebundenes Token auf einem Organisationspfad). Tests, die für „gehört einem anderen Kunden“ ein 403 erwarteten, müssen angepasst werden.
Fehlercodes für Rate-Limits und Größen
| Code | Bedeutung |
|---|---|
imIngestRateLimited | (429, POST /api/im/ingest/:token) Mehr als 300 Anfragen pro Minute für eine Alert-Quelle. data.retryAfter nennt die Wartezeit in Sekunden. |
imIngestByteBudgetExceeded | (429, POST /api/im/ingest/:token) Die Organisation hat innerhalb einer Stunde mehr als 50 MiB Ingest-Bodies gesendet. data.retryAfter nennt die Wartezeit in Sekunden. |
imOutboundRetryRateLimited | (429, POST /api/im/outbound/:id/retry) Mehr als 10 manuelle Wiederholungen pro Minute für eine Integration. |
invalidDateRange | (400, jeder check-history-, alert-history- und incident-history-Endpunkt) from oder to ist kein lesbares Datum, oder from liegt nach to. Vorher wurden solche Werte stillschweigend ignoriert. |
noLocationsConfigured | (400, POST /api/websites/:id/trigger-check) Die Website hat keinen Prüfstandort, auf dem sie laufen könnte. |
invalidScreenshotId | (400, Screenshot-Download) Die Screenshot-Id ist keine UUID. |
invalidHostname | (400, TCP-/SSH-/FTP-Monitor anlegen und ändern) Der Hostname ist kein öffentliches Ziel. Das kam vorher als 500 an. |
POST /api/organizations/:id/vat-id/validate ist auf 10 Prüfungen pro Stunde und Organisation begrenzt (429 mit data.retryAfter). POST /api/im/sources/:id/test-alert wendet jetzt dieselben Body-Grenzen an wie die Ingest-URL (payloadTooLarge, invalidJson, payloadTooDeep).
Fehlercodes für Webhook- und SMTP-Ziele
| Code | Bedeutung |
|---|---|
webhookHostHeaderForbidden | (400, Eskalationskonfiguration und IM-Kanal-Konfiguration) Ein eigener Host-Header wird für Webhooks nicht akzeptiert; der Header kommt immer aus der URL. |
smtpHostNotPublic | (400, PATCH /api/organization/smtp) Der SMTP-Host löst auf eine private, Loopback-, Link-local-, CGNAT- oder sonst nicht öffentliche Adresse auf. Dieselbe Prüfung läuft beim Versand erneut; ein Host, der nicht mehr öffentlich ist, fällt auf den Plattform-Mailanbieter zurück. |
Organisation gesperrt oder gelöscht
Ein Gate vor dem Kontozustand-Gate weiter unten sperrt jede /api/*-Anfrage, Sitzung, wsm_-API-Token oder wsma_-Agenten-Token gleichermaßen, anhand des Organisationsstatus. Plattform-Admins und -Supporter sind ausgenommen, wie überall sonst auch.
Eine suspended (gesperrte) Organisation bekommt 423 Organization suspended ohne data.code, außer für eine schmale Freigabeliste, die erreichbar bleibt, damit sie sich zurückzahlen kann: die Organisation lesen, das Kontingent ändern, die Datenexport-Endpunkte oben, die Tenant-Abfrage sowie die Mollie-Mandat-Rückkehr/-Reconcile- und Rabattcode-Route. Jede andere Route ist gesperrt.
Eine deleted (gelöschte) Organisation bekommt diese Freigabeliste NICHT und verliert zusätzlich zwei Routen, die bei suspended noch offen bleiben: Abmelden (POST auf /api/auth/sign-out) und beide Sitzungs-Bootstrap-Abfragen (GET auf /api/auth/get-session und /api/_internal/auth/get-session) antworten ebenfalls mit 423 Organization deleted und data.code organizationSuspended, bewusst, denn Abmelden löscht die Sitzungszeile aus Redis, und genau diese Zeile ist Beweismittel für die Löschprüfung. Vor dem 15.09.2026 erreichten Sitzung, API-Token und Agenten-Token einer gelöschten Organisation unverändert jede Route, genau die Lücke, die das schließt. „Gelöscht“ heißt hier status = 'deleted' ODER der dauerhafte Marker deletion_completed_at ist gesetzt. Maßgeblich ist, ob die Löschung abgeschlossen ist; der angezeigte Status der Organisation ist dafür nicht entscheidend. Zwei weitere Flächen folgen derselben Regel: /mcp weist API-Tokens und OAuth-Verbindungen einer gelöschten Organisation mit 423 ab, bevor ein Server aufgebaut wird, und eine Passwort-Reset-Mail wird nicht verschickt; der Endpunkt antwortet genau wie bei einer unbekannten Adresse. Eine gekündigte Organisation in ihrer Karenzzeit ist von diesem Gate nur unbetroffen, solange ihre Löschung noch nicht abgeschlossen ist. Diese Sperre greift innerhalb weniger Sekunden statt augenblicklich, weil der Kontozustand dahinter kurz zwischengespeichert wird.
Kontozustand an den Auth-Endpunkten
Better Auths eigene Routen (/api/auth/**, etwa change-password, update-user, two-factor/*, mcp/token-Refresh) weisen ein deaktiviertes Konto oder eine gesperrte Organisation genauso ab wie die API. Sign-in, Sign-out, Passwort-Reset, E-Mail-Verifikation und get-session bleiben erreichbar. Bei einer gelöschten Organisation ist die Liste enger: nur Sign-in, Registrierung, Passwort-Reset und E-Mail-Verifikation bleiben erreichbar, sign-out und get-session antworten wie oben mit 423, und eine neue Anmeldung scheitert ohnehin nach der Zugangsdatenprüfung, weil die Sitzungsanlage selbst abweist.
| Code | Bedeutung |
|---|---|
accountDeactivated | (403) Der angemeldete Nutzer ist deaktiviert. |
organizationSuspended | (423) Die Organisation des Nutzers ist gesperrt oder gelöscht. Eine gekündigte Organisation in ihrer Karenzzeit ist nicht betroffen, außer ihre Löschung ist bereits abgeschlossen. |
MFA-Fehlercodes
Diese Fehlercodes gehören zur Zwei-Schritt-Bestätigung (MFA) und können von ansonsten unabhängigen Endpunkten geliefert werden, sobald das jeweilige Gate greift:
| Code | Status | Bedeutung |
|---|---|---|
mfa_enrollment_required | 403 | Wird von jedem gegateten /api/*-Aufruf geliefert, wenn der Aufrufer (ein Plattform-Admin/-Supporter, sofern MFA_ENFORCE_PLATFORM_ADMINS aktiv ist, oder ein Mitglied einer Organisation, bei der der passende rollenbezogene Schalter requireMfaAdmins, requireMfaEditors oder requireMfaReadonly aktiv ist) die Zwei-Schritt-Bestätigung noch nicht eingerichtet hat. Richte die Zwei-Schritt-Bestätigung ein und wiederhole den Request. |
mfa_step_up_required | 403 | Wird nur bei ändernden API-Token-Aufrufen geliefert (POST, PATCH und DELETE auf /api/organization/tokens sowie POST und DELETE auf /api/customer/tokens; kundenbezogene Tokens lassen sich nicht aktualisieren) und nur wenn der Aufrufer die Zwei-Schritt-Bestätigung aktiviert hat: Der Aufrufer hat in den letzten 5 Minuten keinen Code bestätigt. Bestätige erneut einen Code und wiederhole den Request innerhalb des 5-Minuten-Fensters. Aufrufer ohne aktivierte MFA sehen diesen Code nie, für sie funktioniert das Erstellen und Widerrufen von Tokens unverändert. |
mfa_step_up_locked | 429 | Wird vom internen, nur session-basierten Step-up-Endpoint (POST, /api/account/mfa/step-up, nicht Teil der öffentlichen API-Oberfläche) geliefert, nachdem für dasselbe Konto 5 falsche Codes eingegeben wurden. 15 Minuten gesperrt (data.retryAfterSeconds); warte die Sperrzeit ab und bestätige dann erneut mit deinem Authenticator. |
nonUserSessionForbidden | 403 | Wird von POST /api/users/:id/mfa/reset für jeden Aufruf mit API-Token oder Agent-Access-Token geliefert, sowie von PATCH /api/organization(s), wenn der Request Body requireMfaAdmins, requireMfaEditors oder requireMfaReadonly enthält und der Aufrufer ein API-Token oder Agent-Access-Token ist. Beide MFA-Verwaltungsaktionen erfordern eine echte, eingeloggte Benutzer-Session, ein organisations-gescoptes API-Token mit Admin-Rechten reicht nicht aus. |
Diese Codes stehen als data.code-Wert im JSON-Fehlerbody, zusätzlich zum jeweils genannten HTTP-Status.
Fehlercodes der Kontolöschung
Diese Codes stammen aus der Kontolöschung in der App (GET, POST und DELETE, auf
/api/account/deletion-request). Wie der Step-up-Endpunkt oben sind diese Routen
session-gebunden und nicht Teil der öffentlichen API, sie stehen hier, weil die Codes das
sind, worüber die App und jede Supportanfrage sprechen.
Eine Löschung wird angefordert, nicht sofort vollzogen: die Anforderung setzt einen Termin 30 Tage in der Zukunft und ist bis dahin widerrufbar. Die Administratoren der Organisation werden über die Anforderung und über den Widerruf benachrichtigt, sie sind es, die eine Bereitschaftsrotation nachbesetzen können, die der Weggang offen ließe.
| Code | Status | Bedeutung |
|---|---|---|
accountDeletionSoleOwner | 409 | Der Aufrufer ist der letzte aktive Administrator (role = 'admin') seiner Organisation. Seinen User zu löschen hinterließe eine Organisation ohne Zugang, mit laufenden Monitoren und laufender Abrechnung, dieser Fall führt stattdessen auf die Organisations-Kündigung. data.organizationId nennt die Organisation. Sobald ein zweiter Administrator ernannt ist, gelingt dieselbe Anforderung. |
accountDeletionNotRequested | 404 | Das DELETE auf /api/account/deletion-request fand keine offene Anforderung zum Widerruf. Entweder wurde nie eine gestellt, oder sie ist bereits vollzogen. |
accountDeletionUserNotFound | 404 | Das Konto existiert in der Organisation des Aufrufers nicht. |
accountDeletionNotRecorded | 409 | Die Anforderung konnte nicht festgehalten werden, weil sie beim Schreiben gleichzeitig in einer anderen Sitzung widerrufen wurde. Ein erneuter Versuch ist unbedenklich. |
USt-IdNr.-Validierung: Fehlercodes
POST /api/organizations/:organizationPublicId/vat-id/validate (siehe USt-IdNr. validieren) kann zusätzlich zu den Standard-Codes 401 unauthorized, 403 forbidden und 404 organizationNotFound diese data.code-Werte liefern:
| Code | Status | Bedeutung |
|---|---|---|
vatIdMissing | 422 | Für die Organisation ist keine vatId gespeichert. Setze zuerst eine mit Organisation aktualisieren. |
vatIdChangedDuringValidation | 409 | vatId oder countryCode wurde gleichzeitig geändert, während die VIES-Abfrage lief. Das noch laufende Ergebnis wird verworfen, statt auf Daten geschrieben zu werden, die so nie geprüft wurden. Gefahrlos wiederholbar. |
Update Billing Details: benötigter Body
Endpoint:
PATCH /api/organizations/:organizationPublicId/billing
Aktuell unterstützter Request-Body:
{
"billingEmail": "billing@deinkunde.com"
}Der Endpoint akzeptiert derzeit nur billingEmail.