Dokumentacja API

Rezerwacje

Własny frontend umawia wizyty w tym samym kalendarzu co wbudowana strona placówki: katalog usług i specjalistów, wolne terminy i rezerwacja. Nie ma drugiego grafiku do synchronizowania.

Endpointy

EndpointOdpowiedź 200Opis
GET /api/v1/booking/catalog{ services: BookingCatalogService[]; resources: BookingCatalogResource[] }Katalog rezerwacji: aktywne usługi i specjaliści z powiązaniami do zabiegów i zespołu
GET /api/v1/booking/slots{ slots: Slot[] }Wolne terminy dla usługi (opcjonalnie zawężone do specjalisty)
GET /api/v1/booking/appointments{ appointments: Appointment[] }Lista wizyt (z nazwami usługi i specjalisty)
POST /api/v1/booking/appointmentsAppointmentCreatedRezerwacja wizyty (startAt musi pochodzić z /booking/slots)

1. Katalog

Katalog zwraca aktywne usługi rezerwacji i aktywnych specjalistów placówki. Identyfikatory z katalogu to parametry service i resource dla wolnych terminów oraz pola serviceId i resourceId przy rezerwacji.

curl
curl "https://app.pozyskajpacjenta.pl/api/v1/booking/catalog" \
  -H "Authorization: Bearer pp_live_TWOJ_KLUCZ"
200 OK
{
  "services": [
    {
      "id": "bs_4k9x2m",
      "name": "Konsultacja implantologiczna",
      "durationMin": 30,
      "priceLabel": "300 zł",
      "resourceIds": ["br_7q1w3e"],
      "serviceSlug": "implanty",
      "nfz": false
    }
  ],
  "resources": [
    {
      "id": "br_7q1w3e",
      "name": "Anna Kowalska",
      "title": "lek. dent.",
      "doctorSlug": "anna-kowalska"
    }
  ]
}
  • priceLabel to cena samej wizyty, np. konsultacja za 300 zł, nawet gdy powiązany zabieg kosztuje „od 3 200 zł”. Gdy placówka nie podała ceny wizyty, widzisz cenę powiązanego zabiegu (priceFromLabel z GET /api/v1/services/{slug}), a gdy nie ma żadnej — null. Tak działa od wersji 1.8.1, tak samo jak widżet rezerwacji na stronie placówki.
  • resourceIds to specjaliści, u których da się umówić tę usługę. Pusta lista oznacza, że dziś nie ma u kogo jej umówić.
  • serviceSlug i doctorSlug prowadzą do opublikowanego zabiegu i profilu w zespole. W drugą stronę działają pola bookingServiceId zabiegu i bookingResourceId profilu (Encje): przycisk „Umów” na stronie zabiegu ma od razu właściwe id usługi. Wartość null oznacza, że tej pozycji nie da się umówić online.
  • nfz (od 1.8.0): true oznacza świadczenie NFZ. Pokaż je z dopiskiem „rejestracja telefoniczna” i telefonem placówki — online się go nie umówi: /booking/slots i /booking/appointments odpowiadają 400 z code: "nfz_phone_only". Brak pola (starszy serwer) znaczy false.

2. Wolne terminy

Terminy liczymy na żywo z grafików, urlopów i już umówionych wizyt. Zakres to najwyżej 31 dni; bez parametru resource dostajesz terminy wszystkich specjalistów wykonujących usługę. Odpowiedź nie jest cache'owana.

curl
curl "https://app.pozyskajpacjenta.pl/api/v1/booking/slots?service=bs_4k9x2m&from=2026-10-05&to=2026-10-09" \
  -H "Authorization: Bearer pp_live_TWOJ_KLUCZ"

3. Rezerwacja

startAt musi pochodzić z listy wolnych terminów. Pacjent trafia do CRM jako lead (znajdowany po telefonie albo tworzony), a placówka dostaje wizytę w kalendarzu i e-mail „Nowa wizyta” z plikiem .ics na adres powiadomień.

