Przejdź do głównej zawartości

Bookings

Endpoint do tworzenia rezerwacji. Race-safe — atomic UPDATE chroni przed double-booking. Loser dostaje 409 Conflict.

POST /api/v1/bookings​

Tworzy rezerwację. Guest (email + phone, bez konta) lub Auth user (JWT Bearer).

Auth: opcjonalny Bearer <jwt>. Bez auth = guest mode.

Request — guest​

POST /api/v1/bookings HTTP/1.1
Content-Type: application/json

{
"slot_id": "uuid-...",
"service_id": "uuid-...",
"patient": {
"first_name": "Jan",
"last_name": "Kowalski",
"email": "jan@example.pl",
"phone": "+48501234567",
"pesel": "85010112345"
},
"notes": "Pierwsza wizyta — proszę o wczesny termin"
}

Request — auth user​

POST /api/v1/bookings HTTP/1.1
Authorization: Bearer <jwt-token>
Content-Type: application/json

{
"slot_id": "uuid-...",
"service_id": "uuid-...",
"notes": "..."
}

(patient brany z User profile.)

Pola​

PoleTypWymaganeOpis
slot_iduuid✅ID slotu Pandoktorka (z /doctors/{slug}/slots)
service_iduuid✅ID services oferowanego dla tego slotu
patient.first_namestringguest only
patient.last_namestringguest only
patient.emailemailguest onlyConfirmation email
patient.phonestringguest onlySMS reminders
patient.peselstring (regex ^\d{11}$ + mod11)—Wymagane dla NFZ services
notesstring (max 500)—Notatka dla lekarza

Response 201​

{
"id": "uuid-...",
"status": "pending",
"slot": {
"id": "uuid-...",
"start_at": "2026-05-10T10:00:00+02:00",
"end_at": "2026-05-10T10:30:00+02:00"
},
"doctor": {
"first_name": "Anna",
"last_name": "Kowalczyk",
"slug": "dr-anna-kowalczyk-warszawa"
},
"clinic": {
"name": "MedTrust Warszawa",
"address": "ul. Marszałkowska 1"
},
"service": {
"name": "Konsultacja dermatologiczna",
"price_cents": 25000
},
"cancel_token": "...",
"magic_link_token": "..."
}

magic_link_token (guest only) — link w confirmation email do claim'u booking'a do założonego konta (DEC-005).

Response 409 — race lost​

{"detail": "Slot już zarezerwowany przez innego pacjenta"}

UI pokazuje toast, redirect do listy slotów lekarza.

Response 422 — walidacja​

PESEL nie jest poprawny mod11, email zły format, slot/service mismatch (service_id nie jest w services_allowed slotu), itp.

POST /api/v1/bookings/{id}/cancel​

Cancel booking. Wymaga JWT (patient/clinic_admin) lub cancel_token z SMS link.

Request​

POST /api/v1/bookings/{id}/cancel HTTP/1.1
Authorization: Bearer <jwt>
Content-Type: application/json

{
"reason": "Pacjent rezygnuje"
}

Response 200​

{
"id": "uuid-...",
"status": "cancelled",
"cancelled_at": "2026-05-08T14:30:00+02:00"
}

Slot wraca do status=available. Webhook booking.cancelled emitowany.

POST /api/v1/bookings/cancel-by-token/{token}​

Cancel via SMS link (token z cancel_token). Brak auth.

curl -X POST https://api.pandoktorek.pl/api/v1/bookings/cancel-by-token/abc123def

→ frontend strona /anulacja/[token] używa tego endpointu.

GET /api/v1/bookings/me​

Lista rezerwacji zalogowanego pacjenta. JWT auth.

Query params​

ParamDefaultOpis
status—Filter (pending, confirmed, cancelled, completed, no_show)
from / to—Date range
page / page_size1 / 20Pagination

State machine​

pending ──confirm──► confirmed ──complete──► completed
│ │
│ └──cancel──► cancelled
│
└──cancel──► cancelled

no-show: confirmed ──missed──► no_show
  • pending — rezerwacja utworzona, czeka na confirmacje (manualnie albo auto)
  • confirmed — potwierdzona, reminder SMS/email idzie 24h przed
  • completed — wizyta odbyta, pacjent może dodać review
  • cancelled — anulowana (slot wraca dostępny)
  • no_show — pacjent się nie pojawił (manual flag clinic_admin)

Zmiany statusu emitują webhook eventy: booking.created, booking.cancelled, booking.completed, booking.no_show.

→ Webhook events