Kulcare Docs
General

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:

  1. 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.

  2. 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.

  3. 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:

  1. 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.

  2. One-click add-to-clinic. Pick a patient from the "Kulcare network" results, confirm the booking, and a Clinical::Patient record 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.

  3. 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::Patient row 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::User JWT. 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 clinicsWhat'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 patientMedical history (conditions, prescriptions, observations) — those stay tenant-scoped
Audit metadata to compliance reviewersAudit 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.

TriggerWhat's recorded
Patient confirms first-time-at-tenant booking from the patient appAudit::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 bucketAudit::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 matchAudit::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 PhiAccessLog row. 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/appointments is 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_logs table supports the query today; the UI is missing.
  • Compliance metadata visibility: the pre_existing_in_other_tenants count (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

CapabilityIssue
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

On this page