MentAImentai.pl· API gabinetu · wersja 1

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.

Klucz generuje Administrator w panelu: Ustawienia → API dla integracji. Do wygenerowania klucza potrzebna jest weryfikacja dwuetapowa (2FA) włączona na koncie Administratora. Klucz widać tylko raz. Działa jak hasło do całego gabinetu w zakresie Administratora, z wyłączeniem modułu klinicznego (karty, załączniki, nagrania, notatki kliniczne nie są dostępne przez API). Nowy klucz unieważnia poprzedni; „Cofnij klucz” wyłącza dostęp natychmiast.
Osobny tor bez klucza: publiczna rezerwacja dla agentów AI i integracji. Każdy gabinet ma publiczny tor rezerwacji (wolne terminy + prośba o wizytę), opisany maszynowo w OpenAPI publicznego toru rezerwacji i w katalogu /.well-known/api-catalog. Instrukcja dla asystentów AI: llms.txt, sekcja „Jak umówić wizytę przez agenta AI". Klucz gabinetu opisany niżej daje więcej: odczyt wizyt, klientów i tworzenie rezerwacji z własnych narzędzi.

Podstawy

Adres bazowyhttps://mentai.pl/api/v1
Uwierzytelnianienagłówek Authorization: Bearer mentai_…
FormatJSON, UTF-8, daty w czasie polskim jako YYYY-MM-DDTHH:MM, kwoty w groszach (20000 = 200,00 zł)
Limity60 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 kluczaodczyt (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
Daneserwery 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.

ZdarzenieKiedy
visit.creatednowa wizyta — z panelu, rezerwacji online, API, ZnanegoLekarza albo serii
visit.updatedzmiana 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.cancelledodwołanie (status odwolana, anulowana_przez_gabinet albo termin_zwolniony); pole previousStatus
visit.deletedwizyta usunięta z systemu
payment.receivedwpłata online zaksięgowana na wizycie; pole paymentAmountGrosze
pingwiadomość 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

Bezpieczeństwo i RODO

Co dalej

Brakuje Ci czegoś w API? Napisz, czego potrzebujesz: kontakt@mentai.pl.