Wenn etwas nicht geht: Fehlercodes und Abhilfe
Die Schnittstelle antwortet auf jeden Fehler mit einem Statuscode, einem maschinenlesbaren Kürzel und einer Klartextmeldung. Diese Seite ordnet sie ein.
1. Die Fehlerantwort lesen
{
"error": {
"status": 403,
"code": "ip_not_allowed",
"message": "Der Zugriff von 203.0.113.99 ist für diesen
Schlüssel nicht freigegeben."
}
}
- status — der HTTP-Statuscode, für die grobe Einordnung
- code — das Kürzel, auf das dein Programm reagieren sollte (es bleibt stabil, auch wenn sich der Wortlaut ändert)
- message — die Klartextmeldung, die du einem Benutzer zeigen kannst
- details — nur bei Prüffehlern: welche Felder betroffen sind und warum
2. Alle Fehlercodes
| Status | Kürzel | Bedeutung |
| 400 | bad_request | Unbekannter Parameter, ungültiger Vergleich, kaputtes JSON |
| 401 | unauthorized | Kein oder unbekannter Schlüssel |
| 403 | forbidden | Die Stufe des Schlüssels reicht nicht |
| 403 | key_expired | Der Schlüssel ist abgelaufen |
| 403 | key_not_yet_valid | Der Schlüssel gilt erst ab einem späteren Datum |
| 403 | ip_not_allowed | Die Absender-Adresse steht nicht in der Freigabeliste |
| 403 | https_required | Der Aufruf kam über unverschlüsseltes HTTP |
| 404 | not_found | Bereich oder Datensatz gibt es nicht |
| 405 | method_not_allowed | Diese Methode ist hier nicht möglich |
| 409 | conflict | Doppelter Wert, es hängen Daten daran, oder eine Fachregel greift (z. B. Zahlung auf einen Entwurf, Überzahlung) |
| 409 | record_locked | Der Datensatz ist gesperrt: festgeschriebene Rechnung, stornierte oder abgerechnete Buchung — die Meldung nennt den Ausweg |
| 410 | gone | Der Datensatz liegt im Papierkorb |
| 412 | precondition_failed | Zwischenzeitlich geändert (If-Match passt nicht mehr) |
| 413 | payload_too_large | Die Anfrage ist größer als 4 MB |
| 415 | unsupported_media_type | Falscher Content-Type — erwartet wird application/json |
| 422 | validation_failed | Prüffehler; details nennt die Felder |
| 429 | rate_limit_exceeded | Zu viele Anfragen je Minute |
| 429 | ip_blocked | Absender nach zu vielen Fehlversuchen gesperrt |
| 500 | internal_error | Interner Fehler; die Meldung enthält ein Kennzeichen für die Nachfrage |
| 503 | service_unavailable | Die Schnittstelle ist abgeschaltet (api.enabled) |
3. Die häufigsten Stolperstellen
„401, obwohl der Schlüssel stimmt“
Bei manchen Server-Einstellungen kommt die Kopfzeile Authorization nicht bei PHP an. Benutze dann ersatzweise:
X-Api-Key: 3f7c9a2e-4b81-4d6f-9a03-7c5e12f8ab44
Das ist gleichwertig.
„403 ip_not_allowed“
Im Schlüssel ist eine IP-Freigabeliste hinterlegt, und dein Server steht nicht darin. Ruf von diesem Server aus /api/v1/me auf — unter firewall.your_ip steht die Adresse, mit der PetWorkers ihn sieht. Genau die gehört in die Liste.
Achtung bei Anschlüssen mit wechselnder Adresse: Dort ist eine feste Freigabeliste unpraktisch. Nimm stattdessen den Bereich des Anbieters, oder verzichte auf die Liste und begrenze über Stufe und Laufzeit.
curl bricht bei Filtern ab
Der Schalter -g fehlt. Ohne ihn hält curl die eckigen Klammern für eine Bereichsangabe:
curl -g -H "Authorization: Bearer $KEY" "$BASE/contacts?lastname[like]=Mül"
„400 Unbekannter Parameter oder Feld“
Ein Feldname stimmt nicht. Die richtigen Namen liefert:
GET /api/v1/contacts/_schema
Die Namen sind die technischen Spaltennamen (lastname, nicht „Nachname“). Zu jedem steht die deutsche Bezeichnung dabei.
„409 record_locked“
Der Datensatz ist in einem Endzustand — eine festgeschriebene Rechnung, eine stornierte Buchung, ein abgerechneter Termin. Das ist kein Fehler deines Programms, sondern eine Regel der Software: Solche Datensätze werden nicht mehr verändert. Die Meldung sagt dir, was stattdessen geht (Korrekturrechnung über /cancel, Notizfelder, oder die Oberfläche). Welche Bedingung sperrt, steht in /_schema unter meta.locked_when.
„422: … kann nicht geschrieben werden (nur lesbar — setzt die Fachlogik)“
Du versuchst, ein Zustandsfeld zu setzen — finalized, invoicenumber, paid, checkin, sent und ähnliche. Diese Felder vergibt die Software selbst. Für Rechnungen gibt es dafür die Aktionen /close und /cancel, für Zahlungen /cancel; alles andere läuft über die Oberfläche.
„405 … unterstützt kein Ändern“
Manche Bereiche sind absichtlich nur lesbar oder nur anlegbar: Zahlungen werden nie geändert oder gelöscht (nur storniert), E-Mails, Kassenbuch, Kundenkonten und Onlinebuchungen nur gelesen. Was ein Bereich kann, steht in /api/v1/ unter allowed.
„422 beim Anlegen“
Schau in details — dort steht für jedes Feld, was nicht stimmt. Pflichtfelder findest du vorher in /_schema: dort steht bei jedem Feld required: true oder false.
Leere Liste, obwohl Daten da sind
Zwei Möglichkeiten:
- Die Datensätze liegen im Papierkorb. Probier
?deleted=include. - Ein Filter greift schärfer als gedacht. Lass ihn versuchsweise weg.
„429 rate_limit_exceeded“
Dein Programm fragt zu schnell. Zwei Wege:
- Auf
Retry-Afterreagieren und so lange warten. Das ist der richtige Weg, und alle Beispielskripte machen es so. - Das Kontingent des Schlüssels erhöhen (System → API-Schlüssel → Firewall → Anfragen je Minute).
Meist lässt sich die Anzahl der Aufrufe aber verkleinern: per_page erhöhen statt viele kleine Seiten zu holen, ?fields= benutzen, und mit ?modified[gte]= nur das Neue abfragen.
„500 internal_error“
Ein Fehler in der Software. Die Meldung enthält ein Kennzeichen wie 9A9CF305 — nenn es bei der Nachfrage, dann lässt sich der Vorgang im Protokoll finden.
4. Das Zugriffsprotokoll
Unter System → API-Schlüssel → Zugriffsprotokoll siehst du, was tatsächlich angekommen ist: Zeitpunkt, Schlüssel, Methode, Pfad, Statuscode, Dauer und Absender-Adresse.
Wenn du eine Fehlersuche machst, stell den betroffenen Schlüssel vorübergehend auf Jeden Aufruf mitschreiben. Danach zurückstellen — sonst wächst das Protokoll unnötig.
5. Was du selbst prüfen kannst
Vier Aufrufe, die die häufigsten Ursachen ausschließen:
# 1. Ist die Schnittstelle überhaupt erreichbar?
curl -sg -H "Authorization: Bearer $KEY" "$BASE/health"
# 2. Was darf mein Schlüssel, und wie sieht der Server mich?
curl -sg -H "Authorization: Bearer $KEY" "$BASE/me"
# 3. Kommt dieser Bereich in meiner Liste vor?
curl -sg -H "Authorization: Bearer $KEY" "$BASE/" | grep contacts
# 4. Wie heißen die Felder wirklich?
curl -sg -H "Authorization: Bearer $KEY" "$BASE/contacts/_schema"
Der zweite Aufruf beantwortet die meisten Fragen: Stufe, Restlaufzeit, Schreibrecht, erlaubte Bereiche, Tempo-Kontingent und die erkannte Absender-Adresse stehen alle in einer Antwort.