API gabinetu MentAI (v1)
Własny klucz gabinetu. Odczyt wizyt, klientów, usług, terapeutów i wolnych terminów oraz tworzenie rezerwacji z Twoich narzędzi: strony, arkusza, automatyzacji, własnego systemu. Klucz z zakresem „zapis” dodaje i zmienia klientów oraz przekłada, odwołuje i zmienia status wizyt. Webhooki powiadamiają Twój system o zmianach wizyt i płatnościach. Bez danych klinicznych.
Podstawy
| Adres bazowy | https://mentai.pl/api/v1 |
|---|---|
| Uwierzytelnianie | nagłówek Authorization: Bearer mentai_… |
| Format | JSON, UTF-8, daty w czasie polskim jako YYYY-MM-DDTHH:MM, kwoty w groszach (20000 = 200,00 zł) |
| Limity | 60 wywołań na minutę (429 api_minute_limit, pole retryAfterSeconds) oraz dziennie na klucz: 500 w okresie testowym, 2000 w abonamencie (429 api_daily_limit) |
| Zakres klucza | odczyt (tylko GET) albo zapis (odczyt, rezerwacje i zapis) — wybiera Administrator przy generowaniu klucza. Klucz wygenerowany przed 11.10.2026 działa jak dotąd: odczyt i rezerwacje. |
| Błędy | {"ok": false, "error": "kod"}; 401 invalid_api_key, 403 api_scope_read_only (klucz bez zakresu zapisu), 403 trial_expired (zapis po końcu dostępu), 404 not_found, 409 booking_disabled |
| Dane | serwery AWS w UE (Frankfurt); API nie zwraca danych klinicznych |
curl -H "Authorization: Bearer mentai_TWOJ_KLUCZ" \
https://mentai.pl/api/v1/gabinet
Endpointy
GET /api/v1/gabinet
Dane gabinetu (nazwa, adres, kolor, notatki dla klientów), slug i adres strony rezerwacji, czy rezerwacja online jest włączona, włączone moduły.
GET /api/v1/services
Usługi ze strony rezerwacji: serviceId, nazwa, czas trwania, cena, adresat, flagi qualificationRequired, firstVisitWithoutChild, online.
GET /api/v1/therapists
Aktywni terapeuci: therapistId, imię, oferowane usługi i ceny.
GET /api/v1/availability
Wolne terminy z grafiku. Parametry: serviceId (wymagany do długości slotu), therapistId (opcjonalnie), from, to (YYYY-MM-DD). Wymaga włączonej rezerwacji online w panelu; inaczej 409 booking_disabled.
GET /api/v1/availability?serviceId=bs-xxx&from=2026-09-08&to=2026-09-14
GET /api/v1/visits
Wizyty gabinetu (do 500, najnowsze pierwsze). Filtry: from, to (po dacie wizyty), contactId, status (zaplanowana, potwierdzona, odbyta, odwolana, no_show, …). Każda wizyta: termin, status, usługa, terapeuta, kontakt (alias), rozliczenie, tryb (gabinet / online).
GET /api/v1/visits/{visitId}
Jedna wizyta.
GET /api/v1/contacts
Klienci z kartoteki (do 500). Filtry: query (fragment nazwy), tag (etykieta), archived=1 (z archiwum). Pola jak w panelu: alias, typ, opiekunowie, kontakt, dane nabywcy, zgoda RODO, etykiety, uwagi organizacyjne, preferowana forma wizyt.
GET /api/v1/contacts/{contactId}
Widok klienta: karta, liczby (odbyte, nieobecności, odwołane, zaplanowane, ostatnia i następna wizyta, wartość i wpłaty), oś czasu (wizyty, dokumenty, zgody, komunikacja, zadania), zadania, wpisy listy rezerwowej.
POST /api/v1/bookings
Nowa rezerwacja tym samym torem co publiczna strona rezerwacji: te same reguły (wolny slot, kwalifikacja, zgoda RODO, kanał przypomnień), ta sama izolacja danych osobowych, te same potwierdzenia dla klienta i gabinetu. Rezerwacja czeka na akceptację gabinetu, chyba że panel ma włączone auto-akceptowanie.
POST /api/v1/bookings
{
"serviceId": "bs-xxx",
"therapistId": "t-xxx", // opcjonalnie
"slotStart": "2026-09-10T10:00",
"name": "Anna Nowak",
"contactPhone": "+48600100200",
"contactEmail": "anna@example.com",
"reminderChannel": "sms", // email | sms | whatsapp
"rodoConsent": true,
"visitMode": "online", // tylko gdy usługa ma online: true i gabinet ma telewizyty
"reason": "konsultacja", // opcjonalnie
"patientName": "…", "patientAge": "…" // gdy wizyta dla dziecka
}
Odpowiedź: {"ok": true, "visitId": "…", …}. Błędy jak na stronie rezerwacji: slot_unavailable, service_not_found, qualification_required, phone_required, online_not_available.
Zapis (klucz z zakresem „zapis”)
Zmiany idą tą samą drogą co w panelu: te same reguły, ta sama historia wizyty (z oznaczeniem klucza API jako autora) i te same webhooki. Zapis po końcu dostępu do systemu zwraca 403 trial_expired; odczyt działa dalej.
POST /api/v1/contacts
Nowy klient. Pola: displayName (wymagane), firstName, lastName, email, phone, guardianName, guardian2Name, externalRef, defaultBuyerType, defaultBuyerName, defaultBuyerTaxId, defaultBuyerAddress, invoiceEmail, contactType, preferredVisitMode, tags, leadTherapistIds, orgNote. Inne pole (np. zgoda RODO, archiwum) = 400 unsupported_fields:… z nazwami pól. Jeśli gabinet ma włączoną automatyczną prośbę o zgodę RODO, klient z e-mailem dostaje ją tak samo jak po dodaniu w panelu.
PATCH /api/v1/contacts/{contactId}
Zmiana tych samych pól klienta.
POST /api/v1/visits/{visitId}/reschedule
{
"visitAt": "2026-10-21T11:00", // nowy termin, czas polski
"notifyClient": true, // OBOWIĄZKOWE: true albo false
"therapistId": "t-xxx", // opcjonalnie, zmiana specjalisty
"allowOverlap": false // opcjonalnie; kolizja terminu = 409 visit_slot_conflict z listą
}
notifyClient: true — klient dostaje informację o nowym terminie (e-mail, a SMS, jeśli gabinet wysyła SMS-y) według zgód z kartoteki; false — bez informacji, z wpisem w historii wizyty. Odpowiedź: visit i notice (requested, sent.email, sent.sms, error — np. gdy wizyta jest już miniona i informacji się nie wysyła). Wizyta odwołana = 409 visit_cancelled.
POST /api/v1/visits/{visitId}/cancel
{
"notifyClient": true, // OBOWIĄZKOWE
"cancelledBy": "practice", // practice (domyślnie) albo client
"reason": "zmiana w grafiku", // opcjonalnie: jedna linia, do 200 znaków, bez linków i adresów
"cancellationBilling": "nieplatna" // opcjonalnie: platna | nieplatna (późne odwołanie)
}
Wizyta opłacona online: zwrot jak przy odwołaniu w panelu; przy notifyClient: true zdanie o zwrocie jest w tej samej wiadomości.
PATCH /api/v1/visits/{visitId}/status
{"status": "potwierdzona"} — dozwolone: potwierdzona, odbyta, no_show (dla no_show opcjonalnie cancellationBilling). Odwołania tylko przez /cancel.
Webhooki — powiadomienia o zmianach
Zamiast co chwilę pytać API o zmiany, podaj adres https, na który MentAI wyśle krótką wiadomość, gdy coś się wydarzy. Adres dodaje Administrator w panelu: Ustawienia → API dla integracji → Webhooki — do 3 adresów, dla każdego wybór zdarzeń i przycisk „Wyślij test”. Webhooki nie wymagają klucza API; klucz przyda się do pobrania szczegółów.
| Zdarzenie | Kiedy |
|---|---|
visit.created | nowa wizyta — z panelu, rezerwacji online, API, ZnanegoLekarza albo serii |
visit.updated | zmiana terminu, statusu, specjalisty, usługi, filii, formy wizyty, klienta albo archiwizacja; pola changed i previous mówią, co i z czego się zmieniło |
visit.cancelled | odwołanie (status odwolana, anulowana_przez_gabinet albo termin_zwolniony); pole previousStatus |
visit.deleted | wizyta usunięta z systemu |
payment.received | wpłata online zaksięgowana na wizycie; pole paymentAmountGrosze |
ping | wiadomość testowa z przycisku „Wyślij test” |
Treść zawiera identyfikatory, termin, status i stan płatności — bez imienia i nazwiska, danych kontaktowych klienta i danych klinicznych. Szczegóły pobierzesz kluczem API: GET /api/v1/visits/{visitId}, GET /api/v1/contacts/{contactId}.
POST https://twoj-system.pl/mentai
Content-Type: application/json
User-Agent: MentAI-Webhooks/1.0
MentAI-Event-Id: evt_3f2a9c…
MentAI-Event-Type: visit.updated
MentAI-Signature: t=1760178375,v1=9b1c…
{
"id": "evt_3f2a9c…",
"type": "visit.updated",
"createdAt": "2026-10-11T10:26:15.482913+00:00",
"apiVersion": "2026-10-11",
"data": {
"visitId": "…", "visitAt": "2026-10-21T11:00", "timezone": "Europe/Warsaw",
"durationMinutes": 50, "status": "zaplanowana", "therapistId": "…", "serviceId": "…",
"locationId": "", "visitMode": "gabinet", "contactId": "…", "paymentStatus": "none",
"archived": false, "changed": ["visitAt"], "previous": {"visitAt": "2026-10-20T10:00"}
}
}
Sprawdzanie podpisu
Nagłówek MentAI-Signature ma postać t=<czas unix>,v1=<podpis>. Podpis to HMAC-SHA256 z sekretem adresu (whsec_…, widać go w panelu tylko raz — przy dodaniu adresu i przy „Nowy sekret”) liczony z tekstu <t>. i surowej treści żądania (bajty, przed parsowaniem JSON). Odrzucaj wiadomości z niezgodnym podpisem albo starsze niż 5 minut.
# Python
import hashlib, hmac, time
def podpis_ok(sekret, naglowek, tresc_bajty, tolerancja=300):
czesci = dict(p.split("=", 1) for p in naglowek.split(","))
t, v1 = int(czesci["t"]), czesci.get("v1", "")
wzor = hmac.new(sekret.encode(), f"{t}.".encode() + tresc_bajty, hashlib.sha256).hexdigest()
return hmac.compare_digest(wzor, v1) and abs(time.time() - t) <= tolerancja
// Node.js
const crypto = require("crypto");
function podpisOk(sekret, naglowek, trescBufor, tolerancja = 300) {
const c = Object.fromEntries(naglowek.split(",").map((p) => p.split("=")));
const wzor = crypto.createHmac("sha256", sekret).update(`${c.t}.`).update(trescBufor).digest("hex");
const zgodny = !!c.v1 && c.v1.length === wzor.length && crypto.timingSafeEqual(Buffer.from(wzor), Buffer.from(c.v1));
return zgodny && Math.abs(Date.now() / 1000 - Number(c.t)) <= tolerancja;
}
Dostawa i ponowienia
- Odpowiedz kodem 2xx w ciągu 5 sekund; treść odpowiedzi nie ma znaczenia. Przekierowań (3xx) nie wykonujemy — liczą się jako błąd.
- Gdy adres nie odpowie albo zwróci inny kod, ponawiamy po 5 minutach, godzinie, 6 godzinach i dobie od pierwszej próby (razem 5 prób).
- Ta sama wiadomość może przyjść więcej niż raz, a kolejność nie jest gwarantowana: odrzucaj powtórki po
MentAI-Event-Id, a aktualny stan wizyty sprawdzaj przez API. - Jeśli wiadomość nie dotrze mimo 5 prób, a adres przez ten czas nie przyjął żadnej innej, adres się wyłącza i Administrator dostaje e-mail. Włączysz go w panelu; wiadomości z czasu wyłączenia nie są wysyłane ponownie.
- Adres: tylko https na porcie 443, nazwa domeny (nie numer IP), domena musi prowadzić wyłącznie do publicznych adresów IP. Limit: 5000 wysyłek na dobę na gabinet.
Bezpieczeństwo i RODO
- Klucz przechowujemy wyłącznie jako skrót kryptograficzny; nie da się go odczytać ponownie z panelu.
- Nowy klucz wygeneruje tylko Administrator z włączoną weryfikacją dwuetapową (2FA) — samo hasło do panelu nie wystarczy. Klucze wygenerowane wcześniej działają dalej.
- Gabinet odpowiada za narzędzia, którym powierza klucz, i za dane, które nimi pobiera (regulamin, sekcja o kluczu API). Integracje działają w ramach umowy powierzenia.
- Każde wywołanie liczy się do limitów minutowego i dziennego; użycie widać w panelu: liczba wywołań dziś, ostatnie użycie i „Dziennik użycia” z ostatnich 14 dni (wywołania, zapisy, najczęstsze endpointy).
- Zmieniać dane może tylko klucz z zakresem „zapis”. Przełożenie i odwołanie wizyty wymagają jawnej decyzji
notifyClient— API nie zgaduje, czy powiadomić klienta. - Administrator gabinetu dostaje e-mail przy każdym wygenerowaniu i cofnięciu klucza (bez treści klucza, tylko prefiks).
- Dane kliniczne nigdy nie są dostępne przez API. To celowe ograniczenie, nie brak.
- Dodanie adresu webhooka to polecenie gabinetu w ramach umowy powierzenia (§4a; regulamin, sekcja o webhookach). Data, godzina i osoba dodająca adres są zapisywane; o dodaniu i usunięciu adresu oraz o nowym sekrecie Administrator dostaje e-mail.
Co dalej
Brakuje Ci czegoś w API? Napisz, czego potrzebujesz: kontakt@mentai.pl.
mentai.pl