Betrieb

Die Fragen, die in der ersten Woche aufkommen: Limits, Wiederholungen, was bei einer Trennung mitten im Aufruf passiert, und warum version springt.

Rate-Limit

60 Anfragen pro Minute, je Endpunkt, je Quell-IP — gleitendes Fenster. Darüber antwortet Petla mit 429 und dem Code rate_limited.

Die Fenster gelten JE ENDPUNKT

Ihr GET /events-Polling verbraucht also nicht das Budget, das eine Veröffentlichung braucht. Der Schlüssel ist aber die Quell-IP und nicht die Verbindung: Ein Praxisverwaltungssystem, das viele Praxen über eine Adresse bedient, teilt sich pro Endpunkt einen Topf. Danach sollten Sie dimensionieren.

Jede erfolgreiche Antwort trägt das Fenster, eine 429 zusätzlich Retry-After:

KopfzeileAufBedeutung
RateLimit-Limit · RateLimit-Remaining · RateLimit-Reset2xx und 429Das Fenster — damit Sie sich takten können, bevor Sie abgewiesen werden.
Retry-Afternur 429Ganze Sekunden Wartezeit (RFC 9110). Halten Sie sich daran, statt fest zu schlafen.

Bis zum 04.09.2026 kamen die drei RateLimit-*-Kopfzeilen nur mit der 429 — womit der Satz in der rechten Spalte nicht stimmte: wer sie erst mit der Abweisung sieht, kann sich nicht vorher takten. Sie liegen jetzt auf jeder erfolgreichen Antwort.

Retry-After steht bewusst nicht auf einer 2xx: einer gerade beantworteten Anfrage zu sagen, sie solle warten, ist eine Anweisung ohne Bezug — und ein Client, der sich daran hält, schläft umsonst. Fehlerantworten außer der 429 tragen beides nicht; lesen Sie das Fenster von Ihrer letzten erfolgreichen Antwort ab.

Retry-After ist nie 0 und nie negativ: mindestens 1 Sekunde, weil „jetzt sofort noch einmal“ ein Rat ist, den dieser Endpunkt nicht einhalten kann, und höchstens 300 — fünf Fenster — weil ein größerer Wert eine Uhren-Abweichung zwischen uns wäre und keine echte Wartezeit.

Fallen Sie trotzdem sauber zurück

Die vier Kopfzeilen liegen auf beiden Systemen an, und das Limit gilt dort ohnehin.

Bauen Sie dennoch beides: Retry-After auswerten wenn vorhanden, sonst mit exponentiell wachsendem Abstand zurückfallen. Ein 429 kann auch von einer Schicht vor uns stammen, die diese Kopfzeilen nicht setzt.

Petla fällt offen aus

Ist unser Rate-Limit-Backend nicht erreichbar, werden Anfragen bedient statt abgewiesen. Das Ausbleiben einer 429 ist also kein Beleg dafür, dass Sie innerhalb des Limits liegen — ein Client, der nur bremst, wenn man es ihm sagt, liegt im Alltag richtig und genau an dem Tag falsch, an dem es darauf ankommt.

Wiederholungen: Idempotency-Key

Jeder Schreibvorgang nimmt die Kopfzeile Idempotency-Key (eine UUID) entgegen. Eine Wiederholung innerhalb von 24 Stunden liefert die ursprüngliche Antwort zurück, mit Idempotent-Replayed: true. Derselbe Schlüssel mit anderen Parametern ist ein 400 (idempotency_key_reused).

Hinweis

Die Kopfzeile ist optional, und ihr Fehlen ist kein Fehler. Eine Veröffentlichung ist ohnehin idempotent, weil sie einen Zeitraum ersetzt — zweimal gesendet stehen dieselben Zeilen da. Der Schlüssel kauft Ihnen eine stabile Antwort, nicht ein sicheres erneutes Senden.

Die Schlüsselreihenfolge ist egal

Verglichen wird ein Fingerabdruck des Rumpfes, und der wird über eine von Petla normalisierte Schlüsselreihenfolge gebildet. Zwei Anfragen mit denselben Werten in anderer Reihenfolge sind derselbe Fingerabdruck — Sie müssen dafür nichts tun. Was einen 400 auslöst, sind andere Werte unter demselben Schlüssel, und ein 4xx dürfen Sie laut Vertrag nicht unverändert wiederholen.

⛔ Die Reihenfolge innerhalb von Arrays wird bewusst nicht normalisiert: Zwei busy-Arrays in anderer Reihenfolge sind zwar dieselbe Veröffentlichung, aber das zu entscheiden wäre nicht Aufgabe eines Fingerabdrucks.

Die Bestätigung braucht keinen Schlüssel

POST /appointments/{id}/ack ist der eine Schreibvorgang ohne Idempotency-Key: Termin-ID und Verbindung identifizieren den Vorgang bereits eindeutig. Eine identische Wiederholung ist von sich aus eine Wiedergabe und antwortet 200 mit Idempotent-Replayed: true; ein widersprechendes zweites Urteil ist ein 409 (already_acknowledged) statt eines zweiten Schreibvorgangs.

Trennung mitten im Betrieb

Beendet die Praxis die Verbindung bei Petla, antwortet Ihr nächster Aufruf mit 401 und dem Code connection_revoked. Ab da müssen Sie die Aufrufe einstellen. Beendet die Praxis bei Ihnen, rufen Sie DELETE /connections/{connectionId} auf.

