Skip to main content
Feature ID: CL-06
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. The cl-transmit-prescription edge function described below was an MVP stub (returned fake STUB-… 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

  1. Overview
  2. Quick Reference
  3. Integration Points
  4. API / Data Contracts
  5. Event Contracts
  6. Edge Functions
  7. Decision Trees
  8. Pattern Library
  9. Security and RLS
  10. Common Mistakes
  11. Pre-Flight Checklist
  12. 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 in ChartMedicationsSection) 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 has site_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 filter deleted_at IS NULL
  • Custom fields: custom_fields JSONB NOT NULL DEFAULT '{}'

cl_pharmacies

  • Scope: organization_id only (no site_id — org-wide pharmacy directory; see CL-06 Clarifications)
  • Address columns: address_line_1, address_line_2, city, state, zip_code (no single address column)
  • 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 filter WHERE 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_sent is published by the edge function using createServiceClient(), not from React client code. The event fires only after Surescripts acknowledgement (status transitions to acknowledged) 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:
Responsibilities:
  1. Validate JWT + org access (validateAuth, verifyOrgAccess)
  2. Fetch prescription from cl_prescriptions (verify ownership)
  3. Call Surescripts stub (MVP) or real Surescripts API (production)
  4. Update transmission_status and surescripts_message_id on success
  5. Write audit log entry for controlled-substance transmissions (pf_audit_logs)
  6. Publish prescription_sent event via fw_domain_events insert
  7. Return { success: true, transmission_status: 'acknowledged', correlationId }
EPCS path (controlled substances):
  • Validate epcs_token is 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 SECURITY on both tables (PHI)
  • All UPDATE policies include both USING and WITH 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_sent in KnownEventNamesrc/platform/events/types.ts ✅ Done
  • 5 CL-06 permission constants in CL_PERMISSIONSsrc/platform/permissions/constants.ts ✅ Done
  • cl_has_org_access_for_epcs function exists in DB (CL-06 migration)
  • Migration uses idempotent CREATE TABLE IF NOT EXISTS pattern
  • RLS policies use idempotent DO $$ BEGIN IF NOT EXISTS... pattern (see follow-up migration)
  • FORCE ROW LEVEL SECURITY on both tables
  • UPDATE policies include both USING and WITH CHECK
  • site_id on cl_prescriptions (nullable); absent from cl_pharmacies (documented)
  • deleted_at on both tables
  • custom_fields on cl_prescriptions; custom_fields on cl_pharmacies (migration includes both)
  • fw_workflow_events seed for prescription_sent with ON CONFLICT DO NOTHING
  • pf_module_permissions seed for 5 CL-06 keys with ON CONFLICT DO NOTHING