Ereignisse abrufen

Ein Strom, zwei Transportwege. Polling ist Pflicht, Webhooks sind eine optionale Ergänzung darüber.

Polling Core

GET /events?cursor=<opaque>&limit=100

{
  "events": [
    { "id": "evt_…", "type": "appointment.created", "data": { … } }
  ],
  "nextCursor": "…",
  "hasMore": false
}
Ein abgelaufener Cursor liefert 410, nie eine leere Seite

Ein abgelaufener Cursor liefert 410 Gone — niemals eine leere Seite.

Eine Synchronisierung, die erfolgreich aussieht und dabei nichts überträgt, bleibt unbemerkt: Der Abruf meldet keinen Fehler, Termine kommen nicht an, und es gibt kein Signal darüber. Behandeln Sie 410 als Aufforderung zur vollständigen Neusynchronisierung — Aufruf ohne Cursor.

Webhooks Extended

Petla implementiert Standard Webhooks: die Kopfzeilen webhook-id, webhook-timestamp und webhook-signature; HMAC-SHA256 über {id}.{timestamp}.{body}, base64-kodiert, mit dem Präfix v1,.

Achtung

Webhooks ersetzen das Polling nicht. Sie verkürzen die Latenz. Ein Partner mit Webhooks, der nicht mehr abruft, verliert jedes Ereignis, dessen Zustellung endgültig fehlschlägt.

Einschalten — drei Schritte

⛔ Bis zum 07.09.2026 gab es keinen Weg dorthin. Diese Seite beschrieb vollständig, was Petla sendet, und nannte keinen Endpunkt, an dem man eine URL hinterlegt — man konnte also einen geprüften Empfänger bauen und hatte nichts, worauf man ihn richten konnte.

  1. Deklarieren. PUT /capabilities mit "transport": "webhook". ⚠️ Das ist das Feld transport und kein Eintrag in capabilities: die Liste sagt, was Ihr System kann, das Feld sagt, wie Sie den Strom zugestellt bekommen wollen. Ohne das antwortet alles Weitere mit 400 capability_not_declared.
  2. Registrieren.
    PUT /connections/webhook
    { "url": "https://pms.example.com/petla/webhooks" }
    
    { "url": "…", "secret": "whsec_…", "createdAt": "…", "disabled": false }
  3. Fertig. Ereignisse beginnen ab dem aktuellen Kopf des Protokolls.
Das Secret kommt genau einmal

Nur in der Antwort, die es erzeugt. GET /connections/webhook gibt es nie zurück — die Leseabfrage wählt die Spalte gar nicht erst aus. Speichern Sie es, bevor Sie antworten; der einzige Weg zu einem neuen ist POST /connections/webhook/secret/rotate.

Achtung

Registrieren beginnt am Kopf, nicht am Anfang. Einen Webhook zu hinterlegen heißt „was ab jetzt passiert“; GET /events ohne Cursor ist der eine Aufruf, der „die Nachlieferung“ heißt. Das gilt auch für eine geänderte URL: der neue Endpunkt bekommt nicht, was der alte verpasst hat. Wenn Sie umziehen, rufen Sie danach einmal /events ab.

Dieselbe URL erneut zu registrieren ist idempotent und rotiert das Secret nicht — es aktiviert lediglich einen durch 410 Gone deaktivierten Endpunkt wieder. Ein PUT, das dabei das Secret rotierte, würde jede Signatur ungültig machen, die Sie gerade prüfen.

Welche URL Petla akzeptiert

Nur https, keine Zugangsdaten in der Adresse, keine Loopback-, privaten oder Link-Local-Adressen. Sonst 400 webhook_url_invalid, und fields.url nennt die verletzte Regel — drei Regeln aus drei verschiedenen Gründen, nachzulesen unter Fehler.

Secret rotieren

POST /connections/webhook/secret/rotate

{ "secret": "whsec_…", "previousSecretValidUntil": "2026-09-08T12:00:00Z" }

Das alte Secret signiert 24 Stunden lang weiter, neben dem neuen: webhook-signature ist eine durch Leerzeichen getrennte Liste, und ein konformer Empfänger akzeptiert, wenn irgendein Eintrag passt. Sie können das neue Secret also in Ihrem eigenen Tempo über Ihre Praxisserver ausrollen, ohne dass dazwischen etwas verloren geht — dasselbe Kulanzfenster, das die Partner-Zugangsdaten bekommen.

Replay-Schutz und Deduplizierung

Weisen Sie eine Zustellung zurück, deren webhook-timestamp älter als 5 Minuten ist. ⛔ Sicher ist das nur, weil Petla jede Wiederholung neu stempelt: die Leiter reicht bis 24 Stunden, eine Wiederholung mit dem ursprünglichen Zeitstempel würde also von jedem konformen Empfänger als Angriff verworfen. Stabil über alle Versuche bleibt webhook-id — das ist der Wert zum Deduplizieren.

Zustellgeschwindigkeit und Umfang der Zusage

Die Zusage ist das dauerhafte Ereignisprotokoll plus ein Fünf-Minuten-Durchlauf. Die Geschwindigkeit ist ein bestmöglicher Anstoß aus der Anfrage, die das Ereignis ausgelöst hat: Petlas eigene Buchungs- und Trennungspfade bitten binnen einer Sekunde um einen Durchlauf, alles andere erreicht Sie beim nächsten Durchlauf. Verloren geht nichts — das steht hier, damit eine Zustellung nach vier Minuten statt nach vier Sekunden als dokumentierte Untergrenze lesbar ist und nicht als Störung.

Ereignistypen

appointment.created · appointment.rescheduled · appointment.cancelled · appointment.updated · connection.revoked

Achtung

Offene Aufzählung. Ignorieren Sie, was Sie nicht kennen. Petla darf jederzeit einen Typ ergänzen, ohne dass das eine brechende Änderung ist.

connection.revoked

Die Praxis hat die Verbindung bei Petla beendet. Ihr System muss die Aufrufe einstellen; weitere Aufrufe liefern connection_revoked (401). Beendet die Praxis stattdessen bei Ihnen, rufen Sie DELETE /connections/{connectionId} auf.

Nicht tun

Das Trennen einer Verbindung darf auf keiner Seite bereits geschriebene Termine löschen.

Fehler

Der einzige Fehler, der nur hier vorkommt, ist cursor_expired (410) — siehe oben. Alles andere steht mit Bedeutung, Wiederholverhalten und Abhilfe unter Fehlercodes.