Der 401 IST die Zustellung

Es gibt bewusst kein Ereignis im Strom, das Sie über Ihre eigene Trennung informiert — und zwar aus einem strukturellen Grund: Eine Trennung lässt jeden weiteren Aufruf mit 401 antworten, auch den auf GET /events. Die eine Verbindung, um die es geht, könnte das Ereignis also nie lesen.

Deshalb ist connection_revoked ein eigener Code und nicht ein unauthorized: Er bedeutet aufhören, während unauthorized zum Nachprüfen eines gültigen Tokens führen würde.

Hinweis

Eine Trennung löscht auf keiner Seite bereits geschriebene Termine.

Zugangsdaten wechseln

POST /credential/rotate, authentifiziert mit Ihren aktuellen Zugangsdaten. Sie erhalten neue zurück — genau einmal — und die alten funktionieren noch weiter, bis previousCredentialExpiresAt erreicht ist. Standard sind 24 Stunden; graceHours nimmt 1 bis 168.

Wozu das Übergangsfenster dient

Ohne Übergangsfenster wäre ein Wechsel ein harter Schnitt: In dem Moment, in dem die neuen Zugangsdaten gelten, scheitert der nächste Aufruf jeder Praxis, die Sie betreuen. Sie können ein neues Geheimnis nicht in derselben Sekunde auf Ihrer gesamten Flotte ausrollen, in der Sie uns bitten, unseres zu ändern.

Beide Zugangsdaten funktionieren innerhalb des Fensters. Rollen Sie Ihre Flotte darin aus, dann fällt nichts aus.

Das Geheimnis wird genau einmal angezeigt

Petla speichert nur den Hash. Es gibt keinen Endpunkt, der Zugangsdaten zurückliest: Mit einem solchen Endpunkt genügte ein Datenbank-Lesezugriff, um sich als beliebiger Anbieter auszugeben. Sichern Sie den Wert, bevor Sie die Antwort schließen.

Zwei Fälle, in denen ein Wechsel abgelehnt wird

Ein Wechsel, der mit den alten Zugangsdaten authentifiziert wurde, wird mit 401 abgelehnt — auch innerhalb des Fensters. Sonst könnte, wer ein abgeflossenes altes Geheimnis besitzt, sich in jedem neuen Fenster erneut weiterwechseln und Sie dauerhaft aus Ihrer eigenen Integration aussperren.

Ein zweiter Wechsel bei offenem Fenster wird mit 409 und rotation_in_progress abgelehnt, samt meta.previousCredentialExpiresAt.

Warum der Wiederholungsversuch abgelehnt wird

Der Endpunkt gibt ein Geheimnis genau einmal heraus und speichert nur dessen Hash. Würden wir den Wiederholungsversuch bedienen, würden wir genau die Zugangsdaten außer Kraft setzen, die Sie nachweislich noch besitzen, und ein Fenster um ein Geheimnis öffnen, das Sie nie erhalten haben.

Ging Ihre erste Antwort verloren, funktionieren Ihre alten Zugangsdaten also weiter — meta sagt Ihnen, wie lange. Haben Sie die neuen wirklich verloren, schreiben Sie uns: Wir stellen außer der Reihe neue aus. Dieser Weg widerruft die alten sofort und ohne Übergangsfenster, denn er existiert für ein verlorenes oder abgeflossenes Geheimnis — und ein abgeflossenes Geheimnis einen Deploy-Zyklus lang am Leben zu lassen, heißt, es am Leben zu lassen.

Hinweis

Das sind die Anbieter-Zugangsdaten, nicht ein Verbindungs-Token. Ein Wechsel berührt keine einzige Verbindung: Jede Praxis bleibt gekoppelt, kein Cursor bewegt sich, streamEpoch bleibt unverändert.

Zeitumstellung

Jede Nutzlast mit einer Zeit trägt auch timeZone — lokale Wanduhrzeit plus IANA-Kennung, nie ein blanker Zeitpunkt und nie ein fester Offset.

{ "start": "2026-12-15T09:00:00", "timeZone": "Europe/Berlin" }

Dieser Termin wird im September gebucht, als Europe/Berlin gerade +02:00 ist. Im Dezember gilt +01:00. Hätten Sie 2026-12-15T09:00:00+02:00 gesendet, wäre daraus 08:00 Ortszeit geworden — die Praxis hätte eine Stunde zu früh gewartet, und niemand hätte etwas geändert. Mit Zonenkennung bleibt es 09:00, weil die Zone die Umstellung selbst mitbringt.

version springt — und das ist richtig

version ist eine monoton steigende Ganzzahl je Termin und die einzige Antwort auf Reihenfolge, Dopplung und Veraltung: Wer Version 3 schon angewendet hat, muss eine verspätet eintreffende Version 2 ignorieren.

Monoton, nicht fortlaufend

Es ist eine Ressourcen-Version und bewegt sich bei jeder Änderung, die Petla festhält — auch bei solchen, die kein Feld der Antwort abbildet. 3 → 7 zwischen zwei Lesevorgängen heißt „etwas hat sich geändert, lies neu“, nie „Ihnen fehlen vier Ereignisse“.

⛔ Behandeln Sie eine Lücke nicht als verlorenes Ereignis und setzen Sie deswegen keinen Cursor zurück. Was Änderungen aufzählt, ist der Ereignisstrom.