Przejdź do głównej zawartości

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óbaOdstęp od poprzedniejCzas od pierwszej próby
1—0
22 min2 min
34 min6 min
48 min14 min
516 min30 min
632 min~1 h
764 min~2 h
8128 min~4 h
9256 min~8,5 h
10360 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"​

SytuacjaStatusRetry?
200, 201, 204succeeded❌
301, 302failed❌ (nie podążamy za redirectami)
4xx (auth, walidacja u ciebie)failed❌ (klient błąd, retry nic nie zmieni)
5xxretrying✅
timeout > 10sretrying✅
connection refused / DNS failretrying✅

:::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

→ Idempotency

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)
  • 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​

  1. Zwróć 200 SZYBKO — przyjmij event do swojej kolejki, fan-out async. Nie przetwarzaj w request handlerze (timeouty rosną).
  2. Idempotent processing — dedupe by event_id, ale też idempotent business logic (np. UPSERT do MyDr po booking_external_id, nie INSERT).
  3. Log każdy event — z event_id, event_type, raw body, signature verify result. Przyda się przy debug.
  4. Alerty na failed — twój system powinien alertować jeśli widzi wzrost failed deliveries (np. po deploy z bugiem).