Patient cross-tenant experience — product reference
The product surface that turns Kulcare from a fragmented per-clinic experience into a unified patient surface, while keeping clinic-side data tenant-isolated.
Status (May 2026): Backend complete. Patient-app frontend wiring deferred.
What changed for patients
Before this work, the Kulcare patient experience was siloed. A patient who'd been to three clinics had to manage three relationships, three booking flows, three sets of appointment cards. Logging in via the patient app gave them visibility into one clinic only — the rest were invisible.
After this work:
-
One appointments list, all clinics. Patient app shows past + upcoming appointments across every facility the patient has visited, plus any vendor (wellness/fitness/nutrition) appointments. Doctor name, clinic name, location, chief complaint — all in a single chronologically-sorted view.
-
Search and book any practitioner from the patient app. Pick a doctor in any clinic via patient-app search. The first time at that clinic, see a one-tap consent prompt — "Your name and phone will be shared with [Clinic Name]. Continue?" — and the booking goes through. No phone OTP, no manual re-registration. The clinic gets a record of you; the booking lands in their queue.
-
Public-page bookings recognize returning users. When a patient on a practitioner's public profile enters a phone number that already belongs to a Kulcare account, they're prompted to log in to skip OTP. (Phase 3 — deferred.)
What changed for clinics
Before this work, every clinic had its own siloed patient list. Booking for a patient who'd been to another Kulcare clinic required manual re-registration. Even matching the same person across clinics was impossible.
After this work:
-
Federated patient search at booking. Search by phone, email, or name from the booking modal. Results show three sections: patients already at this clinic, patients in the Kulcare network (no other-clinic info exposed — just name, phone, age), and "no match" which guides to the manual create flow.
-
One-click add-to-clinic. Pick a patient from the "Kulcare network" results, confirm the booking, and a
Clinical::Patientrecord is auto-created at the current clinic linked to the same global identity. The clinic does not learn which other clinics that patient has visited. -
Audit trail for compliance. Every cross-clinic identity exposure (search seeing the result) and every auto-provision (booking confirmed) creates an audit row with the actor, the data subject, and forensic context. Meets HIPAA §164.312(b) and DPDP Act §11.
What didn't change
- Clinics still own their clinical data. A
Clinical::Patientrow at clinic A is fully tenant-isolated under PostgreSQL Row-Level Security. Clinic B can't see clinic A's medical records — it can only learn that the same human exists in the Kulcare identity network. - Patients still own their data. The patient app reads their own appointments via the
Identity::UserJWT. The clinic identity layer is what gets shared between clinics — clinical history doesn't. - Vendor flows unchanged. Wellness/fitness/nutrition vendors continue to use the marketplace flow (
Billing::Marketplace::OrdersController) — vendor appointments are not tenant-scoped. Workstream A unions them into the patient's appointment list, but Workstream C's clinical booking endpoint doesn't accept vendor bookings.
Privacy boundaries
| What's exposed across clinics | What's not |
|---|---|
| Existence of a Kulcare patient identity (name, phone, demographics) | The list of clinics that patient has visited |
| Whether the patient is registered via the patient app vs another clinic (binary enum) | Names of those other clinics |
| Patient's own appointments to the patient | Medical history (conditions, prescriptions, observations) — those stay tenant-scoped |
| Audit metadata to compliance reviewers | Audit metadata to clinics |
The federated-search privacy guard is implemented as an explicit field whitelist in Clinical::Patients::FederatedSearchService#serialize_in_kulcare. The privacy assertion is asserted in the spec — cross-clinic search response never includes other tenant/facility identifiers.
Consent model
| Trigger | What's recorded |
|---|---|
| Patient confirms first-time-at-tenant booking from the patient app | Audit::ConsentAuditLog with consent_type: data_sharing, collection_method: electronic, IP + user-agent stamped, granted_to: { facility_id, facility_name, tenant_id }, scope: { user_id, acting_user_id, via, ... } |
| Clinic adds a patient from the Kulcare-network search bucket | Audit::PhiAccessLog (clinic-side audit; the booking action itself is the consent envelope per established patterns) |
| Public booking page auto-links to existing user via phone match | Audit::ConsentAuditLog (Phase 3 — deferred) |
After the first consent, future bookings at the same facility don't prompt the patient again. Consent revocation (asking to be removed from a clinic's records) is a separate workstream — the audit infra supports it; the UI doesn't exist yet.
Compliance posture
- HIPAA §164.312(b) (audit controls): every cross-tenant identity exposure and every auto-provision creates a
PhiAccessLogrow. Forensic count of cross-tenant exposure is recorded but never surfaced in API responses. - HIPAA §164.502(a)(1)(i) (right of access): patient self-access to their own appointments via
GET /patient/appointmentsis intentionally not audited per-row. - DPDP Act 2023 §11 (data principal rights): patient-facing "who's seen my record" UI is captured as a follow-up issue (#1338). The
audit.phi_access_logstable supports the query today; the UI is missing. - Compliance metadata visibility: the
pre_existing_in_other_tenantscount (how many other clinics already have a record for this human) is recorded in audit logs only, never exposed to any clinic via API.
What's deferred
| Capability | Issue |
|---|---|
| Patient-app frontend (booking endpoint switch + consent checkbox) | #1346 |
| Clinic-app frontend (federated search PatientPicker with 3 buckets) | #1344 item 13 |
| Public booking phone-match prompt | #1346 Phase 3 |
| Patient-facing "who's seen my record" UI | #1338 |
| Audit log retention policy | #1338 |
References
- Developer guide: ../developer/patient/cross-tenant-appointments.md
- QA cases: ../testing/patient-cross-tenant-qa.md
- Brainstorm:
docs/brainstorms/2026-05-06-patient-cross-tenant-appointments-brainstorm.md - Plans:
docs/plans/2026-05-06-feat-clinic-federated-patient-search-plan.md,docs/plans/2026-05-06-feat-patient-app-authenticated-booking-plan.md