Errors
Wszystkie endpointy używają standardowych kodów HTTP. Body błędu to JSON w formacie FastAPI / Pydantic.
Format
Walidacja Pydantic (422)
{
"detail": [
{
"loc": ["body", "doctors", 0, "email"],
"msg": "value is not a valid email address",
"type": "value_error.email"
}
]
}
Reszta (400/401/403/404/409/429/500)
{
"detail": "Slot już zarezerwowany przez innego pacjenta"
}
detail to string PL lub strukturalny obiekt — nigdy stack trace.
Kody HTTP
| Kod | Znaczenie | Kiedy |
|---|---|---|
| 200 | OK | Successful read / upsert (zwraca created + updated + errors) |
| 201 | Created | Booking, lead, review created |
| 204 | No Content | DELETE successful (np. slot delete) |
| 400 | Bad Request | Body prawidłowy ale logika odrzuca (np. slot w przeszłości) |
| 401 | Unauthorized | Brak / nieprawidłowy / revoked API key |
| 402 | Payment Required | Subskrypcja klinki past_due >7d lub cancelled |
| 403 | Forbidden | Klucz nie ma wymaganego scope, lub patient próbuje akcji clinic_admin |
| 404 | Not Found | Resource nie istnieje, lub cross-clinic (G-DATA-001 zwraca 404 unify) |
| 409 | Conflict | Booking race lost — slot zarezerwował kto inny atomowo |
| 422 | Unprocessable | Pydantic walidacja (input format, regex, range) |
| 429 | Too Many Requests | Rate limit (patrz rate limits) |
| 500 | Server Error | Bug — zgłoś na kontakt@pandoktorek.pl z X-Request-ID z headerów response |
Typowe błędy ingest
422 — external_id puste albo > 120 znaków
{"detail": [{"loc":["body","doctors",0,"external_id"],"msg":"ensure this value has at least 1 characters"}]}
Fix: każdy item musi mieć stabilne, unikalne external_id (z twojego CRM).
422 — pwz nie pasuje do ^\d{7}$
{"detail": [{"loc":["body","doctors",0,"pwz"],"msg":"string does not match regex \"^\\d{7}$\""}]}
Fix: PWZ musi być dokładnie 7 cyfr. Jeśli lekarz nie ma PWZ (np. fizjoterapeuta) —
przekaż null lub pomiń pole.
422 — phone nie pasuje do regex
Fix: format E.164 + opcjonalny +, 9-15 cyfr. Przykłady poprawne:
+48501234567, 48501234567, 501234567.
422 — services_allowed zawiera nieistniejący external_id
Fix: najpierw pushuj services, potem slots. Albo pomiń services_allowed —
slot będzie dostępny dla wszystkich active services lekarza.
409 — booking race lost
{"detail": "Slot już zarezerwowany przez innego pacjenta"}
Wystąpiło przy POST /api/v1/bookings. Atomic UPDATE przegrał — inny pacjent
zarezerwował ten sam slot pierwszy. Frontend pokazuje toast, prosi o wybór
innego slotu.
Idempotency a błędy
POST /api/v1/clinic/{doctors,services,slots} jest idempotent po
(clinic_id, external_id). Re-run tego samego payloadu po błędzie sieciowym
(timeout, 502 od Caddy) nie tworzy duplikatów — zwróci created=0, updated=N.
Webhook delivery errors
Twój system odbiera webhook → musi zwrócić 2xx w ciągu 10s. W przeciwnym
razie Pandoktorek retry'uje z exponential backoff. Po 5 nieudanych próbach
status failed — ręczny retry z panelu klinki.
Co przekazać w bug report
Gdy coś nie działa, prześlij:
- Request ID — z headera
X-Request-IDw response (każdy request ma) - Czas UTC (np.
2026-05-08T14:32:11Z) - Endpoint + method
- Klucz prefix (np.
pk_live_a3f8b2c1) — NIE pełen klucz - Status code + detail który dostałeś