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.0.0
Last Updated: 2026-02-17
Constitution Reference: Section 1.2 (Core Independence), Section 1.3 (Integration Patterns)
Source: CL-PM-SPEC-REVIEW Findings 2.1, 5.1
Overview
This contract defines how clinical documentation (CL core) triggers charge capture (PM core) without direct cross-core dependencies. Both CL and PM depend only on Platform Foundation (PF). Integration uses Pattern 2: Event-Based from the constitution.
Encounter Entity
Problem
No spec in CL or PM defines a formal encounter entity. The encounter is the central concept linking appointments, clinical notes, charges, and claims but currently exists only as scattered references:
pm_charges.encounter_id UUID (nullable, no FK)
- CL-04 progress notes function as encounter records
- PM-03 appointments transition to encounters implicitly
Solution: pm_encounters Table
The encounter entity lives in PM (owned by PM-03) because encounter lifecycle tracks the business/operational flow. CL adds clinical content to encounters via events.
CREATE TABLE pm_encounters (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES pf_organizations(id),
patient_id UUID NOT NULL,
appointment_id UUID REFERENCES pm_appointments(id),
encounter_date DATE NOT NULL,
encounter_type TEXT NOT NULL CHECK (encounter_type IN (
'outpatient', 'residential_daily', 'iop', 'php',
'crisis', 'group', 'telehealth', 'evaluation'
)),
status TEXT NOT NULL DEFAULT 'scheduled' CHECK (status IN (
'scheduled', 'checked_in', 'in_progress', 'documented',
'charge_captured', 'billed', 'cancelled', 'no_show'
)),
rendering_provider_id UUID NOT NULL,
supervising_provider_id UUID,
service_location_id UUID,
place_of_service TEXT NOT NULL DEFAULT '11',
-- Populated by CL-04 via event
clinical_note_id UUID,
note_finalized_at TIMESTAMPTZ,
-- Populated by PM-07 via charge capture
charge_id UUID,
charge_captured_at TIMESTAMPTZ,
-- Populated by PM-08 via claim submission
claim_id UUID,
-- Standard columns
custom_fields JSONB DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
created_by UUID REFERENCES pf_profiles(id),
updated_by UUID REFERENCES pf_profiles(id),
deleted_at TIMESTAMPTZ
);
-- Indexes
CREATE INDEX idx_pm_encounters_org ON pm_encounters(organization_id);
CREATE INDEX idx_pm_encounters_patient ON pm_encounters(organization_id, patient_id);
CREATE INDEX idx_pm_encounters_date ON pm_encounters(organization_id, encounter_date);
CREATE INDEX idx_pm_encounters_status ON pm_encounters(organization_id, status)
WHERE deleted_at IS NULL;
-- RLS
ALTER TABLE pm_encounters ENABLE ROW LEVEL SECURITY;
ALTER TABLE pm_encounters FORCE ROW LEVEL SECURITY;
-- Uses pf_has_org_access SECURITY DEFINER helper (never query RLS-protected tables in policies)
CREATE POLICY pm_encounters_select ON pm_encounters
FOR SELECT USING (pf_has_org_access(organization_id, auth.uid()));
CREATE POLICY pm_encounters_insert ON pm_encounters
FOR INSERT WITH CHECK (pf_has_org_access(organization_id, auth.uid()));
CREATE POLICY pm_encounters_update ON pm_encounters
FOR UPDATE
USING (pf_has_org_access(organization_id, auth.uid()))
WITH CHECK (pf_has_org_access(organization_id, auth.uid()));
CREATE POLICY pm_encounters_delete ON pm_encounters
FOR DELETE USING (pf_is_org_admin(auth.uid(), organization_id));
Encounter Lifecycle
PM-03 Appointment created → encounter status = 'scheduled'
Patient checks in → encounter status = 'checked_in'
Clinician starts session → encounter status = 'in_progress'
CL-04 note finalized → encounter status = 'documented'
(via clinical_note_finalized event)
PM-07 charge captured → encounter status = 'charge_captured'
PM-08 claim submitted → encounter status = 'billed'
Event Contract: clinical_note_finalized
Event: clinical_note_finalized
Channel: cl_pm_events
Publisher: CL (Clinical & EHR) — CL-04 Progress Notes
Subscribers: PM (Practice Management) — PM-07 Charge Capture
Status: 📝 Planned
Trigger
When a progress note transitions to signed or cosigned status in cl_progress_notes.
Payload Schema
interface ClinicalNoteFinalized {
event: 'clinical_note_finalized';
publisher: 'CL';
subscriber: ['PM'];
payload: {
note_id: uuid;
encounter_id: uuid; // Links to pm_encounters
patient_id: uuid;
provider_id: uuid; // Rendering provider
supervising_provider_id?: uuid;
service_date: string; // ISO date
begin_time: string; // HH:MM:SS
end_time: string; // HH:MM:SS
duration_minutes: number;
note_type: string; // 'individual' | 'group' | 'crisis' | 'family' etc.
service_line: string; // 'outpatient' | 'residential' | 'iop' | 'php' etc.
cpt_code?: string; // Suggested CPT from note type/duration
diagnosis_codes: string[]; // ICD-10-CM codes
place_of_service: string; // POS code
is_telehealth: boolean;
telehealth_modality?: string; // 'audio_video' | 'audio_only'
is_group: boolean;
group_participant_count?: number;
treatment_goal_ids: uuid[]; // Linked treatment goals
modifiers_suggested: string[]; // Suggested modifiers (HQ, GT, 95, etc.)
};
metadata: {
organization_id: uuid;
site_id?: uuid;
user_id: uuid;
timestamp: string; // ISO timestamp
correlation_id: uuid;
idempotency_key: uuid; // Prevents duplicate charge creation
};
}
Implementation
-- Trigger on CL-04 note finalization
CREATE OR REPLACE FUNCTION cl_publish_note_finalized()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.status IN ('signed', 'cosigned') AND OLD.status NOT IN ('signed', 'cosigned') THEN
PERFORM pg_notify('cl_pm_events', json_build_object(
'event', 'clinical_note_finalized',
'publisher', 'CL',
'payload', json_build_object(
'note_id', NEW.id,
'encounter_id', NEW.encounter_id,
'patient_id', (SELECT patient_id FROM cl_patient_charts WHERE id = NEW.chart_id),
'provider_id', NEW.clinician_id,
'service_date', NEW.service_date,
'begin_time', NEW.begin_time,
'end_time', NEW.end_time,
'duration_minutes', NEW.duration_minutes,
'diagnosis_codes', ARRAY[NEW.diagnosis_code],
'is_telehealth', COALESCE(NEW.is_telehealth, false),
'is_group', (NEW.note_type = 'group')
),
'metadata', json_build_object(
'organization_id', NEW.organization_id,
'user_id', NEW.clinician_id,
'timestamp', now(),
'correlation_id', gen_random_uuid(),
'idempotency_key', NEW.id
)
)::text);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER trigger_note_finalized
AFTER UPDATE ON cl_progress_notes
FOR EACH ROW
EXECUTE FUNCTION cl_publish_note_finalized();
Consumer: PM-07 Charge Capture
Edge Function: supabase/functions/cl-note-charge-capture/index.ts
Consumer responsibilities:
- Receive
clinical_note_finalized event
- Check idempotency_key — skip if charge already exists for this note
- Look up fee schedule for provider’s payer/plan
- Calculate AHCCCS timed units from
duration_minutes
- Apply auto-modifiers (HQ for group, GT/95 for telehealth, etc.)
- Create
pm_charges record with status pending
- Update
pm_encounters.charge_captured_at and status
- Log to
pf_audit_logs
Error Handling
| Scenario | Action |
|---|
| Fee schedule not found | Create charge with needs_review flag; alert billing team |
| Duplicate event (same idempotency_key) | Skip silently, return success |
| Invalid CPT/diagnosis combination | Create charge with needs_review flag |
| Consumer failure | Retry 3x with exponential backoff; dead-letter after exhaustion |
| Encounter not found | Create encounter from event payload (backfill) |
Event Contract: charge_status_changed
Event: charge_status_changed
Channel: pm_events
Publisher: PM — PM-07 Charge Capture
Subscribers: CL (for status visibility on chart)
Status: 📝 Planned
Payload Schema
interface ChargeStatusChanged {
event: 'charge_status_changed';
publisher: 'PM';
subscriber: ['CL'];
payload: {
charge_id: uuid;
encounter_id: uuid;
patient_id: uuid;
old_status: string;
new_status: string; // 'pending' | 'reviewed' | 'approved' | 'billed' | 'void'
cpt_code: string;
total_charge: number;
};
metadata: {
organization_id: uuid;
user_id: uuid;
timestamp: string;
correlation_id: uuid;
};
}
Cross-Reference
| Spec | Role | Integration Point |
|---|
| PM-03 | Creates encounter when appointment transitions | pm_encounters table owner |
| CL-04 | Publishes clinical_note_finalized on note sign-off | Event publisher |
| PM-07 | Consumes event, creates charges | Event consumer |
| PM-08 | Creates claims from approved charges | Downstream consumer |
| PM-09 | Posts payments against claims | Further downstream |