Przejdź do głównej zawartości

Webhooks — overview

Pandoktorek POST'uje JSON do twojego URL gdy zachodzi event w klinice (np. nowa rezerwacja). Twój system odbiera, weryfikuje signature, fan-out'uje do swojego CRM (MyDr / Medfile / własnego).

Architektura​

┌────────────────┐
│ Pandoktorek │
Pacjent rezerwuje ───────────► │ POST /bookings│
└────────┬───────┘
│
booking.created │
▼
┌──────────────────────────┐
│ WebhookDeliveryService │
│ - HMAC sign payload │
│ - httpx POST do twojego │
│ URL z headerami │
└──────────┬───────────────┘
│
▼ POST + sig
┌──────────────────────────┐
│ Twój system │
│ - verify HMAC │
│ - dedupe by event_id │
│ - fan-out do MyDr │
│ - return 200 │
└──────────────────────────┘

Setup webhooka​

Panel klinki: Webhooks → Nowa subskrypcja.

URL: https://twój-system.pl/webhooks/pandoktorek
Events: ☑ booking.created
☑ booking.cancelled
☑ booking.completed
☐ booking.no_show
☐ review.created

Przy create dostajesz secret — zapisz go w .env. Nie pokażemy go ponownie.

Headers wysyłane przez Pandoktorek​

Każdy POST do twojego URL:

POST /webhooks/pandoktorek HTTP/1.1
Host: twój-system.pl
Content-Type: application/json
X-Pandoktorek-Event: booking.created
X-Pandoktorek-Event-Id: 550e8400-e29b-41d4-a716-446655440000
X-Pandoktorek-Signature: sha256=4e8f3c1...
User-Agent: Pandoktorek-Webhook/1.0

{...payload...}
HeaderOpis
X-Pandoktorek-EventTyp eventu (np. booking.created)
X-Pandoktorek-Event-IdUUID eventu — używaj do dedup
X-Pandoktorek-Signaturesha256=<hex> HMAC-SHA256 z body, klucz = secret subskrypcji

Wymagania od twojego systemu​

  • Odpowiedź 2xx w ciągu 10s, inaczej Pandoktorek retry'uje
  • Verify signature ZANIM przetworzysz event (anti-spoof)
  • Dedupe by event_id — Pandoktorek może retry'ować ten sam event
  • Idempotent processing — przetwarzanie 2× tego samego event'u nie może podwoić efektu w MyDr

Best practice — szkielet handlera​

from fastapi import FastAPI, Request, HTTPException
import hmac, hashlib, redis

app = FastAPI()
SECRET = os.environ["PANDOKTOREK_WEBHOOK_SECRET"].encode()
r = redis.Redis()

@app.post("/webhooks/pandoktorek")
async def webhook(request: Request):
body = await request.body()
sig_header = request.headers.get("X-Pandoktorek-Signature", "")
expected = "sha256=" + hmac.new(SECRET, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig_header, expected):
raise HTTPException(401, "Invalid signature")

event = await request.json()
event_id = event["event_id"]

# Idempotent guard (24h dedup window)
if r.set(f"event:{event_id}", "1", ex=86400, nx=True) is None:
return {"status": "duplicate", "event_id": event_id}

# Fan-out do MyDr
if event["event_type"] == "booking.created":
mydr.create_appointment(event["payload"])

return {"status": "ok"}

→ Pełen przykład: Signature verification

Co dalej​