Retry policy
Gdy twój endpoint nie zwróci 2xx w ciągu 10s lub zwróci błąd, Pandoktorek retry'uje z exponential backoff.
Algorytm
Nieudana próba (odpowiedź spoza 2xx, timeout 10 s, błąd połączenia) → kolejna
po min(2^N, 360) minutach, gdzie N = numer nieudanej próby. Maksymalnie
10 prób; potem status failed (ręczny retry w panelu kliniki).
| Próba | Odstęp od poprzedniej | Czas od pierwszej próby |
|---|---|---|
| 1 | — | 0 |
| 2 | 2 min | 2 min |
| 3 | 4 min | 6 min |
| 4 | 8 min | 14 min |
| 5 | 16 min | 30 min |
| 6 | 32 min | ~1 h |
| 7 | 64 min | ~2 h |
| 8 | 128 min | ~4 h |
| 9 | 256 min | ~8,5 h |
| 10 | 360 min | ~14,5 h |
Webhook to sygnał, nie jedyne źródło prawdy: integracja musi dodatkowo
co kilka minut pobierać zmiany przez GET /api/v1/clinic/bookings?updated_since=…
— wtedy nawet całkowita utrata webhooka nie gubi rezerwacji.
Kiedy Pandoktorek liczy próbę jako "failure"
| Sytuacja | Status | Retry? |
|---|---|---|
200, 201, 204 | succeeded | ❌ |
301, 302 | failed | ❌ (nie podążamy za redirectami) |
4xx (auth, walidacja u ciebie) | failed | ❌ (klient błąd, retry nic nie zmieni) |
5xx | retrying | ✅ |
timeout > 10s | retrying | ✅ |
connection refused / DNS fail | retrying | ✅ |
:::warning 4xx jest finalne
Jeśli zwrócisz 400 / 401 / 403 / 404 Pandoktorek nie retry'uje.
Powód: 4xx mówi "klient zrobił coś źle" → kolejna próba też się nie uda.
Jeśli twój system tymczasowo nie umie obsłużyć eventu (np. baza down), zwróć 5xx żeby dostać retry. :::
Idempotency po stronie klienta
Pandoktorek może dostarczyć ten sam event więcej niż raz (at-least-once). Twój system MUSI
deduplikować po event_id:
# Idempotent guard z Redis SETEX (24h TTL)
if r.set(f"event:{event_id}", "1", ex=86400, nx=True) is None:
return {"status": "duplicate", "event_id": event_id}
Albo tabela:
CREATE TABLE webhook_events_processed (
event_id UUID PRIMARY KEY,
event_type TEXT NOT NULL,
processed_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
INSERT INTO webhook_events_processed (event_id, event_type)
VALUES ($1, $2)
ON CONFLICT DO NOTHING;
-- jeśli rowcount=0 → już przetworzono
Panel klinki — Webhooks dashboard
W panelu klinki Webhooks → konkretna subskrypcja widzisz:
- Lista delivery'ów (recent N=100): event_id, event_type, status, attempts, status_code, last_attempt_at, next_attempt_at
- Filter po status (
succeeded/retrying/failed) - Per-row akcje:
- Retry now — wymuś próbę dostarczenia (nawet po
failed) - View payload — pełen body który był wysłany (do debug)
- Retry now — wymuś próbę dostarczenia (nawet po
- Per-subscription:
- Pause — wyłącz subskrypcję (zostaną active deliveries, ale nowe nie idą)
- Rotate secret — wygeneruj nowy secret (stary invalidowany od razu — twój system ma 0s grace period, deploy z nowym secretem MUSI być przed)
Monitoring
W panelu klinki badge'e pokazują ile delivery'ów failed w ostatnie 24h
per subskrypcja. Powyżej 5% threshold — czerwony alert.
Po stronie Pandoktorka: platform_admin widzi globalny dashboard
/admin/security/api-keys-dormant (analog dla webhooków w Faza 2).
Best practice
- Zwróć 200 SZYBKO — przyjmij event do swojej kolejki, fan-out async. Nie przetwarzaj w request handlerze (timeouty rosną).
- Idempotent processing — dedupe by
event_id, ale też idempotent business logic (np. UPSERT do MyDr po booking_external_id, nie INSERT). - Log każdy event — z
event_id,event_type, raw body, signature verify result. Przyda się przy debug. - Alerty na
failed— twój system powinien alertować jeśli widzi wzrost failed deliveries (np. po deploy z bugiem).