Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt

Use this file to discover all available pages before exploring further.

Version: 1.1.0 Last Updated: 2026-02-23 Status: Complete (all features including T11 Clinical Record Access)

Overview

PM-12 Patient Portal provides a self-service interface for patients. It integrates with multiple PM and PF specs via the Platform Integration Layer—no direct cross-core imports.

Integration Matrix

IntegrationSpecDirectionPatternStatus
Self-SchedulingPM-03Portal → PMDirect PM query (same core)✅ Complete
Intake/Consent FormsPF-08Portal → PFPlatform Integration Layer✅ Complete
Demographic UpdatesPM-01Portal → PMDirect PM query + form submission✅ Complete
Billing & PaymentsPM-09Portal → PMDirect PM query (same core)✅ Complete
Secure MessagingPM-14Portal → PMDirect PM hooks (same core)✅ Complete
Clinical RecordsCL-*Portal → CLPlatform Integration Layer✅ Complete

Contract: Self-Scheduling (PM-03)

Hook: usePortalAppointments Source: src/cores/pm/hooks/usePortalAppointments.ts Data Access:
  • Table: pm_appointments
  • Filter: .eq('organization_id', orgId).eq('patient_id', patientId)
  • Scope: patientId from usePortalPatientId() (enforces proxy scope)
Operations:
OperationMethodAuthTenant Filter
List upcomingSELECTPortal userorg_id + patient_id
List pastSELECTPortal userorg_id + patient_id
CancelUPDATE (status → cancelled)Portal userorg_id + patient_id + appointment_id
Constraints:
  • Cancellation only for status = 'scheduled'
  • No direct booking of new slots (future enhancement)
  • RLS enforces patient isolation at DB level

Contract: Intake/Consent Forms (PF-08)

Page: PortalFormsPage Source: src/cores/pm/pages/portal/PortalFormsPage.tsx Integration Pattern:
  • Uses FormEmbed from @/platform/forms (Platform Integration Layer)
  • Patient context injected from usePortalPatientId()
  • Form submissions stored in fw_form_submissions with patient linkage
Operations:
OperationMethodAuthNotes
List available formsPlatform forms APIPortal userFiltered by owningCore
Submit formPlatform forms APIPortal userLinked to patient_id

Contract: Demographic Updates (PM-01)

Hook: usePortalDemographicRequests Source: src/cores/pm/hooks/usePortalDemographicRequests.ts Data Access:
  • Read: pm_patients (current demographics, read-only display)
  • Write: fw_form_submissions (update request submission)
Operations:
OperationMethodAuthNotes
View demographicsSELECT on pm_patientsPortal userRead-only, scoped by patient_id
Submit update requestINSERT on fw_form_submissionsPortal userStaff reviews via PM-01 workflow

Contract: Billing & Payments (PM-09)

Hook: usePortalBilling Source: src/cores/pm/hooks/usePortalBilling.ts Data Access:
  • Tables: pm_charges, pm_payments
  • Filter: .eq('organization_id', orgId).eq('patient_id', patientId)
Operations:
OperationMethodAuthNotes
View balanceSELECT (charges - payments)Portal userAggregated from pm_charges and pm_payments
View payment historySELECT on pm_paymentsPortal userScoped by patient_id
Constraints:
  • No direct card data handling (PCI compliance via existing PM-09 flow)
  • Balance is calculated client-side from total_amount columns

Contract: Secure Messaging (PM-14)

Hook: usePortalMessaging Source: src/cores/pm/hooks/usePortalMessaging.ts Integration Pattern:
  • Thin wrapper around existing PM-14 hooks
  • Enforces senderType: 'patient' on all outbound messages
  • Conversations filtered by patient_id from usePortalPatientId()
Operations:
OperationMethodAuthNotes
List conversationsPM-14 hooksPortal userPatient-visible only
View threadPM-14 hooksPortal userScoped by conversation participant
Send messagePM-14 hooksPortal usersenderType forced to ‘patient’
Create conversationPM-14 hooksPortal userCategory defaults set

Contract: Clinical Record Access (CL-*) — ✅ Implemented

Hook: usePortalClinicalSummary Source: src/cores/pm/hooks/usePortalClinicalSummary.ts Page: PortalClinicalPage Source: src/cores/pm/pages/portal/PortalClinicalPage.tsx Integration Pattern:
  • Read-only access to clinical chart, medications, and treatment goals
  • Consent enforcement via useConsentCheck from @/platform/clinical (Platform Integration Layer)
  • SUD-indicated data gated by CL-11 cl_check_sud_consent RPC
  • Non-SUD data always accessible (21st Century Cures Act compliance)
  • Patient context from usePortalPatientId() (proxy-aware)
Data Access:
  • Tables: cl_patient_charts, cl_medications, cl_treatment_goals
  • Filter: .eq('patient_id', patientId) from usePortalPatientId()
Operations:
OperationMethodAuthNotes
View chart summarySELECT on cl_patient_chartsPortal userRead-only, scoped by patient_id
View medicationsSELECT on cl_medicationsPortal userActive/on_hold only
View treatment goalsSELECT on cl_treatment_goalsPortal userActive/in_progress only
Consent Enforcement:
  • If cl_patient_charts.flags.sud_indicated is true, calls cl_check_sud_consent RPC
  • Without consent: diagnosis-level data suppressed, medications and goals still visible (non-SUD)
  • Consent banner displayed when data is restricted

Security Architecture

Authentication

  • Supabase Auth (email/password + TOTP MFA)
  • Portal sessions audited in pm_portal_sessions (append-only)
  • Account lockout after 5 failed attempts

Data Isolation

  • RLS Layer: pm_portal_has_org_access() and pm_portal_visible_patient_ids() (SECURITY DEFINER)
  • Application Layer: usePortalPatientId() hook enforces scope on all queries
  • Proxy Support: Active, non-expired proxy relationships only

Tables with FORCE ROW LEVEL SECURITY

  • pm_portal_users
  • pm_portal_sessions