curl
curl -X POST "https://app.pozyskajpacjenta.pl/api/v1/booking/appointments" \
  -H "Authorization: Bearer pp_live_TWOJ_KLUCZ" \
  -H "Content-Type: application/json" \
  -d '{
    "serviceId": "bs_4k9x2m",
    "resourceId": "br_7q1w3e",
    "startAt": 1791187200,
    "patient": { "name": "Jan Nowak", "phone": "+48 600 100 200" }
  }'

Od 1.7.0: rezerwacja opiera się na art. 6 ust. 1 lit. b i art. 9 ust. 2 lit. h RODO, więc formularz nie ma wymaganej zgody na przetwarzanie. Pokaż pod nim klauzulę informacyjną z GET /api/v1/privacy i opcjonalne, niezaznaczone pole zgody marketingowej, a w treści rezerwacji wyślij privacyNoticeVersion i (gdy pacjent zaznaczył pole) marketingConsent: true. Wizyta zapisze wersję klauzuli i czas wysłania. Zgoda marketingowa bez privacyNoticeVersion nie jest zapisywana, a u powracającego pacjenta zgoda e-mail obejmuje tylko adres zapisany przy jego leadzie.

Jeśli ktoś zajął ten termin w międzyczasie, dostajesz 409 i nic się nie zapisuje. Odpowiedź zawiera świeżą listę wolnych terminów tego dnia, więc możesz od razu zaproponować pacjentowi inny:

409 Conflict
{
  "error": "slot_taken",
  "slots": [
    {
      "resourceId": "br_7q1w3e",
      "startAt": 1791189000,
      "endAt": 1791190800,
      "localDate": "2026-10-05",
      "localStartMin": 630
    }
  ]
}

Pola

BookingCatalogService — usługa w katalogu

PoleTypWymaganeOpis
idstringtakParametr service dla /booking/slots.
namestringtak
durationMinintegertakCzas wizyty w minutach.
priceLabelstring | nulltakCena wizyty do wyświetlenia: etykieta ceny samej usługi rezerwacji (np. konsultacja 300 zł), a gdy placówka jej nie ustawiła — priceFromLabel opublikowanego zabiegu powiązanego z usługą (ta sama, co w /services/{slug}); null, gdy nie ma żadnej. Od 1.8.1 cena wizyty ma pierwszeństwo — wcześniej wygrywała cena zabiegu, więc konsultacja powiązana z ortodoncją pokazywała „od 1900 zł” zamiast swoich 300 zł.
resourceIdsstring[]takAktywni specjaliści wykonujący usługę (id z resources). Pusta lista = nie ma u kogo umówić. Wolne terminy zawsze z /booking/slots.
serviceSlugstring | nulltakSlug opublikowanego zabiegu powiązanego z usługą (GET /api/v1/services/{slug}); null, gdy żaden opublikowany zabieg jej nie wskazuje.
nfzboolean—Od 1.8.0. true = świadczenie NFZ: pokazujesz je informacyjnie z dopiskiem „rejestracja telefoniczna” i telefonem placówki, ale nie da się go zarezerwować online — /booking/slots i /booking/appointments odpowiadają 400 z code nfz_phone_only. Brak pola (starsze serwery) = false.

BookingCatalogResource — specjalista w katalogu

PoleTypWymaganeOpis
idstringtakParametr resource dla /booking/slots.
namestringtak
titlestring | nulltakTytuł, np. lek. dent.
doctorSlugstring | nulltakSlug opublikowanego profilu w zespole (GET /api/v1/doctors/{slug}); null, gdy żaden opublikowany profil go nie wskazuje.

Slot — wolny termin

PoleTypWymaganeOpis
resourceIdstringtak
startAtintegertakPoczątek, sekundy epoch (UTC).
endAtintegertakKoniec, sekundy epoch (UTC).
localDatestringtakLokalna data YYYY-MM-DD.
localStartMinintegertakLokalna minuta dnia (np. 570 = 9:30).

SlotTaken — odpowiedź 409 na zajęty termin

PoleTypWymaganeOpis
error"slot_taken"tak
slotsSlot[]tak

Czytaj dalej: Leady →