Status: 🚧 In Progress
Spec: CL-06-e-prescribing-epcs.md
Last Updated: 2026-06-29
Last Verified: 2026-02-18
[!IMPORTANT] Vendor update (2026-06, #1921 / #1966): WENO is the e-prescribing / EPCS vendor. Non-controlled and controlled (EPCS) prescribing are transmitted via WENO ComposeRx (EPCS Prescriber role + ID.me), with NewRx sync into the chart and the CL-64 EPCS audit trail — see CL-06-EN-23. Thecl-transmit-prescriptionedge function described below was an MVP stub (returned fakeSTUB-…ids) and has been removed; the sections that describe it as the active transmit path are historical. The formal Surescripts-network certification track is owned by PM-73 and is separate from the WENO adapter.
Table of Contents
- Overview
- Quick Reference
- Integration Points
- API / Data Contracts
- Event Contracts
- Edge Functions
- Decision Trees
- Pattern Library
- Security and RLS
- Common Mistakes
- Pre-Flight Checklist
- Related Docs
Overview
CL-06 defines electronic prescribing (e-prescribing) and EPCS (electronic prescribing for controlled substances): Surescripts integration, NCPDP SCRIPT 2023011, RTPB, and integration with medication list (CL-05), clinical decision support (CL-08), and Arizona CSPMP (CL-17). Prescriptions surface within the Medications tab of PatientChartPage (embedded inChartMedicationsSection) via a permission-gated “New Prescription” button. A standalone /cl/pharmacies route manages the org-level pharmacy directory.
Quick Reference
Integration Points
API / Data Contracts
cl_prescriptions
- Scope:
organization_id+chart_id; also hassite_id(nullable) for site-level context - PHI: Yes — full RLS enforcement + FORCE ROW LEVEL SECURITY
- Key FK columns:
chart_id → cl_patient_charts(id),prescriber_id → pf_profiles(id),pharmacy_id → cl_pharmacies(id) - Transmission status lifecycle:
pending → sent → acknowledged | error | cancelled - EPCS columns:
is_controlled,dea_schedule,epcs_token(TEXT — set when EPCS two-factor completed) - RTPB columns:
rtpb_checked_at(TIMESTAMPTZ),rtpb_formulary_status,rtpb_patient_cost - Other columns:
medication_name,ndc_code,strength,form,route,quantity,quantity_unit,days_supply,refills,sig,transmitted_at,error_message - Soft delete:
deleted_at TIMESTAMPTZ— all application queries must filterdeleted_at IS NULL - Custom fields:
custom_fields JSONB NOT NULL DEFAULT '{}'
cl_pharmacies
- Scope:
organization_idonly (nosite_id— org-wide pharmacy directory; see CL-06 Clarifications) - Address columns:
address_line_1,address_line_2,city,state,zip_code(no singleaddresscolumn) - EPCS flag:
is_surescripts_enabled BOOLEAN— picker filters this for controlled-substance prescriptions - NCPDP ID:
ncpdp_id(TEXT); application enforces uniqueness per org - Soft delete:
deleted_at TIMESTAMPTZ— pharmacy picker queries filterWHERE deleted_at IS NULL AND is_active = true - Custom fields:
custom_fields JSONB NOT NULL DEFAULT '{}'(migration includes it)
Privileged Operations (Edge Function Only)
- Prescription transmit to Surescripts: Edge Function
cl-transmit-prescription; service role client; validates auth before transmit - EPCS signing/token validation: Edge Function only (FIPS path delegated to certified vendor: DrFirst or DoseSpot)
- SECURITY DEFINER functions used only for DB operations that bypass RLS (audit writes, webhook callbacks)
Event Contracts
Outbound: prescription_sent
Payload schema:
Note:prescription_sentis published by the edge function usingcreateServiceClient(), not from React client code. The event fires only after Surescripts acknowledgement (status transitions toacknowledged) or on MVP stub success.
No Inbound Events
CL-06 does not subscribe to events from other cores for MVP. Future: CL-17 PDMP result may trigger a workflow event once CL-17 is implemented.Edge Functions
cl-transmit-prescription
Request body:
- Validate JWT + org access (
validateAuth,verifyOrgAccess) - Fetch prescription from
cl_prescriptions(verify ownership) - Call Surescripts stub (MVP) or real Surescripts API (production)
- Update
transmission_statusandsurescripts_message_idon success - Write audit log entry for controlled-substance transmissions (
pf_audit_logs) - Publish
prescription_sentevent viafw_domain_eventsinsert - Return
{ success: true, transmission_status: 'acknowledged', correlationId }
- Validate
epcs_tokenis set on the prescription record before transmitting (EPCS two-factor completed) - Delegate FIPS 140-2 / token validation to certified vendor (DrFirst/DoseSpot stub in MVP)
Decision Trees
When to Use Edge Function vs. Client Mutation
EPCS Flow (Controlled Substances)
Pharmacy Picker Logic
Pattern Library
Pharmacy Picker (Client Hook)
Surescripts MVP Stub Pattern
Edge Function: Publish prescription_sent
Security and RLS
RLS Policy Summary
FORCE ROW LEVEL SECURITYon both tables (PHI)- All UPDATE policies include both
USINGandWITH CHECK(constitution §5.2.4) - Migration uses idempotent
DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_policies...) THEN CREATE POLICY... END IF; END $$pattern
Audit Requirements
- All controlled-substance prescription views/creates/updates must be logged to
pf_audit_logs(user_id, timestamp, action, prescription_id) epcs_token(TEXT) set on prescription record when EPCS two-factor completed- Edge function writes audit entry for EPCS transmissions
- PHI: no prescription data in console logs; no PHI in AI prompts or event payloads beyond stable identifiers
Common Mistakes
Pre-Flight Checklist
Before running the Phase 1 migration:-
prescription_sentinKnownEventName—src/platform/events/types.ts✅ Done - 5 CL-06 permission constants in
CL_PERMISSIONS—src/platform/permissions/constants.ts✅ Done -
cl_has_org_access_for_epcsfunction exists in DB (CL-06 migration) - Migration uses idempotent
CREATE TABLE IF NOT EXISTSpattern - RLS policies use idempotent
DO $$ BEGIN IF NOT EXISTS...pattern (see follow-up migration) -
FORCE ROW LEVEL SECURITYon both tables - UPDATE policies include both USING and WITH CHECK
-
site_idoncl_prescriptions(nullable); absent fromcl_pharmacies(documented) -
deleted_aton both tables -
custom_fieldsoncl_prescriptions;custom_fieldsoncl_pharmacies(migration includes both) -
fw_workflow_eventsseed forprescription_sentwithON CONFLICT DO NOTHING -
pf_module_permissionsseed for 5 CL-06 keys withON CONFLICT DO NOTHING