21 KiB
MEDEVIO API -- Working Notes
General Architecture
- Frontend: React + Material-UI (MUI) + Apollo GraphQL
- Backend: GraphQL API ->
https://api.medevio.cz/graphql - Authentication: cookies/tokens stored in
medevio_storage.json(Playwright session) - Frontend base:
https://my.medevio.cz/mudr-buzalkova/klinika/... - Session handling: token refreshed through
AccessToken_AuthSelfcalls
Authentication and Session
Reuse session cookies from Playwright:
state = json.load(open("medevio_storage.json"))
cookies = {c["name"]: c["value"] for c in state["cookies"] if "medevio" in c["domain"]}
Use these cookies in every requests.post(GRAPHQL_URL, headers, cookies, data=...) call.
HTTP Setup (Python)
GRAPHQL_URL = "https://api.medevio.cz/graphql"
headers = {
"content-type": "application/json",
"origin": "https://my.medevio.cz",
"referer": "https://my.medevio.cz/",
}
response = requests.post(GRAPHQL_URL, headers=headers, cookies=cookies, data=json.dumps(payload))
Key IDs
| Entity | ID |
|---|---|
| Clinic | 25f24970-dae3-4f80-9337-d3616e53fb10 |
| Clinic slug | mudr-buzalkova |
| Calendar MUDr. Buzalkova (manzelka) | 144c4e12-347c-49ca-9ec0-8ca965a4470d |
| Calendar Vlado | b6555c7e-4e95-4657-b441-87c2c9a7b2ca |
| AIS entity (Medicus) | ef1549a5-d266-4f52-9a4d-7275e79ac82e |
Complete GraphQL Operations Catalog
Session & Auth
| Operation | Purpose |
|---|---|
AccessToken_AuthSelf |
Verify or refresh session |
Search
| Operation | Variables | Response |
|---|---|---|
Search |
clinicSlug, locale, query |
clinic, results (patients + requests) |
Patient Card (modal)
| Operation | Variables | Response |
|---|---|---|
ClinicPatientDetailModal_GetData |
clinicSlug, patientId, patientUuid, locale, challengesStatus |
clinic, calendars, patient (full detail), challenges, patientRequestsResponse, treatmentPlanPatients, premiumPlans, insuranceCards, pinnedRequests, canBeRemoved |
ClinicPatientRequestHistory_FilterPatientRequestsForClinic |
clinicSlug, locale, patientId, patientRequestFilter, pageInfo {first, offset} |
filterResponse, clinic, queues, calendars, userECRFs, substates, tags |
ClinicPatientDetailModal_FindMergeSuggestions |
clinicSlug, patientUuid |
mergeSuggestions |
Patient object structure (from GetData)
patient {
id, name, surname, identificationNumber (rodne cislo bez lomitka),
sex (Male/Female), dob (YYYY-MM-DD), email, phone,
kind (Human), type (Human), status (ACTIVE),
isInClinic, isInOrganizationOnly, hasMobileApp,
anamnesisShared, anamnesisStatusForClinic { updatedAt },
insuranceCompanyObject { id, code, name, shortName },
tags [], familyMembers [], clinics [],
note, organizationNotes [],
city, street, houseNumber,
user { id, name, surname, email, phone, registrationCompletedTime },
createdAt, editableByDoctor, userRelationship, premiumPlanPatient
}
Patient Edit
| Operation | Variables | Response |
|---|---|---|
EditPatientModal_GetClinic |
clinicSlug |
clinic |
ClinicEditPatient_GetPatientForClinic |
clinicSlug, patientId, includePatient |
patient, clinic |
ClinicEditPatient_ListInsuranceCompanies |
country |
insuranceCompanies [] (id, code, name) |
UseSupportedCountryCodes_Codes |
(none) | codes (phone country codes) |
Patient List (Kartoteka)
| Operation | Variables | Response |
|---|---|---|
LegacyClinicPatientListPage_ListClinicPatients |
clinicSlug, criteria {premiumPlanId, tagId}, pageInfo {first: 50, offset} |
count, patients, clinic |
Fungující dotaz pro stránkovaný seznam pacientů (sync)
Pozor: listClinicPatients nefunguje pro hromadný export. Fungující query je listPatients:
query SyncListPatients($clinicSlug: String!, $pageInfo: PageInfo!, $filter: ListPatientFilter!) {
patientsList: listPatients(clinicSlug: $clinicSlug, filter: $filter, pageInfo: $pageInfo) {
count
patients {
id
identificationNumber
name
surname
sex
phone
status2
isInClinic
locale
insuranceCompanyObject { id shortName }
user { id phone name surname }
}
}
}
Variables:
{
"clinicSlug": "mudr-buzalkova",
"filter": {},
"pageInfo": { "first": 50, "offset": 0 }
}
Stránkování: offset se zvyšuje po 50, count v odpovědi = celkový počet pacientů.
Dotaz pro detail jednoho pacienta
query GetPatientDetail($clinicSlug: String!, $patientId: String!) {
patient: getPatientForClinic(clinicSlug: $clinicSlug, patientId: $patientId) {
id
name
surname
identificationNumber
sex
dob
email
phone
status
isInClinic
hasMobileApp
anamnesisShared
note
city
street
houseNumber
createdAt
insuranceCompanyObject {
code
name
}
user {
id
email
phone
}
}
}
Variables:
{
"clinicSlug": "mudr-buzalkova",
"patientId": "<UUID>"
}
Sync pacientů do MySQL
Dvoustupňový proces:
listPatientsstránkovaně stáhne seznam všech patient IDs (po 50)- Pro každého pacienta se zavolá
getPatientForClinicpro detail - UPSERT do
medevio.medevio_pacient(23 sloupců)
Skript: Medevio/80 Pacienti/sync_patients_to_mysql.py
Výsledek: 1963 pacientů synchronizováno (květen 2026).
Má pacient Medevio účet? → user_id IS NOT NULL (žádný redundantní boolean sloupec)
- 1 230 pacientů má účet (63 %), 733 nemá (37 %) — stav květen 2026
Známé problémy
-
hasMobileAppje nefunkční na úrovni pacienta — ověřeno na více endpointech:getPatientForClinic→ vždyfalse❌ClinicPatientDetailModal_GetData→patient.hasMobileAppvždyfalse❌listPatients→ pole vůbec neexistuje v GraphQL schema ❌
-
Správná hodnota
hasMobileAppje na objekturequest, nikoli pacienta:ClinicRequestDetail_GetPatientRequest2→request.hasMobileAppvrací správně ✅request.extendedPatient.hasMobileAppje stále vždyfalse❌- UI komponenta
PatientRequest.MessageSendčte hodnotu zrequest.hasMobileAppa rozhoduje:true→ zobrazí "Odeslat do aplikace"false+ telefon existuje → zobrazí "Odeslat přes SMS"false+ žádný telefon → tlačítko se nezobrazí vůbec
-
Praktické získání
hasMobileApppro pacienty:- Stáhnout všechny požadavky přes
ClinicLegacyRequestList_ListPatientRequestsForClinic(aktivní i vyřízené) - Pro každého pacienta vzít nejnovější
hasMobileAppz jeho požadavků - Nevýhoda: pacienti bez jediného požadavku budou
NULL; hodnota odráží stav k době posledního požadavku
- Stáhnout všechny požadavky přes
Requests (Pozadavky)
| Operation | Variables | Response |
|---|---|---|
ClinicLegacyRequestList_ListPatientRequestsForClinic |
clinicSlug, locale, pageInfo {first: 30, offset}, queueAssignment, queueId, state (ACTIVE/DONE) |
requests, clinic |
ClinicRequestList2 |
clinicSlug, queueId, queueAssignment, state (ACTIVE/DONE), pageInfo {first, offset}, locale |
requestsResponse { count, patientRequests [] } |
ClinicRequestList2 volá listPatientRequestsForClinic2 — novější endpoint, vrací count a plně stránkovaný seznam. Struktura položky:
patientRequest { id, displayTitle, createdAt, updatedAt, doneAt, removedAt,
extendedPatient { name, surname, identificationNumber },
lastMessage { createdAt } }
Skript: Medevio/10ReadPozadavky/PRAVIDELNE_0_READ_ALL_ACTIVE_POZADAVKY.py
Request list item structure
request {
id, createdAt, dueDate, displayTitle, doneAt, removedAt,
priority, evaluationResult, clinicId,
extendedPatient { id, identificationNumber, kind, name, surname,
note, owner, key, premiumPlanPatient, status, tags [] },
queue { id, name }, substate, pinType,
ecrf { id, name, iconMaskColor { name } },
flags [], hasMobileApp
}
Request Creation (Vytvoření požadavku lékařem) — ODCHYCENO 2026-06-13
Lékařský účet (klinický token) NEumí vyplnit pacientský dotazník smysluplně — formulář
„Recept na léky" má z lékařské strany (sid: ERECEPT_SIMPLEST_BEZ_DAVKOVANI) jen jedno
pole nazev-leku, kdežto pacient v appce vyplní dvě pole („Název léků" + „Poznámka").
Proto: obsah z e-mailu zapisujeme do INTERNÍ POZNÁMKY, ne do dotazníku.
Vytvoření prázdného požadavku „Recept na léky" je dvoukrok:
# 1) vytvoř (prázdný) ECRF fill → vrátí ecrfFill.id
mutation ClinicRequestCreateModal_FillECRFForm($input: FillECRFFormInput!) {
ecrfFill: fillECRFForm(input: $input) { id }
}
# input: { byDoctor: true, fields: [], patientId, sid: "ERECEPT_SIMPLEST_BEZ_DAVKOVANI", stepId: "erecept-gp-request" }
# 2) vytvoř požadavek s odkazem na ecrfFill
mutation ClinicRequestCreateModal_CreateRequest($clinicSlug: String!, $input: CreatePatientRequestWithoutReservationInput!) {
patientRequest: createPatientRequestWithoutReservation(clinicSlug: $clinicSlug, input: $input) { id }
}
# input: { patientId, userECRFId, ecrfFillIds: [<id z kroku 1>], createdByDoctor: true, shouldInvitePatient: false }
| Klíč | Hodnota |
|---|---|
ECRF „Recept na léky" userECRFId |
79488e86-e9e5-47e3-8b19-7e5229427f23 |
ECRF sid |
ERECEPT_SIMPLEST_BEZ_DAVKOVANI |
ECRF stepId |
erecept-gp-request |
Seznam typů požadavků: UserEcrfAutocomplete_ListUserECRFsByClinic.
Tagy / štítky požadavku — ODCHYCENO 2026-06-13
query TagRequestEditModal_ListTags($clinicSlug: String!, $requestId: UUID!) { ... } # seznam štítků + zda jsou přiřazené
mutation TagRequestEditModal_AssignTagToRequest($clinicSlug: String!, $requestId: UUID!, $tagId: UUID!) {
tagRequest: assignTagToPatientRequest(clinicSlug: $clinicSlug, patientRequestId: $requestId, tagId: $tagId) { id }
}
| Štítek | tagId | barva |
|---|---|---|
CLAUDE |
c136aeca-0625-4c43-b81f-fc3949ec6ba6 |
ORCHID |
NEZAPOMENOUT |
5bced917-83d2-46db-896c-c8e615de1a69 |
GREY |
Request Detail
| Operation | Variables | Response |
|---|---|---|
ClinicRequestDetail_GetPatientRequest2 |
clinicSlug, isDoctor, requestId, locale |
request (full detail) |
UseMessages_ListMessages |
requestId, updatedSince |
messages |
Communication_GetClinicFooter |
clinicSlug |
footer |
ClinicRequestNotes_Get |
patientRequestId |
notes [] |
ClinicRequestNotes_Update |
noteInput { id, content } |
{ id } |
ClinicRequestNotes_Create |
noteInput { requestId, content } |
{ id } |
ClinicRequestDetail_GetMessages |
clinicSlug, requestId |
zprávy (alternativní endpoint) |
Interní poznámky k požadavku (klinické notes)
# Čtení
query ClinicRequestNotes_Get($patientRequestId: String!) {
notes: getClinicPatientRequestNotes(requestId: $patientRequestId) {
id content createdAt updatedAt createdBy { id name surname }
}
}
# Aktualizace existující
mutation ClinicRequestNotes_Update($noteInput: UpdateClinicPatientRequestNoteInput!) {
updateClinicPatientRequestNote(noteInput: $noteInput) { id }
}
# Vytvoření nové
mutation ClinicRequestNotes_Create($noteInput: CreateClinicPatientRequestNoteInput!) {
createClinicPatientRequestNote(noteInput: $noteInput) { id }
}
K jednomu požadavku existuje typicky jedna interní poznámka. Pokud neexistuje → Create, pokud existuje → Update.
Skript: Medevio/30 ManipulacePoznámek/101 JednoducheDoplneniInterniPoznamky.py
Alternativní endpoint pro zprávy konverzace
query ClinicRequestDetail_GetMessages($clinicSlug: String!, $requestId: ID!) {
clinicRequestDetail_GetPatientRequestMessages(clinicSlug: $clinicSlug, requestId: $requestId) {
id text createdAt
sender { id name }
extendedPatient { name surname identificationNumber }
}
}
Skript: Medevio/10ReadPozadavky/10 UpdateMessageswithJmeno.py
Request detail structure
request {
id, doneAt, doneBy { id, name, surname },
removedAt, queue, isInClinic,
clinic { id, paymentEnabled, hasInvoicingEnabled, slug, features },
clinicMedicalRecord, clinicMedicalRecordVisibleToPatient,
extendedPatient { ... (same as patient) },
ecrf, ecrfFilledData, eventType (PATIENT_REQUEST),
flags [], invoice, userNote, paymentData,
recording, reservations [], evaluationResult, tags [],
createdAt, createdBy, customTitle, createdByDoctor,
displayTitle, referringClinic, pinType,
dueDate, substate, hasMobileApp,
priceWhenCreated, currencyWhenCreated
}
Calendar (Kalendar)
| Operation | Variables | Response |
|---|---|---|
ClinicCalendar_ListClinicReservations |
calendarIds [], clinicCountry, clinicSlug, locale, since (ISO), until (ISO), showTimeSlots, schedulePatientId, emptyCalendarIds |
holidays, reservations, vacations |
ClinicCalendar_GetWindows |
calendarIds [], clinicSlug, locale, since, until |
calendarWindows (ordinacni hodiny) |
Agenda_ListAll |
calendarIds [], clinicSlug, locale, since, until |
reservations (jednorazove) + recurringReservations (opakujici) |
Agenda_ListAll volá dva endpointy zároveň:
listClinicReservations→ jednorazové rezervace (pacienti + poznámky lékaře)listClinicRecurringReservations→ opakující se rezervace; vracírecurringReservation { id calendarId color note rrule { frequency interval dtstart tzid byweekday bymonthday byweekno } }ainstances { start end note color }
Fungující skript: Medevio/agenda_dne.py — funkce list_agendu(start, end, calendar)
Vytvoření poznámky lékaře v kalendáři
mutation CreateReservation_MakeReservationByDoctor(
$clinicSlug: String!, $color: ECRFIconColor, $note: String!, $timeSlotInput: TimeSlotInput!
) {
reservation: makeReservationByDoctor(
clinicSlug: $clinicSlug color: $color note: $note timeSlotInput: $timeSlotInput
) { id __typename }
}
Variables: clinicSlug, color (např. "CHARCOAL"), note (text), timeSlotInput { calendarId, start (UTC ISO), end (UTC ISO) }
Vrací: reservation.id — UUID nové rezervace.
Skript: Medevio/zapis_poznamky.py — funkce zapis_poznamku(calendar, den, cas, trvani_min, poznamka, color)
Smazání / zrušení rezervace
# Jednorazová:
mutation UpdateReservation_CancelReservationByDoctor(
$clinicSlug: String!, $reservationId: UUID!
) {
reservation: cancelReservationByDoctor(clinicSlug: $clinicSlug, reservationId: $reservationId) { id __typename }
}
# Opakující se:
mutation UpdateReservation_CancelRecurringReservationByDoctor($input: RemoveRecurringReservationInput!) {
success: removeDateFromRecurringReservation(input: $input)
}
Pro opakující se: input { clinicSlug, recurringReservationId, date (UTC ISO), updateType: "Single"|"ThisAndFuture"|"All" }
Skript: Medevio/smaz_poznamku.py — funkce smaz_jednorazovou(reservation_id), smaz_opakujici(recurring_id, date, update_type)
Reservation structure
reservation {
id, calendarId,
clinic { id, slug, aisEntity { id, slug } },
color, done, canceledAt,
end (ISO), note,
request {
displayTitle, id,
ecrf { id, name, iconMaskColor { name } },
extendedPatient { dob, email, id, identificationNumber,
kind, name, surname, note, phone, sex, status, tags [],
insuranceCompanyObject { code, name } }
}
}
Patient Status Change (Zmena stavu)
| Operation | Variables | Response |
|---|---|---|
ClinicPatientEditStatusModal_UpdateClinicPatientStatus |
clinicSlug, patientId (UUID), status |
updated (boolean) |
Dostupné stavy (ClinicPatientStatus)
| UI název | API hodnota |
|---|---|
| Aktivní | ACTIVE |
| Odmítnutý | DECLINED |
| Vyřazený | REMOVED |
| Čekající | PENDING |
Mutation
mutation ClinicPatientEditStatusModal_UpdateClinicPatientStatus($clinicSlug: String!, $patientId: String!, $status: ClinicPatientStatus!) {
updated: updateClinicPatientStatus(
clinicSlug: $clinicSlug
patientId: $patientId
status: $status
)
}
Odpověď: { "data": { "updated": true } }
Skript pro hromadnou změnu: Medevio/80 Pacienti/bulk_set_removed.py
References (Referovani)
| Operation | Variables | Response |
|---|---|---|
ClinicReferences_GetClinic |
clinicSlug |
clinic, substates |
ClinicReferences_ListReferencesSentByClinic |
clinicSlug, locale, pageInfo {first: 51, offset} |
references |
Treatment Plans (Lecebne plany)
| Operation | Variables | Response |
|---|---|---|
PlanPatientsPage_GetClinic |
clinicSlug |
clinic |
PlanPatientsPage_ListPlanPatients |
clinicSlug, pageInfo {first: 51, offset} |
planPatients |
Settings (Nastaveni)
| Operation | Purpose |
|---|---|
ClinicSettingsSubNavigation_GetClinic |
Navigation state for settings |
ClinicRequestSettings_GetClinic |
Request settings |
UserEcrfListing_GetClinicECRFMenu |
List request types (ECRF forms) |
ClinicEcrfSet_ListECRFSets |
List request type groups |
ClinicOpeningHoursEdit_GetClinicOpeningHours2 |
Opening hours settings |
ClinicPaymentsSettings_GetClinic |
Payment settings |
StripeVerificationContext_GetClinicCompanyInfo |
Stripe payment verification |
SettingsPage |
General settings |
InviteSettings |
Invite settings |
InviteSettings_GetInviteContent |
Invite content/templates |
ImportAisSettings_GetClinic |
AIS integration settings |
ImportAisSettings_GetAises |
Available AIS systems |
ClinicUserGrid_Columns |
User grid columns (queues, calendars) |
ClinicUserGrid_Rows |
User list for clinic |
Clinic Info
| Operation | Purpose |
|---|---|
ClinicNavigation_GetClinic |
Basic clinic info |
ClinicAgenda_GetCalendarsForClinic |
List calendars within clinic |
Playwright Selectors
| Purpose | Selector |
|---|---|
| Agenda rows | div[data-testid='reservation-row'] |
| Row ID | data-id |
| Agenda fields | div[data-field='Time'], div[data-field='Patient'], div[data-field='Reason'] |
URL Patterns
| Page | URL |
|---|---|
| Search | /mudr-buzalkova/klinika/hledat/?q={query} |
| Patient card | /mudr-buzalkova/klinika/hledat/?q={query}&pacient={uuid} |
| Request detail | /mudr-buzalkova/klinika/hledat/?q={query}&pozadavek={uuid} |
| Requests (active) | /mudr-buzalkova/klinika/pozadavky/ |
| Requests (done) | /mudr-buzalkova/klinika/pozadavky?vyrizene=1 |
| Calendar | /mudr-buzalkova/klinika/kalendar/?kalendar={calendarId}&pohled=pracovni-tyden |
| Patient list | /mudr-buzalkova/klinika/kartoteka/ |
| References | /mudr-buzalkova/klinika/reference |
| Treatment plans | /mudr-buzalkova/klinika/lecebne-plany/pacienti |
| Settings - requests | /mudr-buzalkova/klinika/nastaveni/pozadavky |
| Settings - groups | /mudr-buzalkova/klinika/nastaveni/skupiny-pozadavku |
| Settings - invites | /mudr-buzalkova/klinika/nastaveni/pozvanky |
| Settings - integrations | /mudr-buzalkova/klinika/nastaveni/propojky |
| Settings - hours | /mudr-buzalkova/klinika/nastaveni/ordinacni-doba |
| Settings - payments | /mudr-buzalkova/klinika/nastaveni/platby |
| Users | /mudr-buzalkova/klinika/uzivatele/ |
Insurance Companies (CZ)
| Code | Name |
|---|---|
| 0 | Ministerstvo zdravotnictvi CR |
| 111 | VZP |
| 201 | VoZP |
| 205 | CPZP |
| 207 | OZP |
| 209 | ZPS |
| 211 | ZPMV |
| 213 | RBP |
| 999 | Jina |
Clinic Features (enabled)
RESERVATION_REMINDERS, PUSH_NOTIFICATIONS, INVITES, ON_DEMAND_SLOT_GENERATOR, REQUEST_TYPES_ACCESS_RULES, CAN_REFER_PATIENTS, TREATMENT_PLANS_V2, MEDICAL_RECORDS, CAMPAIGNS, PATIENT_SEGMENTATION, ADVANCED_CALENDAR_FUNCTIONS, PATIENT_CLINIC_STATUS, STANDARD_TARIFF_FUNCTIONS, CLINIC_CLOSED_WARNINGS, ADVANCED_SWITCHBOARD, REQUEST_AUTOMATIONS, REQUEST_PINNING, AI_SWITCHBOARD_PROMO, PATIENT_REQUEST_SUBSTATES
Security / Maintenance Notes
- GraphQL introspection disabled.
- Refresh session cookies periodically.
- Keep data GDPR-compliant.
Future Tasks
- Automate cookie renewal.
- Build helper functions:
get_reservations(date)usingClinicCalendar_ListClinicReservationsget_reservation_detail(reservation_id)get_patient(patient_id)usingClinicPatientDetailModal_GetDatalist_patients(offset)usingLegacyClinicPatientListPage_ListClinicPatientssearch_patients(query)usingSearchget_requests(state, offset)usingClinicLegacyRequestList_ListPatientRequestsForClinicget_request_detail(request_id)usingClinicRequestDetail_GetPatientRequest2
- Map vaccine keywords.
- Export to Excel/MySQL for vaccine order planning.