Konventionen#
Diese gelten für die ganze Schnittstelle. Wer das einmal liest, muss es nicht bei jeder Ressource neu herausfinden.
Versionierung#
Die Version steht im Pfad: /api/public/v1/.
Additive Änderungen — neue Felder, neue Endpunkte, neue optionale Parameter, neue Enum-Werte — kommen innerhalb von v1. Dein Client muss sie vertragen, und dafür braucht es zwei Gewohnheiten: ignoriere Antwortfelder, die du nicht kennst, und brich nicht bei einem unbekannten Enum-Wert ab.
Entfernungen, Umbenennungen, Typwechsel und alles, was die Validierung verschärft, erhöhen die Version, mit einem Überlappungsfenster, in dem beide laufen.
Antworten auf Listenabfragen#
Listen sind cursor-paginiert und kommen immer in derselben Hülle zurück:
{
"object": "list",
"data": [ { "object": "shift", "id": "0194d1c0-…", "…": "…" } ],
"has_more": true,
"next": "https://app.shiftavo.com/api/public/v1/shifts/?cursor=…"
}
nextist eine undurchsichtige URL. Folge ihr unverändert; bau oder zerleg keine Cursor.Hör auf, wenn
has_morefalseist.page_sizesteuert die Seitenlänge.
Der Unterscheider object#
Jede Ressource führt ein Feld "object", das ihren Typ nennt — "shift", "location", "employee". Verzweige darüber statt über die Form der Nutzlast, besonders in Webhook-Handlern und gemischten Sammlungen.
Fehler#
Eine Hülle, überall:
{
"error": {
"type": "conflict_error",
"code": "not_cancellable",
"message": "…",
"details": [],
"param": "start_date"
}
}
detailsist immer vorhanden — eine Liste der Fehler auf Feldebene, leer, wenn der Fehler keine Aufschlüsselung nach Feldern hat.paramerscheint nur, wenn genau ein Feld schuld ist.
Es gibt sieben Typen. Jeder bildet auf einen Status ab, ausser invalid_request_error, dem Sammelbecken für die nicht tabellierten 4xx, wo der code die Unterscheidung trägt:
|
Status |
Hinweise |
|---|---|---|
|
400 · 405 · 406 · 415 |
|
|
400 |
|
|
401 |
fehlendes / ungültiges / abgelaufenes Token |
|
403 |
|
|
404 |
wird auch statt 403 für Datensätze ausserhalb deines Unternehmens zurückgegeben — kein Rückschluss auf die Existenz |
|
409 |
Zustandskonflikte: |
|
429 |
bringt |
Verzweige über type für die Behandlungsklasse und über code für den konkreten Fall. Beide sind stabil; message ist es nicht — behandle es nur als für Menschen lesbar.
Idempotenz#
Sende bei Erstellungen einen Idempotency-Key-Header. Eine wiederholte Anfrage spielt dann das erste Ergebnis erneut aus, statt einen zweiten Datensatz anzulegen.
Denselben Schlüssel mit einem anderen Body zu verwenden, ergibt einen 400 idempotency_key_reused — der Schlüssel bezeichnet eine Anfrage, nicht einen Endpunkt.
Anfragen nachverfolgen#
Jede Antwort, bei Erfolg wie bei Fehler, trägt einen X-Request-Id-Header. Protokollier ihn. Sendest du selbst ein X-Request-Id, wird es zurückgespiegelt, sodass eine Trace-ID von Ende zu Ende mitlaufen kann. Nenn sie in jeder Supportanfrage.
Übergänge im Lebenszyklus#
Ein Übergang wird als POST auf einen Aktions-Unterpfad ausgelöst: POST …/<id>/publish/, …/cancel/, …/revoke/.
Ein Übergang, der nicht möglich ist — einen Entwurf absagen, etwas bereits Veröffentlichtes veröffentlichen —, ist ein 409 mit einem eigenen Code (not_cancellable, not_publishable, not_unpublishable). Nie ein 200, das stillschweigend nichts getan hat.
Löschen#
DELETE gibt einen Stummel zurück, keinen 204:
{ "id": "0194d1c0-…", "object": "shift", "deleted": true }
Ein DELETE hat nie einen Body. Alles, was ein Löschvorgang braucht, steht im Pfad oder in der Query, sodass ein generierter Client alle Fälle ausdrücken kann. (HTTP lässt die Bedeutung eines DELETE-Bodys offen, und OpenAPI 3.0 sagt Konsumenten, einen solchen zu ignorieren — ein Body-Feld hier würde also stillschweigend aus deinem Client verschwinden und als 400 zurückkommen, den das Schema nicht erklären könnte.)
Löschen ist nicht das sanfte Verb#
Wo eine Ressource beides anbietet, sind sie keine Synonyme, und die falsche Wahl lässt sich nicht rückgängig machen:
Ressource |
|
Das sanfte Verb |
|---|---|---|
|
entfernt die Schicht; die Zugeteilten einer veröffentlichten Schicht werden benachrichtigt, und ein laufendes Angebot in der Schichtbörse wird vorher zurückgezogen |
|
|
endgültige Entfernung: Zugang entzogen, die Person verschwindet aus jeder Sammlung, und ein späterer |
|
|
nur solange ausstehend oder abgelehnt — ein bestätigter oder zurückgezogener Antrag trägt eine Journalbuchung, deshalb ein |
|
Wiederkehrende Datensätze#
Das Löschen eines einzelnen Vorkommens eines wiederkehrenden shifts- oder availability-Datensatzes braucht einen ausdrücklichen Geltungsbereich:
DELETE …/<id>/?scope=this|following|all # shifts
DELETE …/<id>/?scope=single|following|all # availability
Lässt du ihn bei einer Serie weg, bekommst du einen 409 delete_scope_required statt einer Vermutung darüber, was du gemeint hast.
Ratenbegrenzungen#
Ein 429 bringt einen Retry-After-Header mit. Halt dich daran und warte bei Wiederholungen exponentiell länger.