Skip to main content
Version: 2.3.0
Last Updated: 2026-05-28
Constitution Reference: Section 1.3 (Integration Patterns - Pattern 3)
API Contracts provide synchronous request-response interactions between cores. These are REST APIs for direct data queries between cores when event-driven patterns are not suitable.

API Contract Standards

All API contracts MUST follow:
  • Versioning: /api/v1/{core}/{resource}
  • Authentication: JWT auth with RLS enforcement
  • Rate Limiting: Per-organization limits
  • Error Handling: Standard HTTP status codes
  • Response Format: JSON with consistent structure

Machine / organization API access (PF-97)

Interactive users authenticate with Supabase Auth JWTs; callers validate organization_id against session membership (pf_user_role_assignments / pf_has_org_access) as documented per endpoint. Programmatic access (organization API keys, service accounts, future OAuth client credentials) is specified in PF-97: Multi-Tenant Organization API Access. Machine clients use a separate credential lifecycle (issue, rotate, revoke, scope) and MUST NOT rely on interactive user RBAC alone. Integration contracts and error shapes for machine clients will live in PF-97 integration once implementation is available. Related: PF-30-EN-01 machine principals & scopes, PF-89-EN-01 machine client versioning.

Status Legend

  • Implemented - Fully implemented and tested
  • 📝 Planned - Specified but not yet implemented
  • 🟡 In Progress - Partially implemented

Planned API Contracts

CL-68: Residential Episode Context (planned)

Provider: CL (CL-68) via @/platform/clinical platform layer (synchronous; SECURITY DEFINER RPC) Consumers: PM-74 (authorization context), GR-27 (compliance counters) Status: 📝 Planned Spec Reference: CL-68 BHRF Clinical Residential Program; full contract CL-GR-PM-BHRF-EPISODE-LIFECYCLE Purpose: Lets PM/GR read BHRF clinical-residential episode context without querying CL tables (no cross-core import). Returns coded context only — no clinical narrative. 42 CFR Part 2: locValue is redacted to null (not omitted) when CL-11 SUD consent is absent/revoked, and each access is audit-logged per §2.31; the function returns null only when the episode is inaccessible/not found. Consumers MUST treat locValue === null as “LOC not disclosable” (no fallback guessing). CL is architecturally downstream — consumers depend on this platform layer, not on CL-68.

Auth & Error Model

  • Authentication: caller must present a valid bearer token (401 if unauthenticated).
  • Tenant isolation: caller must be org-authorized for organizationId (pf_has_org_access or equivalent) validated before any lookup. Parameter scoping alone is not a boundary. Required permission scope: cl.bhrf.view (residential episode context read).
  • Error → return mapping (the two cross-tenant cases are distinct and unambiguous):
    • 401 — unauthenticated request.
    • 403 — authenticated but not org-authorized for the supplied organizationId (cross-tenant/mismatched token, or missing cl.bhrf.view). Decided before any lookup.
    • top-level null (404 semantics) — caller is org-authorized for organizationId, but the episodeId does not exist within that authorized tenant scope (existence is not revealed). Never used for an authorization failure.
    • locValue === null — record exists but LOC redacted by 42 CFR Part 2 (CL-11 consent absent/revoked); the context object is still returned.
    • Operational failures return standardized platform errors — do not overload top-level null.
  • Audit: every access attempt is logged per §2.31, including whether locValue was redacted and any denied-access (401/403) outcome.

Synchronous API Contract

TypeScript Interface:

GR-20: External LMS Bridge (planned)

Provider: GR-20 (External LMS Bridge) Consumers: GR-02 (training assignments), HR-22 / HR-27 (completion evidence) when implemented Status: 📝 Planned Spec Reference: GR-20 External LMS Bridge Purpose: Outbound REST/sync and optional inbound webhooks to external LMS vendors (SCORM/xAPI adapters). Contract payloads and auth live in the spec and future GR-20-*-INTEGRATION.md; this row satisfies integration-contract coverage for GR-20 until the integration doc ships.

Synchronous API Contract

Endpoints:
  • Pull Completions: GET /lms/completions?org_id={org_id}&since={date}
  • Push Enrollment: POST /lms/enrollments
TypeScript Interface:
Error Handling:
  • pushEnrollment failures: Event not acknowledged on failure; retried on next cron run
  • After 5 consecutive failures for the same enrollment, gr_lms_sync_runs.status marked 'failed' with requires_attention flag
  • Unmapped vendor users/courses: logged to gr_lms_sync_runs.error_summary, records_failed incremented
Supported Vendors:
  • Relias
  • HealthStream
  • Cornerstone
  • ADP Learning
  • Workday Learning
Rate Limiting:
  • Pull operations: ≤10k completions per run, chunked per gr_module_settings.lms.max_pull_batch_size
  • Target: <5 minutes per pull run (p95)
Required Schemas:
  • gr_lms_integrations: vendor configuration + vault secret reference
  • gr_lms_user_mappings: vendor user ID ↔ hr_employees.id
  • gr_lms_course_mappings: vendor course ID ↔ gr_training_courses.id
  • gr_lms_sync_runs: audit trail of sync operations
Error Codes:
  • UNMAPPED_USER: No mapping exists for external_user_id
  • UNMAPPED_COURSE: No mapping exists for external_course_id
  • VENDOR_AUTH_FAILED: Invalid credentials
  • VENDOR_RATE_LIMIT: Vendor rate limit exceeded
  • SYNC_RETRY_EXHAUSTED: 5+ consecutive failures

PF-43: Resource Quota Check (Database)

Provider: PF-43 (Tenant Resource Quotas)
Consumers: Edge functions, server-side callers, client via useResourceQuota hook
Status: 📝 Planned
Integration Doc: PF-43-tenant-resource-quotas-INTEGRATION.md
Purpose: Server-side quota check before operations; client hook useResourceQuota calls backend/edge that uses this function. Database function: pf_check_resource_quota(organization_id, resource_type, requested_amount)
Returns: (allowed, remaining, reset_at, quota_id, limit_type)reset_at is timestamptz (quota window reset); callers use it for countdown/display.
Request (logical):
  • organization_id (UUID): Tenant context
  • resource_type (TEXT): One of storage, api_calls, users, custom_objects, workflow_executions
  • requested_amount (NUMERIC, optional): Amount to check; default 1
Response: Allowed boolean, remaining quota, reset_at (quota window reset), quota_id, limit_type (soft/hard). Writes to pf_resource_usage and pf_quota_violations via SECURITY DEFINER. Client hook: useResourceQuota({ organizationId, resourceType, requestedAmount? }) — see spec API Design and integration doc.

PF-91: Compliance Edge Functions (internal)

Provider: PF-91 (Compliance Automation & Regulatory Dashboard)
Consumers: Supabase cron, authenticated org admins (evidence only)
Status: 📝 Planned
Integration Doc: PF-91-compliance-automation-regulatory-dashboard-INTEGRATION.md
Purpose: Internal Edge Function invocations (not public REST versioning). Contract shapes belong in the integration doc and supabase/functions/*/README.md; this row tracks ownership. Storage: Evidence ZIPs and sidecar files use private bucket compliance-evidence with org-first path segment; signed URL download (see PF-91 integration doc). Note: Final JSON Schemas — see PENDING_CONTRACTS.md until PF-91 implementation freezes payloads.

PF-96: Jurisdiction Profile Resolution RPC

Provider: PF-96 (Medicaid State Compliance Configuration) Consumers: All CL/PM features, PF-91 compliance dashboard, edge functions Status: 📝 Planned Integration Doc: PF-96-medicaid-state-compliance-configuration-INTEGRATION.md Spec Reference: PF-96 spec Purpose: Server-side jurisdiction profile resolution with four-tier merge (Federal Baseline → State Profile → Org Overrides → Site Overrides). Used by frontend hook useJurisdictionProfile() and edge function helper getJurisdictionProfile(). Database RPC: pf_resolve_jurisdiction_profile(p_org_id UUID, p_site_id UUID DEFAULT NULL) Type: PostgreSQL RPC (SECURITY DEFINER) Resolution Order: Federal baseline → State profile (from pf_org_jurisdiction_config) → Org overrides → Site overrides (if p_site_id provided) Parameters:
  • p_org_id (UUID, required): Organization ID
  • p_site_id (UUID, optional): Site ID for site-level profile override
Returns: JSONB with the merged profile:
Frontend Hook:
Edge Function Helper:
Merge Behavior:
  • Per-rule-pack JSONB merge (clinical, billing, compliance packs merged separately)
  • Array fields replaced entirely (not appended) on override
  • Federal baseline fields cannot be weakened (validated via pf_validate_jurisdiction_override() trigger)
Performance: Profile resolution < 50ms p95; results cached client-side (5min staleTime, 10min gcTime) Tenant Authorization: Caller must be a member of p_org_id (verified via pf_has_org_access()); if p_site_id is provided, the site must belong to the organization. Unauthorized calls return 403. See also:

PF-101: Google Workspace Integration Edge Functions

Provider: PF-101 (Google Workspace Integration) Consumers: HR-01, CE-07, CE-21, PF-10, PF-11, PF-35, PF-86 Status: 🚧 Work in progress / partial delivery Integration Doc: PF-101-google-workspace-integration-INTEGRATION.md Spec Reference: PF-101 spec Purpose: Edge function contracts for Google Workspace Admin SDK Directory, Gmail, Calendar/Meet, Drive, Chat, Licensing, and Reports APIs. All functions use PF-76 credential retrieval, PF-42 rate limiting where applicable, sanitized errors, and correlation IDs.

google-workspace-test-connection

Type: Edge Function (HTTP) Auth: JWT + pf.google_workspace.test permission Purpose: Validate capability scopes and health Request:
Response:
Error Codes:
  • MISSING_CONNECTION: Connection not found
  • INVALID_CREDENTIALS: Credentials invalid or expired
  • INSUFFICIENT_SCOPES: Required scopes not granted
  • BAA_NOT_ATTESTED: BAA attestation required for PHI-capable operations

google-workspace-directory-sync

Type: Edge Function (Service Role / Scheduled) Auth: Service role (scheduled worker) Purpose: Batch Directory user/group/org unit sync Tenant-Scoping: REQUIRED - All queries MUST include .eq('organization_id', organizationId) and .eq('connection_id', connectionId) where applicable Request:
Response:
Validation: Function must assert presence of tenant-scoping filters in all multi-tenant table queries.

google-workspace-provision-user

Type: Edge Function (Event Consumer + Service Role) Auth: Service role (event consumer) Purpose: Provision/link one Workspace user Tenant-Scoping: REQUIRED - All queries MUST include .eq('organization_id', organizationId) and .eq('connection_id', connectionId) where applicable Request:
Response:
Validation: Function must assert presence of tenant-scoping filters in all multi-tenant table queries.

google-workspace-offboard-user

Type: Edge Function (Event Consumer + Service Role) Auth: Service role (event consumer) Purpose: Suspend user, revoke licenses, remove groups, queue Drive review Tenant-Scoping: REQUIRED - All queries MUST include .eq('organization_id', organizationId) and .eq('connection_id', connectionId) where applicable Request:
Response:
Validation: Function must assert presence of tenant-scoping filters in all multi-tenant table queries.

google-workspace-gmail-send

Type: Edge Function (HTTP) Auth: JWT + sender policy Purpose: Shared Gmail sender path with sender allowlist, BAA gate, PF-86 signature, and redacted audit Request:
Response:
Error Codes:
  • SENDER_NOT_ALLOWED: Sender not in allowlist
  • BAA_NOT_ATTESTED: BAA required for Gmail operations
  • QUOTA_EXCEEDED: Gmail send quota exceeded

google-workspace-calendar-sync

Type: Edge Function (JWT/Service Role) Auth: JWT or service role Purpose: Calendar/Meet event sync and CE wrapper support Tenant-Scoping: REQUIRED - All queries MUST include .eq('organization_id', organizationId) and .eq('connection_id', connectionId) where applicable Request:
Response:
Validation: Function must assert presence of tenant-scoping filters in all multi-tenant table queries.

google-workspace-drive-sync

Type: Edge Function (JWT/Service Role) Auth: JWT or service role Purpose: Drive metadata and mapping sync Tenant-Scoping: REQUIRED - All queries MUST include .eq('organization_id', organizationId) and .eq('connection_id', connectionId) where applicable Request:
Response:
Validation: Function must assert presence of tenant-scoping filters in all multi-tenant table queries.

google-workspace-reports-ingest

Type: Edge Function (Service Role / Scheduled) Auth: Service role (scheduled worker) Purpose: Reports API audit ingestion Tenant-Scoping: REQUIRED - All queries MUST include .eq('organization_id', organizationId) and .eq('connection_id', connectionId) where applicable Request:
Response:
Validation: Function must assert presence of tenant-scoping filters in all multi-tenant table queries.

google-workspace-chat-notify

Type: Edge Function (Service Role / PF-10) Auth: Service role (PF-10 consumer) Purpose: Send approved notifications to Chat spaces Tenant-Scoping: REQUIRED - All queries MUST include .eq('organization_id', organizationId) and .eq('connection_id', connectionId) where applicable Request:
Response:
Validation: Function must assert presence of tenant-scoping filters in all multi-tenant table queries.
Security & Compliance:
  • No PHI-capable operations unless pf_google_workspace_connections.baa_attested_at is set
  • All credentials stored in PF-76 only
  • Domain-wide delegation scopes versioned and reviewed
  • No PHI/PII in logs, cache keys, or telemetry
See also:

PM-47: Managed Care Auth Tracking

Provider: PM-47 (Managed Care Authorization Tracking) Consumers: PM-08 (claims submission), PM dashboard, edge functions Status: 📝 Planned Integration Doc: PM-47-managed-care-authorization-tracking-INTEGRATION.md Spec Reference: PM-47 spec Purpose: Edge functions for auth expiration monitoring and pre-submission auth-to-claim validation.

Edge Function: pm-auth-expiration-alerts

Type: Scheduled (Cron) Method: Invoked by pg_cron daily at 6:00 AM Auth: Service role (cron invocation) Schedule: 0 6 * * * Purpose: Check for authorizations reaching configured alert thresholds (14/7/3/1 days before expiration) and send PF-10 notifications to assigned UR coordinators. Request: None (cron-triggered, no request body) Response:
Processing:
  1. Query pm_managed_care_authorizations where expiration_date - CURRENT_DATE matches alert intervals from pm_module_settings.auth_alert_intervals_days
  2. For each matching authorization, send notification via PF-10 with patient, payer, service type, expiration date, and remaining units
  3. Track sent alerts to avoid duplicates (idempotent)
Performance: Process up to 10,000 active authorizations per organization in < 30s (NFR-1)

Edge Function: pm-auth-claim-validation

Type: HTTP POST Method: POST Auth: JWT (user context) Permission Required: pm.managed_care_auth.validate Purpose: Validate claim lines against active authorizations before claim submission. Called by PM-08 claim submission workflow. Request:
Response:
Validation Logic:
  1. For each claim line, query pm_managed_care_authorizations where:
    • organization_id matches
    • status IN ('active', 'expiring')
    • service_type matches claim line service type
    • effective_date <= date_of_service <= expiration_date
  2. Check that used_units + claim_line_units <= authorized_units
  3. Return structured validation result per line
Performance: Complete validation in < 500ms for batch of up to 50 claim lines (NFR-1) Idempotency: Safe to re-run; no state changes during validation Error Response Schema:
Status Code Mappings:
  • 400 Bad RequestINVALID_REQUEST - Invalid input (missing fields, malformed data)
  • 403 ForbiddenACCESS_DENIED - Auth/permission failure (missing pm.managed_care_auth.validate permission or organization_id mismatch)
  • 404 Not FoundCLAIM_NOT_FOUND - Missing claim_id in request
  • 500 Internal Server ErrorSERVER_ERROR - Server-side processing error
Tenant Validation Requirements:
  • Organization ID Validation: The incoming organization_id in the request body MUST be validated against the authenticated JWT tenant context. Reject requests where organization_id does not match the JWT org claim.
  • Permission Check: Caller MUST have pm.managed_care_auth.validate permission for the organization. Return 403 ACCESS_DENIED if permission check fails.
  • Application-Layer Filtering: Implementations MUST apply organization_id filtering on all mutations (UPDATE/DELETE operations) to prevent cross-tenant access.
  • Database Layer: RLS policies and tenant-isolation MUST be enabled at the DB layer as defense-in-depth to prevent cross-tenant data access even if application-layer filtering fails.

CL-02-EN-60: Intake Element Completeness Checker RPC

Provider: CL (Clinical Core) Consumers: Internal use (triggers, application-layer completeness checks) Status: ✅ Complete Integration Doc: CL-02-EN-60-jurisdiction-aware-assessment-requirements-INTEGRATION.md Spec Reference: CL-02-EN-60 spec Purpose: Generic intake assessment element completeness checker that accepts a dynamic list of required elements from jurisdiction profiles, replacing Arizona-specific logic. Database RPC: cl_check_intake_elements(p_assessment_id UUID, p_required_elements JSONB) Type: PostgreSQL RPC (SECURITY DEFINER) Call Pattern: Synchronous Parameters:
  • p_assessment_id (UUID, required): Intake assessment ID
  • p_required_elements (JSONB, required): JSON array of required element codes (e.g., '["presenting_problem","medical_history","sdoh_screening_completed"]')
Returns: JSONB with per-element boolean completion status:
Element Code Mapping: Each element code maps to a specific column or condition in cl_intake_assessments:
  • chief_complaint, presenting_problemchief_complaint IS NOT NULL AND <> ''
  • history_present_illness, history_of_present_illnesshistory_of_present_illness IS NOT NULL AND <> ''
  • medical_historymedical_history IS NOT NULL AND <> ''
  • mental_health_history, psychiatric_historymental_health_history IS NOT NULL AND <> ''
  • substance_use_historysubstance_use_history IS NOT NULL AND <> ''
  • social_historysocial_history IS NOT NULL AND <> ''
  • preliminary_diagnoses, diagnostic_formulationpreliminary_diagnoses IS NOT NULL AND jsonb_array_length(preliminary_diagnoses) > 0
  • sdoh_screening_completedsdoh_screening_id IS NOT NULL
Error Behavior:
  • Returns NULL if assessment not found
  • Returns empty object {} if p_required_elements is empty array
  • Unknown element codes return false in result object
Versioning: Internal RPC; no external API versioning. Schema changes require migration coordination with consuming features. Related RPCs:
  • cl_check_ahcccs_18_elements(p_assessment_id UUID) — Legacy wrapper (DEPRECATED); calls cl_check_intake_elements with Arizona 18-element defaults
Trigger Usage:
  • trg_cl_intake_element_complete on cl_intake_assessments calls this function to maintain intake_element_complete column
See also:

FA-01: Episode Balance Query

Endpoint: /api/v1/fa/episode-balance Method: GET Provider: FA (Finance & Accounting) Consumer: RH (Recovery Housing) — episode detail page and bed board payment status Status: 🟡 Partial — Edge function fa-episode-balance + hook useFaEpisodeBalance; FA AR ledger join 📋 Planned Spec Reference: Purpose: Query payment status for an episode to display in the RH episode detail view and (optionally) the bed board. Enables RH to surface billing information without direct database access to FA tables. Request:
Query Parameters:
  • episode_id (required): UUID of episode
  • as_of_date (optional): Date to calculate balance as of (ISO 8601 format, defaults to current date)
Response Schema (200 OK):
Error Response Schema:
Error Codes:
  • 400 Bad Request: Invalid query parameters (e.g., malformed UUID, invalid date format)
    • Error code: INVALID_DATE or INVALID_EPISODE_ID
  • 401 Unauthorized: Missing or invalid JWT token
    • Error code: UNAUTHORIZED
  • 403 Forbidden: User does not have access to episode (RLS policy violation)
    • Error code: ACCESS_DENIED
  • 404 Not Found: Episode not found in FA system or episode belongs to different organization
    • Error code: EPISODE_NOT_FOUND
  • 429 Too Many Requests: Rate limit exceeded
    • Error code: RATE_LIMIT_EXCEEDED
    • Response includes X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset headers
  • 500 Internal Server Error: Internal FA error
    • Error code: SERVER_ERROR
Authentication:
  • JWT required in Authorization: Bearer {token} header
  • Token validated against Supabase Auth
  • RLS policies enforce organization isolation
  • User must have access to the episode’s organization
Rate Limiting:
  • 100 requests/minute per organization
  • Rate limit headers included in all responses:
    • X-RateLimit-Limit: 100
    • X-RateLimit-Remaining: Remaining requests in current window
    • X-RateLimit-Reset: Unix timestamp when limit resets
Implementation Notes:
  • Balance calculation includes all transactions up to as_of_date
  • Past due balance calculated as transactions >30 days overdue
  • Payment status determined by balance and payment history
  • All amounts in organization’s base currency
  • Response cached for 5 minutes (same episode_id + as_of_date)
Version: v1.0.1
Last Updated: 2026-03-20

FA-10: Tax Reporting APIs

Status: 📝 Planned
Spec Reference: FA-10 (Tax Reporting & Compliance)
Last Verified: N/A – planned

API 1: Generate 1099 Forms

Endpoint: /api/v1/fa/tax/1099/generate
Method: POST
Provider: FA (Finance & Accounting)
Consumer: Internal (FA module)
Status: 📝 Planned
Tenant Context Enforcement:
  • The organization_id from the request body MUST be validated against the authenticated session’s organization membership using pf_has_org_access(organization_id, auth.uid())
  • If the request organization_id does not match any organization the caller has access to, return 403 ACCESS_DENIED
  • Do NOT trust organization_id from the request body alone; always derive from authenticated session or validate via helper function
PII/PHI Logging Guidelines:
  • NEVER log: Vendor names, vendor emails, tax identification numbers (EIN/SSN), bank account fragments, payment amounts in error logs
  • ALLOWED in logs: UUIDs (form_id, vendor_id, tax_year_id), error codes, operation timestamps, organization_id
  • Restrict error.details to non-sensitive identifiers only (UUIDs, codes, timestamps)
Request Schema:
Response Schema (200 OK):
Usage Example (Write Endpoint):

FA-11: Fixed Assets APIs

Status: 📝 Planned
Spec Reference: FA-11 (Fixed Assets & Depreciation)
Last Verified: N/A – planned

API 1: Asset Depreciation

Endpoint: /api/v1/fa/assets/{asset_id}/depreciation
Method: GET
Provider: FA (Finance & Accounting)
Consumer: FA-07 (Financial Reporting)
Status: 📝 Planned (Implementation: client-side — Supabase queries from FA module)
Tenant Context Enforcement:
  • The asset’s organization_id MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED if the asset belongs to an organization the caller cannot access
PII/PHI Logging Guidelines:
  • NEVER log: Asset serial numbers, purchase prices, vendor details, location addresses in error logs
  • ALLOWED in logs: UUIDs (asset_id, journal_entry_id), error codes, timestamps, organization_id
Response Schema (200 OK):

API 2: Process Monthly Depreciation

Endpoint: /api/v1/fa/assets/depreciation/process
Method: POST
Provider: FA (Finance & Accounting)
Consumer: Internal (FA module)
Status: 📝 Planned (Implementation: client-side — Supabase mutations from FA module)
Tenant Context Enforcement:
  • The organization_id from the request body MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED if the organization is not accessible
PII/PHI Logging Guidelines:
  • NEVER log: Asset serial numbers, purchase prices, vendor details, location addresses in error logs
  • ALLOWED in logs: UUIDs (organization_id, asset_id), error codes, timestamps, counts
Request Schema:
Response Schema (200 OK):
Usage Example (Write Endpoint):

FA-12: Expense Management APIs

Status: 📝 Planned
Spec Reference: FA-12 (Expense Management & Reimbursements)
Last Verified: N/A – planned

API 1: Approve Expense Report

Endpoint: /api/v1/fa/expenses/reports/{report_id}/approve
Method: POST
Provider: FA (Finance & Accounting)
Consumer: FW-03 (Approval Workflow)
Status: 📝 Planned (Implementation: client-side — Supabase + workflow from FA/FW modules)
Tenant Context Enforcement:
  • The expense report’s organization_id MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED if the report belongs to an organization the caller cannot access
PII/PHI Logging Guidelines:
  • NEVER log: Employee names, receipt images, credit card numbers, bank account details, expense descriptions containing PHI
  • ALLOWED in logs: UUIDs (report_id, employee_id), error codes, timestamps, organization_id, status codes
Request Schema:
Response Schema (200 OK):
Usage Example (Write Endpoint):

FA-13: Project Accounting APIs

Status: 📝 Planned
Spec Reference: FA-13 (Project Accounting & Grant Tracking)
Last Verified: N/A – planned

API 1: Project Budget vs Actual

Endpoint: /api/v1/fa/projects/{project_id}/budget-vs-actual
Method: GET
Provider: FA (Finance & Accounting)
Consumer: FA-07 (Financial Reporting), FA-08 (Budgeting)
Status: 📝 Planned (Implementation: client-side — Supabase/RPC from FA module)
Tenant Context Enforcement:
  • The project’s organization_id MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED if the project belongs to an organization the caller cannot access
PII/PHI Logging Guidelines:
  • NEVER log: Grant recipient names, client identifiers, project descriptions containing PHI
  • ALLOWED in logs: UUIDs (project_id, account_id), error codes, timestamps, organization_id
Query Parameters:
  • period_start (optional): Start date for period
  • period_end (optional): End date for period
Response Schema (200 OK):

FA-14: Cash Management APIs

Status: 📝 Planned
Spec Reference: FA-14 (Cash Management & Treasury)
Last Verified: N/A – planned

API 1: Cash Position

Endpoint: /api/v1/fa/cash/position
Method: GET
Provider: FA (Finance & Accounting)
Consumer: FA-07 (Financial Reporting), FA-16 (Analytics)
Status: 📝 Planned (Implementation: client-side — Supabase/hooks from FA module)
Tenant Context Enforcement:
  • The organization_id MUST be derived from the authenticated session (not from query parameters)
  • If a request includes organization_id in query params, validate it matches the caller’s accessible organizations via pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED for mismatches
PII/PHI Logging Guidelines:
  • NEVER log: Bank account numbers, routing numbers, account holder names, transaction details
  • ALLOWED in logs: UUIDs (organization_id), error codes, timestamps, aggregated amounts (without account-level detail)
Query Parameters:
  • as_of_date (optional): Date for cash position (defaults to current date)
Response Schema (200 OK):
Usage Example (Read Endpoint):

API 2: Update Cash Position

Endpoint: /api/v1/fa/cash/position
Method: PUT
Provider: FA (Finance & Accounting)
Consumer: Internal (FA module), FA-06 (Bank Reconciliation)
Status: 📝 Planned (Implementation: client-side — Supabase mutations from FA module)
Tenant Context Enforcement:
  • The organization_id from the request body MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED if the organization is not accessible
PII/PHI Logging Guidelines:
  • NEVER log: Bank account numbers, routing numbers, account holder names, transaction details
  • ALLOWED in logs: UUIDs (organization_id, position_id), error codes, timestamps, aggregated amounts
Request Schema:
Response Schema (200 OK):
Usage Example (Write Endpoint):

API 3: Create Investment

Endpoint: /api/v1/fa/investments
Method: POST
Provider: FA (Finance & Accounting)
Consumer: Internal (FA module)
Status: 📝 Planned
Tenant Context Enforcement:
  • The organization_id from the request body MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED if the organization is not accessible
PII/PHI Logging Guidelines:
  • NEVER log: Investment account numbers, bank details, account holder names
  • ALLOWED in logs: UUIDs (organization_id, investment_id), error codes, timestamps
Request Schema:
Response Schema (201 Created):
Usage Example (Write Endpoint):

FA-15: Cost Allocation APIs

Status: 📝 Planned
Spec Reference: FA-15 (Cost Allocation & Indirect Cost Rates)
Last Verified: N/A – planned

API 1: Allocate Indirect Costs

Endpoint: /api/v1/fa/idc/rates/{rate_id}/allocate
Method: POST
Provider: FA (Finance & Accounting)
Consumer: FA-13 (Project Accounting)
Status: 📝 Planned
Tenant Context Enforcement:
  • The rate’s organization_id and all project organization_id values MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED if any referenced entity belongs to an inaccessible organization
PII/PHI Logging Guidelines:
  • NEVER log: Project names containing client identifiers, grant recipient details
  • ALLOWED in logs: UUIDs (rate_id, project_id, journal_entry_id), error codes, timestamps, organization_id
Request Schema:
Response Schema (200 OK):

FA-16: Financial Analytics APIs

Status: 📝 Planned
Spec Reference: FA-16 (Financial Analytics & Dashboards)
Last Verified: N/A – planned

API 1: Financial Dashboard

Endpoint: /api/v1/fa/analytics/dashboard
Method: GET
Provider: FA (Finance & Accounting)
Consumer: Internal (FA module)
Status: 📝 Planned
Tenant Context Enforcement:
  • The organization_id MUST be derived from the authenticated session (not from query parameters)
  • If a request includes organization_id in query params, validate it matches the caller’s accessible organizations via pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED for mismatches
PII/PHI Logging Guidelines:
  • NEVER log: Individual transaction details, vendor names, employee names, account-level breakdowns containing identifiers
  • ALLOWED in logs: UUIDs (organization_id, kpi_id), error codes, timestamps, aggregated metrics only
Query Parameters:
  • period_start (optional): Start date for analytics
  • period_end (optional): End date for analytics
Response Schema (200 OK):

FA-17: Intercompany APIs

Status: 📝 Planned
Spec Reference: FA-17 (Intercompany Transactions)
Last Verified: N/A – planned

API 1: Generate Intercompany Eliminations

Endpoint: /api/v1/fa/intercompany/eliminations/generate
Method: POST
Provider: FA (Finance & Accounting)
Consumer: FA-09 (Consolidation)
Status: 📝 Planned
Tenant Context Enforcement:
  • The consolidation’s organization_id and all related organization IDs MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid())
  • For intercompany transactions, validate access to both source_org_id and target_org_id
  • Return 403 ACCESS_DENIED if the caller lacks access to any involved organization
PII/PHI Logging Guidelines:
  • NEVER log: Organization names, transaction descriptions containing identifiers
  • ALLOWED in logs: UUIDs (consolidation_id, elimination_id, journal_entry_id), error codes, timestamps
Request Schema:
Response Schema (200 OK):

FA-18: Revenue Recognition APIs

Status: 📝 Planned
Spec Reference: FA-18 (Revenue Recognition Advanced)
Integration: FA-18-revenue-recognition-advanced-INTEGRATION.md
Last Verified: 2026-04-26 — contract frozen (implementation pending migrations)

API 1: Recognize Revenue

Endpoint: /api/v1/fa/revenue/recognize
Method: POST
Provider: FA (Finance & Accounting)
Consumer: Internal (FA module)
Status: 📝 Planned
Implementation note: Primary server entry point is RPC fa_process_revenue_recognition(organization_id, recognition_date, fiscal_period_id, user_id) per integration doc. This HTTP route, when implemented, is a thin wrapper that enforces PF-30 fa.revenue_recognitions.process and maps the body to RPC arguments. Tenant Context Enforcement:
  • All revenue schedules’ organization_id values MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid()) (or equivalent FA tenant helper)
  • Return 403 ACCESS_DENIED if any schedule belongs to an inaccessible organization or the caller lacks fa.revenue_recognitions.process
PII/PHI Logging Guidelines:
  • NEVER log: Contract details, client names, revenue source identifiers
  • ALLOWED in logs: UUIDs (schedule_id, recognition_id, journal_entry_id), error codes, timestamps, organization_id
Request Schema:
Response Schema (200 OK):

FA-19: Financial Close APIs

Status: 📝 Planned
Spec Reference: FA-19 (Financial Close Management)
Last Verified: N/A – planned

API 1: Approve Close Period

Endpoint: /api/v1/fa/close/periods/{period_id}/approve
Method: POST
Provider: FA (Finance & Accounting)
Consumer: Internal (FA module)
Status: 📝 Planned
Tenant Context Enforcement:
  • The close period’s organization_id MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED if the period belongs to an inaccessible organization
PII/PHI Logging Guidelines:
  • NEVER log: Approval notes containing sensitive details, financial summaries with identifiers
  • ALLOWED in logs: UUIDs (period_id, approved_by), error codes, timestamps, organization_id, status codes
Request Schema:
Response Schema (200 OK):
Usage Example:
Testing Requirements:
  • Unit tests for balance calculation logic
  • Integration tests for RLS enforcement
  • E2E tests for full request/response cycle
  • Rate limiting tests
  • Error handling tests for all error codes

RH-01: Current Residence/Bed Query (CL Chart — Optional)

Endpoint: /api/v1/rh/current-residence Method: GET Provider: RH (Recovery Housing) Consumer: CL (Clinical & EHR) — patient chart, read-only Status: 📝 Planned (CL-01 chart enhancements — optional) Spec Reference: Purpose: Allows CL to display the patient’s current residence and bed on the patient chart without a direct cross-core dependency on RH tables. Returns null if patient is not currently admitted in any RH facility. Request:
Query Parameters:
  • patient_id (required): UUID of the patient (from pf_patient_identities)
Response Schema (200 OK):
Notes:
  • RLS enforces org-level isolation; patient_id must belong to the caller’s organization.
  • CL must not import directly from @/cores/rh/...; use this Platform Integration Layer endpoint.
Content-Type: application/json (response); no request body (GET). Authentication & Permissions: PII/PHI Logging Guidance (current-residence):
  • Allowed to log: Non-identifying metadata only — request timestamps, response status (e.g. 200, 404, 403, 429), residence_id (redacted or hashed), facility_type.
  • Forbidden: Any patient identifiers (patient_id, pf_patient_identities, names, DOB), exact admission_date beyond date-only if considered PHI, and JWT or token contents.
  • Practice: Redact or use hashed IDs for any IDs in logs; never log raw JWT or patient identifiers.
  • Example log line: GET /api/v1/rh/current-residence 200 residence_id=<redacted> facility_type=recovery_housing ts=2026-02-22T12:00:00Z
Tenant Context Enforcement:
  • RLS policies on rh_episodes, rh_beds, and rh_residences enforce organization_id ownership.
  • patient_id (from pf_patient_identities) must belong to the caller’s organization; requests for cross-org patients return 404 Not Found (not 403) to avoid leaking existence.
Rate Limits:
  • Limit: 60 requests / minute per authenticated user.
  • Response headers: X-RateLimit-Limit: 60, X-RateLimit-Remaining: {n}, X-RateLimit-Reset: {unix_ts}.
  • Exceeded: 429 Too Many Requests with Retry-After header.
Error Response Schema:
TypeScript usage example (GET current-residence with TanStack Query):
Performance SLA:
  • p50: < 100 ms · p95: < 500 ms · p99: < 1 000 ms.
  • Server-side query timeout: 5 000 ms; returns 504 Gateway Timeout if exceeded.
  • This endpoint reads cached census data; no heavy aggregates are executed.
Retry & Idempotency:
  • Safe to retry: GET is idempotent; callers may retry on 429 (after Retry-After) and 503/504.
  • No write semantics; no idempotency key required.
Caching Strategy:
  • Response is cacheable; recommended client Cache-Control: max-age=30, stale-while-revalidate=60.
  • Server sets Cache-Control: public, max-age=30 on 200 responses.
  • Callers should use TanStack Query with staleTime: 30_000 and gcTime: 300_000.

PF-30: Permission Check API

Endpoint: Database function pf_has_permission()
Provider: PF (Platform Foundation)
Consumer: All Cores
Status: 🟡 In Progress (Phase 1 Complete)
Spec Reference: PF-30 Permissions System V2
Implementation Notes (Phase 1 Complete - 2025-12-11):
  • Database schema created: pf_custom_roles, pf_module_permissions, pf_role_permissions, pf_user_role_assignments
  • RLS policies implemented with proper WITH CHECK clauses
  • ~168 module permissions seeded across 7 modules + system
  • Helper function pf_get_user_permissions() available for permission lookup
Purpose: Check if a user has a specific module permission, integrating with PF-26 for three-tier permission checks. Function Signature:
Parameters:
  • p_user_id (required): UUID of the user to check
  • p_organization_id (required): UUID of the organization context
  • p_permission_key (required): Permission key in format {module}.{entity}.{action}
  • p_site_id (optional): UUID of site for site-scoped permission checks
Return Value:
  • TRUE: User has the permission
  • FALSE: User does not have the permission
Permission Check Hierarchy:
Integration with PF-26: When checking access to a specific record, the full permission check hierarchy is:
Usage Example:
Frontend Hook:
Security Notes:
  • Function is SECURITY DEFINER to avoid RLS recursion
  • Always uses SET search_path = public for security
  • Logs permission checks to audit log when enabled
  • Returns FALSE on any error (fail closed)
Performance:
  • p50 < 20ms, p95 < 50ms, p99 < 100ms
  • Optimized with database indexes on role and permission tables
  • Frontend caching reduces database load
Version: v1.0.0 (Phase 1 complete, Phase 2 planned)
Last Updated: 2025-12-11

HR-01: Employee Lookup

Endpoint: /api/v1/hr/employees/{employee_id} Method: GET Provider: HR (Workforce) Consumer: RH (Recovery Housing); optional CL consumer for provider lookup — see PM-17 integration doc. Status: 📝 Planned (RH-06 Implementation, Q2 2026) Spec Reference: RH-06 (Compliance & Staff Operations) Tenant isolation: organization_id enforced by RLS; JWT required. All queries scoped by authenticated session/claims. Purpose: Lookup employee details for staff assignments in Recovery Housing Request:
Path Parameters:
  • employee_id (required): UUID of employee
Response Schema (200 OK):
Error Codes:
  • 404 Not Found: Employee not found
  • 403 Forbidden: User does not have access to employee
  • 500 Internal Server Error: Internal HR error
Authentication: JWT required, RLS enforces organization isolation
Rate Limiting: 100 requests/minute per organization
Version: v1.0.0 (planned)

Endpoint: /api/v1/hr/employees Method: GET Provider: HR (Workforce) Consumer: RH (Recovery Housing); optional CL consumer for provider lookup — see PM-17 integration doc. Status: 📝 Planned (RH-06 Implementation, Q2 2026) Spec Reference: RH-06 (Compliance & Staff Operations) Tenant isolation: organization_id enforced by RLS; JWT required. All queries scoped by authenticated session/claims. Purpose: Search employees for staff assignments in Recovery Housing Request:
Query Parameters:
  • department (optional): Filter by department name
  • status (optional): Filter by employment status (active, inactive, terminated)
  • job_title (optional): Filter by job title
  • page (optional): Page number (default: 1)
  • page_size (optional): Results per page (default: 50, max: 100)
Response Schema (200 OK):
Error Codes:
  • 400 Bad Request: Invalid query parameters
  • 403 Forbidden: User does not have access
  • 500 Internal Server Error: Internal HR error
Authentication: JWT required, RLS enforces organization isolation
Rate Limiting: 100 requests/minute per organization
Version: v1.0.0 (planned)

HR-06 ↔ HR-11: Benefits-Enabled Leave Integration

Integration Type: API Contract (Pattern 3)
Status: 📝 Planned (HR-06 Phase 2)
Version: 1.0
Provider: HR-11 (Benefits Administration)
Consumer: HR-06 Phase 2 (Leave Management)
Purpose: Link leave policies to benefit plans and track benefits-eligible leave. Enables HR-06 to check employee benefit enrollment status and subscribe to enrollment changes. API Endpoints:

Query: Employee Benefit Enrollment Status

Hook: useEmployeeBenefitEnrollment(employeeId, planType)
Location: src/cores/hr/hooks/useEmployeeBenefitEnrollment.ts
Provider: HR-11
Parameters:
  • employeeId (required): UUID of employee
  • planType (required): Benefit plan type - 'std' | 'ltd' | 'disability' | 'health' | 'dental' | 'vision' | 'retirement' | 'life'
Response Schema:
Usage Example:

Query: Leave Policy Benefits

Hook: useLeavePolicyBenefits(leavePolicyId)
Location: src/cores/hr/hooks/useLeavePolicyBenefits.ts
Provider: HR-06 Phase 2
Parameters:
  • leavePolicyId (required): UUID of leave policy
Response Schema:
Data Flow:
  1. HR Admin links leave policy to benefit plan (creates hr_leave_policy_benefits record with FK to hr_benefits_plans)
  2. Employee enrolls in benefit (HR-11 creates hr_benefits_enrollments record)
  3. HR-11 publishes benefits_enrollment_approved event
  4. HR-06 Phase 2 subscribes to event, updates leave eligibility
  5. Employee views leave request form, sees benefits-eligible status via useEmployeeBenefitEnrollment hook
Events:
  • benefits_enrollment_approved - HR-11 → HR-06 (enrollment status change to ‘approved’ or ‘active’)
  • benefits_enrollment_terminated - HR-11 → HR-06 (enrollment termination)
See Also: Version: v1.0.0 (planned)
Last Updated: 2026-01-09

FW-19: Workflow Query Action

Endpoint: Edge function workflow-query-action
Method: POST
Provider: FW (Forms & Workflow)
Consumer: Automation Executor
Status: 📝 Planned (FW-19 Implementation)
Spec Reference: FW-19 Data Query & Integration Actions
Purpose: Execute parameterized queries against whitelisted database tables within workflow execution. Request:
Request Body:
Response Schema (200 OK):
Error Response Schema:
Error Codes:
  • 400 Bad Request: Invalid query parameters or filters
  • 403 Forbidden: Table not in organization’s whitelist
  • 408 Request Timeout: Query exceeded 30s timeout
  • 500 Internal Server Error: Database error
Authentication: JWT required, RLS enforces organization isolation
Rate Limiting: 100 requests/minute per organization
Version: v1.0.0 (planned)

FW-19: Workflow API Action

Endpoint: Edge function workflow-api-action
Method: POST
Provider: FW (Forms & Workflow)
Consumer: Automation Executor
Status: 📝 Planned (FW-19 Implementation)
Spec Reference: FW-19 Data Query & Integration Actions
Purpose: Execute external API calls with authentication and retry logic within workflow execution. Request:
Request Body:
Response Schema (200 OK):
Error Response Schema:
Error Codes:
  • 400 Bad Request: Invalid config or URL
  • 401 Unauthorized: External API authentication failed
  • 408 Request Timeout: External API exceeded timeout
  • 502 Bad Gateway: External API returned error after all retries
  • 500 Internal Server Error: Internal function error
Authentication: JWT required, organization context required for credential lookup
Rate Limiting: 50 requests/minute per organization (lower due to external API calls)
Version: v1.0.0 (planned)

FW-22: Workflow Debug Control

Endpoint: Edge function workflow-debug-control
Method: POST
Provider: FW (Forms & Workflow)
Consumer: Workflow Developer, Operations Engineer
Status: ✅ Implemented (FW-22 Phase 4)
Spec Reference: FW-22 Workflow Execution Monitoring & Debugging
Purpose: Control debug sessions for workflow executions - start, pause, resume, step through. Request:
Request Body:
Response Schema (200 OK):
Error Response Schema:
Error Codes:
  • 400 Bad Request: Invalid action or missing required fields
  • 403 Forbidden: User lacks debug permissions for this execution
  • 404 Not Found: Execution or session not found
  • 409 Conflict: Another user has an active debug session
  • 500 Internal Server Error: Debug control error
Authentication: JWT required, user must have org_admin or workflow owner role
Rate Limiting: 100 requests/minute per organization
Version: v1.0.0 (released 2025-12-08)

FW-24: Sandbox Execute

Endpoint: Edge function sandbox-execute
Method: POST
Provider: FW (Forms & Workflow)
Consumer: Workflow Developer, QA Engineer
Status: 📝 Planned (FW-24 Implementation)
Spec Reference: FW-24 Workflow Testing & Sandbox
Purpose: Execute workflow in an isolated sandbox environment for testing. Request:
Request Body:
Response Schema (200 OK):
Error Response Schema:
Error Codes:
  • 400 Bad Request: Invalid input data or rule_id
  • 403 Forbidden: User lacks workflow edit permissions
  • 404 Not Found: Rule or test dataset not found
  • 500 Internal Server Error: Sandbox execution error
Authentication: JWT required, user must have workflow edit permissions
Rate Limiting: 50 requests/minute per organization
Version: v1.0.0 (planned)

FW-24: Test Datasets Import

Endpoint: Edge function test-datasets-import
Method: POST
Provider: FW (Forms & Workflow)
Consumer: Workflow Developer, QA Engineer
Status: 📝 Planned (FW-24 Implementation)
Spec Reference: FW-24 Workflow Testing & Sandbox
Purpose: Import test data from CSV or JSON files. Request:
Request Body (multipart):
  • file: File (CSV or JSON, max 10MB)
  • organization_id: UUID
  • name: string
  • description: string (optional)
Response Schema (200 OK):
Error Codes:
  • 400 Bad Request: Invalid file format
  • 413 Payload Too Large: File exceeds 10MB limit
  • 422 Unprocessable Entity: Data validation errors
Authentication: JWT required, user must have workflow edit permissions
Rate Limiting: 10 requests/minute per organization
Version: v1.0.0 (planned)

FW-24: Test Cases Run

Endpoint: Edge function test-cases-run
Method: POST
Provider: FW (Forms & Workflow)
Consumer: Workflow Developer, QA Engineer
Status: 📝 Planned (FW-24 Implementation)
Spec Reference: FW-24 Workflow Testing & Sandbox
Purpose: Execute test cases against a workflow in sandbox. Request:
Request Body:
Response Schema (200 OK):
Error Codes:
  • 400 Bad Request: Invalid test case IDs
  • 403 Forbidden: User lacks workflow edit permissions
  • 404 Not Found: Rule or test cases not found
  • 500 Internal Server Error: Test execution error
Authentication: JWT required, user must have workflow edit permissions
Rate Limiting: 20 requests/minute per organization
Version: v1.0.0 (planned)
Usage Example:

FW-45: Evaluate Decision Table

Endpoint: Edge function evaluate-decision-table
Method: POST
Provider: FW (Forms & Workflow)
Consumer: Workflow steps, server-side validation, integrations
Status: ✅ Implemented
Spec Reference: FW-45 Decision Tables
Integration: Server-side evaluation + fw_log_decision_table_evaluation RPC
Request body (logical): table_id, facts (record), optional version_id, evaluation_context (workflow_step | form_validation | api_request), optional source_execution_id, source_step_id. Response (200): success, matched, matchedRuleCount, outputs, evaluationId, versionNumber, hitPolicy, optional error. Authentication: JWT; org access verified per _shared/auth.ts.

FW-46: Workflow Executor Worker

Endpoint: Edge function workflow-executor-worker
Method: POST (typically invoked by pg_cron via pg_net, not direct client)
Provider: FW
Consumer: Platform scheduler / automation infrastructure
Status: ✅ Implemented
Spec Reference: FW-46 Durable Execution Worker
Purpose: Dequeue from pgmq workflow_execution_queue (primary) or claim queued rows (fallback); advance workflow execution with checkpoint integration (FW-48); route permanent failures toward DLQ (FW-47). Request Body: Typically empty (cron invocation). Optional JSON body for manual testing:
Success Response (200):
Error Response:
HTTP Status Codes:
  • 200 OK - Success (may include partial failures in results)
  • 400 Bad Request - Invalid request body
  • 500 Internal Server Error - Runtime error (e.g., queue read failure, semaphore acquisition failure)
Side Effects:
  • Updates fw_module_settings.worker_running and worker_last_run_at (semaphore pattern)
  • Updates fw_module_settings.worker_last_batch_size with processed count
  • Routes failed executions to workflow_dlq (FW-47) after max retries
  • Writes checkpoint data via FW-48 integration
  • Emits domain events for execution state changes (if configured)
Authentication: Service role / scheduled invocation pattern per deployment; not a general user-facing API.

FW-49: Workflow Timeout Checker

Endpoint: Edge function workflow-timeout-checker
Method: POST
Provider: FW
Consumer: pg_cron–scheduled watchdog
Status: ✅ Implemented
Spec Reference: FW-49 Execution Timeout & Watchdog
Integration: FW-49-execution-timeout-watchdog-INTEGRATION.md
Purpose: Scan overdue executions, apply on_timeout action (from timeout_config), emit workflow.execution.timed_out, integrate with FW-47 and PF-10. Request Body: Empty (cron invocation via createCronHandler). Optional JSON for manual testing:
Success Response (200):
Error Response:
  • Errors are logged but do not cause HTTP error responses (cron handler pattern)
  • Individual execution processing errors increment errorsCount and continue processing
  • Database query errors are logged with correlation ID
HTTP Status Codes:
  • 200 OK - Processing completed (may include errorsCount > 0)
Side Effects:
  • Updates fw_workflow_executions.status to 'timed_out' or 'cancelled' based on timeout_config.on_timeout
  • Sets fw_workflow_executions.completed_at and error_message for timed-out executions
  • Sends notifications via PF-10 (createNotificationIfNew) for:
    • Timeout events (normal or high priority for escalate)
    • At-risk warnings (when elapsedPercent >= warning_threshold_percent)
  • Updates fw_workflow_executions.timeout_warning_sent = true after warning sent
  • Emits workflow.execution.timed_out domain event (if configured)
Authentication: Service role / cron invocation; not a user-facing API.

FW-49: Extend Execution Deadline

Endpoint: Edge function extend-execution-deadline
Method: POST
Provider: FW
Consumer: FW UI (admin), execution detail
Status: ✅ Implemented
Spec Reference: FW-49 Execution Timeout & Watchdog
Request Body:
Success Response (200):
Error Response:
HTTP Status Codes:
  • 200 OK - Deadline extended successfully
  • 400 Bad Request - Validation error:
    • Missing required fields (execution_id, organization_id, extension_minutes, reason)
    • extension_minutes outside valid range (1-1440)
    • reason length invalid (5-500 characters)
    • Execution status not active (running, paused, pending, queued)
  • 401 Unauthorized - Missing or invalid JWT token
  • 403 Forbidden - Insufficient permissions (workflow admin required)
  • 404 Not Found - Execution not found or organization_id mismatch
  • 500 Internal Server Error - Database update failure
Side Effects:
  • Updates fw_workflow_executions.deadline_at with new deadline
  • Resets fw_workflow_executions.timeout_warning_sent = false to allow new warnings
  • Logs audit event with correlation ID, user ID, extension details, and timestamps
Authentication: JWT required; validates via validateAuth() from _shared/auth.ts. Requires fw.workflows.admin permission or equivalent org admin role.

FW-52: Workflow Export (Edge Function)

Endpoint: Edge function fw-workflow-export
Method: POST
Provider: FW
Consumer: FW UI, fw-promote CLI
Status: 📝 Planned
Spec Reference: FW-52 Workflow Import/Export & Portability
Integration Doc: FW-52-workflow-import-export-portability-INTEGRATION.md
Request Body (logical):
Success Response (200): WorkflowExportPackage JSON (see spec schema). Authentication: JWT; verifyOrgAccess; permission fw.workflows.export.

FW-52: Workflow Import (Edge Function)

Endpoint: Edge function fw-workflow-import
Method: POST
Provider: FW
Consumer: FW UI, fw-promote CLI
Status: 📝 Planned
Spec Reference: FW-52 Workflow Import/Export & Portability
Integration Doc: FW-52-workflow-import-export-portability-INTEGRATION.md
Request Body (logical):
Success Response (200): Dry-run: analysis summary JSON. Apply: { success: true, import_log_id: string }. Authentication: JWT; verifyOrgAccess; permission fw.workflows.import.

API Implementation Guidelines

Authentication

All API endpoints MUST:
  • Require JWT authentication in Authorization header
  • Validate JWT token and extract user context
  • Enforce RLS policies based on user’s organization access
  • Return 401 Unauthorized for missing/invalid tokens
  • Return 403 Forbidden for insufficient permissions

Rate Limiting

All API endpoints MUST:
  • Implement per-organization rate limits
  • Default limit: 100 requests/minute per organization
  • Return 429 Too Many Requests when limit exceeded
  • Include rate limit headers in response:
    • X-RateLimit-Limit: Maximum requests per window
    • X-RateLimit-Remaining: Remaining requests in window
    • X-RateLimit-Reset: Time when limit resets

Error Handling

All API endpoints MUST:
  • Use standard HTTP status codes
  • Return consistent error response format:
  • Log all errors for debugging
  • Never expose sensitive data in error messages

Response Format

All successful responses MUST:
  • Return JSON with consistent structure
  • Include metadata when applicable (pagination, timestamps)
  • Use camelCase for field names
  • Include organization_id for multi-tenant context

Planned API Contracts (IT Module)

The IT (Information Technology) module defines 4 planned API contracts for asset, vendor, and license management. Full IT API documentation: IT Integration Contracts

IT-01: Asset Lookup

Endpoint: /api/v1/it/assets/{asset_id}
Method: GET
Provider: IT (IT-01 Asset Management)
Consumer: IT-02 (Support Ticketing), IT-05 (Security)
Status: 📝 Planned
Spec Reference: IT-01 IT Asset Management
Purpose: Lookup IT asset details for ticket linking and security tracking. Response Schema (200 OK):
Error Codes: 404 Not Found, 403 Forbidden, 500 Internal Server Error
Authentication: JWT required, RLS enforces organization isolation
Rate Limiting: 100 requests/minute per organization

Endpoint: /api/v1/it/assets
Method: GET
Provider: IT (IT-01 Asset Management)
Consumer: IT-02, IT-04, IT-05, IT-06
Status: 📝 Planned
Query Parameters:
  • asset_type (optional): Filter by asset type
  • status (optional): Filter by status
  • site_id (optional): Filter by site
  • search (optional): Search by asset tag, name, or serial number
  • page, page_size: Pagination
Authentication: JWT required, RLS enforces organization isolation
Rate Limiting: 100 requests/minute per organization

IT-03: Vendor Lookup

Endpoint: /api/v1/it/vendors/{vendor_id}
Method: GET
Provider: IT (IT-03 Vendor Management)
Consumer: IT-01, IT-04, IT-06
Status: 📝 Planned
Spec Reference: IT-03 IT Vendor Management
Purpose: Lookup IT vendor details for asset, license, and procurement linking. Response Schema (200 OK):
Authentication: JWT required, RLS enforces organization isolation
Rate Limiting: 100 requests/minute per organization

IT-04: License Compliance Check

Endpoint: Database function it_check_license_compliance()
Provider: IT (IT-04 Software License Management)
Consumer: IT-04 Dashboard, scheduled compliance checks
Status: 📝 Planned
Spec Reference: IT-04 Software License Management
Purpose: Check license compliance for a software license or all licenses. Function Signature:
Security: Function is SECURITY DEFINER with SET search_path = public

FA-19: Financial Close Management APIs

Status: ✅ Implemented
Spec Reference: FA-19-financial-close-management.md
Last Verified: 2026-01-19

API 1: Close Period Approval

Endpoint: /api/v1/fa/close/periods/{period_id}/approve
Method: POST
Provider: FA (Finance & Accounting)
Consumer: FW-03 (Approval Workflow), FA-19 Internal
Status: ✅ Implemented
Purpose: Approve or reject a close period after all tasks are complete. This is a critical control point in the financial close process. Tenant Context Enforcement:
  • The close period’s organization_id MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED if the period belongs to an organization the caller cannot access
  • User must have fa.close.approve permission
PII/PHI Logging Guidelines:
  • NEVER log: User names, comments containing PII, financial amounts
  • ALLOWED in logs: UUIDs (period_id, user_id), error codes, timestamps, organization_id, status codes
Request Schema:
Response Schema (200 OK):
Error Response Schema:
Error Codes:
  • 400 Bad Request: Invalid request body
  • 403 Forbidden: User lacks fa.close.approve permission or organization access
    • Error code: ACCESS_DENIED or PERMISSION_DENIED
  • 404 Not Found: Close period not found
    • Error code: PERIOD_NOT_FOUND
  • 409 Conflict: Period not in pending_approval status or has incomplete tasks
    • Error code: INVALID_STATUS or INCOMPLETE_TASKS
  • 500 Internal Server Error: Unexpected error
    • Error code: SERVER_ERROR
Usage Example:
Implementation Notes:
  • Period must be in pending_approval status
  • All tasks must be completed or skipped before approval
  • Approval triggers close_period_approved event
  • Rejection triggers status change back to in_progress
  • Approval locks period from further modifications

API 2: Close Period Status Query

Endpoint: /api/v1/fa/close/periods/{period_id}/status
Method: GET
Provider: FA (Finance & Accounting)
Consumer: FA-07 (Financial Reporting), FA-02 (General Ledger)
Status: ✅ Implemented
Purpose: Query the current status and progress of a close period. Used by Financial Reporting to verify close status before generating final reports. Tenant Context Enforcement:
  • The close period’s organization_id MUST be validated against the authenticated session using pf_has_org_access(organization_id, auth.uid())
  • Return 403 ACCESS_DENIED if the period belongs to an organization the caller cannot access
PII/PHI Logging Guidelines:
  • NEVER log: User names, task descriptions containing PHI
  • ALLOWED in logs: UUIDs (period_id), error codes, timestamps, organization_id, task counts
Response Schema (200 OK):
Error Codes:
  • 403 Forbidden: User does not have access to period’s organization
    • Error code: ACCESS_DENIED
  • 404 Not Found: Close period not found
    • Error code: PERIOD_NOT_FOUND
Usage Example:
Implementation Notes:
  • Used by FA-07 to verify period is closed before final reporting
  • Completion percentage = (completed + skipped) / total * 100
  • Task summary aggregated from fa_close_tasks
  • Returns fiscal_period_id if close period is linked to fiscal period

FA-19 Consumed APIs

From FA-02 (General Ledger):
  • fa_fiscal_periods table query for linking close periods to fiscal periods
  • Period status validation before close approval
  • Fiscal period closed status check
From FA-07 (Financial Reporting):
  • Close status check before generating final reports
  • fa_report_definitions query for close-related reports

PF-59: AI Edge Functions (OpenRouter)

Status: 🔄 Updated (PF-59 migration from Lovable AI Gateway)
Implemented: 2025-12-06 (PF-27), Updated 2026-01-28 (PF-59)
Spec Reference:

ai-assistant

Endpoint: /functions/v1/ai-assistant
Method: POST
Provider: PF (Platform Foundation)
Consumer: All cores via @/platform/ai hooks
Status: ✅ Implemented (PF-27), 🔄 Updated (PF-59)
Purpose: Streaming AI chat with module-specific system prompts and automatic model selection based on module context. Request Schema:
Response Schema (200 OK - Streaming):
  • Server-Sent Events (SSE) stream with chunks:
Response Schema (200 OK - Non-Streaming):
Model Selection:
  • Automatically selected based on moduleContext.module:
    • gr, hranthropic/claude-3.5-sonnet
    • fa, fw, rh, pfopenai/gpt-4o
    • lo, fmopenai/gpt-4o-mini
    • Unknown/default → openai/gpt-4o
Error Codes:
  • 400 Bad Request: Invalid request format or PHI detected
    • Error code: INVALID_REQUEST or PHI_DETECTED
  • 401 Unauthorized: Missing or invalid JWT token
    • Error code: UNAUTHORIZED
  • 402 Payment Required: AI credits depleted
    • Error code: CREDITS_DEPLETED
    • User message: “AI credits depleted”
  • 429 Too Many Requests: Rate limited
    • Error code: RATE_LIMITED
    • User message: “Rate limited, please try again”
    • Automatic exponential backoff retry (max 3 retries)
  • 500 Internal Server Error: OpenRouter service error
    • Error code: SERVICE_ERROR
    • User message: “AI service error. Please try again.”
Authentication:
  • JWT required in Authorization: Bearer {token} header
  • Token validated against Supabase Auth
  • Organization ID derived from authenticated user profile
Tenant Context Enforcement:
  • The organization_id is derived from the authenticated user via auth.uid() and user profile metadata
  • If moduleContext.organization_id is provided, it MUST be validated using pf_has_org_access(organization_id, auth.uid())
  • If validation fails (user does not have access to the specified organization), return 403 Forbidden with error code ACCESS_DENIED
  • Never trust client-provided organization IDs without validation
PII/PHI Logging Guidelines:
  • FORBIDDEN: Do NOT log user messages, AI responses, conversation history, or moduleContext identifiers
  • ALLOWED: Log UUIDs (correlation_id, user_id), error codes, timestamps, organization_id (UUID only), model selection, and aggregated token counts
  • Usage logging to pf_ai_usage_logs and telemetry MUST exclude message content and AI responses
  • Only log metadata required for billing, debugging, and analytics (token counts, model used, success/failure status)
Rate Limiting:
  • OpenRouter enforces rate limits
  • Exponential backoff: 1s initial, max 30s delay, max 3 retries
  • Retry only on 429 errors
Environment Variables:
  • OPENROUTER_API_KEY (required) - OpenRouter API key
  • APP_URL (optional) - Application URL for referrer header (defaults to https://encoreos.io)
Implementation Notes:
  • Streaming responses use Server-Sent Events (SSE)
  • PHI detection runs on user messages before sending to AI
  • Module context enables specialized system prompts
  • Usage logged to pf_ai_usage_logs table (metadata only, no content)
Content-Type Headers:
  • Streaming: text/event-stream; charset=utf-8
  • Non-streaming: application/json
Performance SLA Targets:
  • p50 latency: <500ms (time-to-first-token for streaming)
  • p95 latency: <2s (time-to-first-token for streaming)
  • p99 latency: <5s (time-to-first-token for streaming)
  • Non-streaming response: <3s p95
Request/Processing Timeouts:
  • Default timeout: 30s (configurable via OPENROUTER_TIMEOUT environment variable)
  • Streaming timeout: 60s (longer for streaming responses)
  • Client should set appropriate timeout based on expected response length
Idempotency Semantics:
  • Non-idempotent: Identical requests may produce different responses due to:
    • Model non-determinism (temperature > 0)
    • Context window state differences
    • Rate limiting and retry behavior
  • Clients should not retry identical requests expecting identical results
  • Use correlationId for tracking related requests
Retry Semantics:
  • Non-retryable errors (do not retry):
    • 400 Bad Request (INVALID_REQUEST, PHI_DETECTED) - Client error, retry won’t help
    • 401 Unauthorized - Authentication issue, retry won’t help
    • 402 Payment Required (CREDITS_DEPLETED) - Billing issue, retry won’t help
    • 403 Forbidden (ACCESS_DENIED) - Permission issue, retry won’t help
  • Retry on 429 (Rate Limited):
    • Exponential backoff: 1s initial delay, max 30s delay, max 3 retries
    • Retry-After header should be respected if present
    • Client should implement jitter to avoid thundering herd
  • Client guidance for 500 (Internal Server Error):
    • Retry with exponential backoff (1s, 2s, 4s delays)
    • Max 3 retries for 500 errors
    • If all retries fail, show user-friendly error message
    • Log correlationId for support
Caching Strategy:
  • No caching: Responses are not cached
  • No TTL: Each request is processed fresh
  • Clients may cache responses locally if needed, but server does not cache
Testing Requirements:
  • Unit Tests:
    • Model selection logic based on moduleContext.module
    • PHI detection function (positive and negative cases)
    • Error handling for all error codes (400, 401, 402, 403, 429, 500)
    • Request validation (message format, required fields)
  • Integration/E2E Tests:
    • Streaming behavior (SSE format validation, chunk parsing)
    • Non-streaming response format
    • Authentication flow (JWT validation)
    • Rate limiting behavior (429 responses)
    • Timeout handling
  • Tenant Isolation/RLS Tests:
    • Exercise pf_has_org_access() function for organization_id validation
    • Verify cross-tenant access is denied (403 Forbidden)
    • Test with users from different organizations
    • Verify RLS policies prevent data leakage
Version: v1.1.0 (PF-59 - OpenRouter migration)
Last Updated: 2026-01-28

ai-document-analyze

Endpoint: /functions/v1/ai-document-analyze
Method: POST
Provider: PF (Platform Foundation)
Consumer: All cores via @/platform/ai hooks
Status: ✅ Implemented (PF-27), 🔄 Updated (PF-59)
Purpose: Analyze documents using AI with structured JSON output. Optimized for document extraction and analysis tasks. Request Schema:
Response Schema (200 OK):
Model: openai/gpt-4o (optimized for structured output) Error Codes:
  • Same as ai-assistant (400, 401, 402, 429, 500)
  • 403 Forbidden: Access denied when document’s organization_id validation fails
    • Error code: ACCESS_DENIED
    • User message: “Access denied to this document”
Authentication: Same as ai-assistant (JWT required) Tenant Context Enforcement:
  • The document’s organization_id (from pf_documents table) MUST be validated using pf_has_org_access(document.organization_id, auth.uid())
  • If validation fails, return 403 Forbidden with error code ACCESS_DENIED
  • Never process documents from organizations the user cannot access
PII/PHI Logging Guidelines:
  • FORBIDDEN: Do NOT log document content, analysis results containing PHI/PII, or schema outputs with sensitive fields
  • ALLOWED: Log UUIDs (document_id, correlation_id), error codes, timestamps, confidence scores (without content), and non-sensitive metadata
  • Analysis results and extracted fields may contain PHI/PII and MUST NOT be logged to application logs, telemetry, or audit trails
  • Only log metadata required for debugging and analytics (document_id, success/failure, processing time, model used)
Environment Variables: Same as ai-assistant Content-Type Headers:
  • application/json (non-streaming only)
Performance SLA Targets:
  • p50 latency: <2s
  • p95 latency: <5s
  • p99 latency: <10s
Request/Processing Timeouts:
  • Default timeout: 30s (configurable via OPENROUTER_TIMEOUT environment variable)
  • Client should set appropriate timeout based on document size
Idempotency Semantics:
  • Non-idempotent: Identical requests may produce different analysis results due to:
    • Model non-determinism (temperature > 0)
    • Document content changes (if document is updated between requests)
  • Clients should not retry identical requests expecting identical results
  • Use correlationId for tracking related requests
Retry Semantics:
  • Non-retryable errors (do not retry):
    • 400 Bad Request (INVALID_REQUEST, PHI_DETECTED) - Client error, retry won’t help
    • 401 Unauthorized - Authentication issue, retry won’t help
    • 402 Payment Required (CREDITS_DEPLETED) - Billing issue, retry won’t help
    • 403 Forbidden (ACCESS_DENIED) - Permission issue, retry won’t help
  • Retry on 429 (Rate Limited):
    • Exponential backoff: 1s initial delay, max 30s delay, max 3 retries
    • Retry-After header should be respected if present
    • Client should implement jitter to avoid thundering herd
  • Client guidance for 500 (Internal Server Error):
    • Retry with exponential backoff (1s, 2s, 4s delays)
    • Max 3 retries for 500 errors
    • If all retries fail, show user-friendly error message
    • Log correlationId for support
Caching Strategy:
  • No caching: Responses are not cached
  • No TTL: Each request is processed fresh
  • Clients may cache analysis results locally if needed, but server does not cache
Testing Requirements:
  • Unit Tests:
    • Model selection (should use openai/gpt-4o for structured output)
    • PHI detection function (positive and negative cases)
    • Error handling for all error codes (400, 401, 402, 403, 429, 500)
    • Request validation (documentId format, schema validation)
  • Integration/E2E Tests:
    • Response format validation (analysis, confidence, extractedFields)
    • Authentication flow (JWT validation)
    • Rate limiting behavior (429 responses)
    • Timeout handling
  • Tenant Isolation/RLS Tests:
    • Exercise pf_has_org_access() function for document organization_id validation
    • Verify cross-tenant access is denied (403 Forbidden)
    • Test with users from different organizations accessing same document
    • Verify RLS policies prevent data leakage
Version: v1.1.0 (PF-59 - OpenRouter migration)
Last Updated: 2026-01-28

generate-report-narrative

Endpoint: /functions/v1/generate-report-narrative
Method: POST
Provider: PF (Platform Foundation)
Consumer: All cores via @/platform/ai hooks
Status: ✅ Implemented (PF-27), 🔄 Updated (PF-59)
Purpose: Generate narrative text for reports based on data and context. Optimized for narrative generation tasks. Request Schema:
Response Schema (200 OK):
Model: openai/gpt-4o (optimized for narrative generation) Error Codes:
  • Same as ai-assistant (400, 401, 402, 429, 500)
  • 403 Forbidden: Access denied when report’s organization_id validation fails
    • Error code: ACCESS_DENIED
    • User message: “Access denied to this report”
Authentication: Same as ai-assistant (JWT required) Tenant Context Enforcement:
  • The incoming request.reportData MUST include organization_id
  • The server MUST validate access using pf_has_org_access(reportData.organization_id, auth.uid())
  • If validation fails (user does not have access to the specified organization), return 403 Forbidden with error code ACCESS_DENIED
  • Never generate narratives for reports from organizations the user cannot access
PII/PHI Logging Guidelines:
  • FORBIDDEN: Do NOT log report content, generated narrative text, or summaries containing identifiers or financial data
  • ALLOWED: Log UUIDs (report_id, correlation_id), error codes, timestamps, and non-content metadata (reportType as enum, not content)
  • Report data, narratives, and summaries may contain PHI/PII and financial information and MUST NOT be logged to application logs, telemetry, or audit trails
  • Only log metadata required for debugging and analytics (report_id, reportType enum, success/failure, processing time, model used)
Environment Variables: Same as ai-assistant Content-Type Headers:
  • application/json (non-streaming only)
Performance SLA Targets:
  • p50 latency: <3s
  • p95 latency: <8s
  • p99 latency: <15s
Request/Processing Timeouts:
  • Default timeout: 30s (configurable via OPENROUTER_TIMEOUT environment variable)
  • Client should set appropriate timeout based on report data size
Idempotency Semantics:
  • Non-idempotent: Identical requests may produce different narrative text due to:
    • Model non-determinism (temperature > 0)
    • Report data changes (if report is updated between requests)
  • Clients should not retry identical requests expecting identical results
  • Use correlationId for tracking related requests
Retry Semantics:
  • Non-retryable errors (do not retry):
    • 400 Bad Request (INVALID_REQUEST, PHI_DETECTED) - Client error, retry won’t help
    • 401 Unauthorized - Authentication issue, retry won’t help
    • 402 Payment Required (CREDITS_DEPLETED) - Billing issue, retry won’t help
    • 403 Forbidden (ACCESS_DENIED) - Permission issue, retry won’t help
  • Retry on 429 (Rate Limited):
    • Exponential backoff: 1s initial delay, max 30s delay, max 3 retries
    • Retry-After header should be respected if present
    • Client should implement jitter to avoid thundering herd
  • Client guidance for 500 (Internal Server Error):
    • Retry with exponential backoff (1s, 2s, 4s delays)
    • Max 3 retries for 500 errors
    • If all retries fail, show user-friendly error message
    • Log correlationId for support
Caching Strategy:
  • No caching: Responses are not cached
  • No TTL: Each request is processed fresh
  • Clients may cache narrative text locally if needed, but server does not cache
Testing Requirements:
  • Unit Tests:
    • Model selection (should use openai/gpt-4o for narrative generation)
    • PHI detection function (positive and negative cases)
    • Error handling for all error codes (400, 401, 402, 403, 429, 500)
    • Request validation (reportData format, context validation)
  • Integration/E2E Tests:
    • Response format validation (narrative, summary)
    • Authentication flow (JWT validation)
    • Rate limiting behavior (429 responses)
    • Timeout handling
  • Tenant Isolation/RLS Tests:
    • Exercise pf_has_org_access() function for reportData organization_id validation
    • Verify cross-tenant access is denied (403 Forbidden)
    • Test with users from different organizations accessing same report
    • Verify RLS policies prevent data leakage
Version: v1.1.0 (PF-59 - OpenRouter migration)
Last Updated: 2026-01-28

IT Module APIs (Implemented)

IT-07: Dashboard Summary RPC

Endpoint: Database function it_get_dashboard_summary(p_organization_id UUID)
Provider: IT (IT-07 Dashboard & Reporting)
Consumer: IT Dashboard
Status: ✅ Implemented
Last Verified: 2026-02-15
Purpose: Return aggregated IT dashboard metrics in a single call. Invocation Pattern:
Function Signature:
Response Schema:
React Hook: useITDashboardSummary(organizationId) in src/cores/it/hooks/useITDashboardSummary.ts Authentication: JWT required. Validated via it_has_org_access(p_organization_id, auth.uid()) SECURITY DEFINER check.
Tenant Enforcement: p_organization_id validated against caller’s JWT via it_has_org_access(). Returns only data for the specified organization.
Permissions: it.dashboard.view
Error Response Schema:
Rate Limiting: 100 req/min per organization (standard). Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
Caching: staleTime: 5min, gcTime: 10min (configured in React hook).
Performance SLA: p95 < 500ms.
Idempotency: Read-only RPC — safe to retry.

PII/PHI Logging Guidelines (IT-07)

The it_get_dashboard_summary RPC and its React hook useITDashboardSummary handle only aggregated counts — no individual-level PII is returned or logged. Compliant log line:
Non-compliant log line:
Reference: See FA-10/FA-11 PII/PHI Logging Policy in this document for the canonical policy. IT-07 follows the same standards.

IT-09: Change Request List/Detail RPCs

Endpoint 1: Database function it_get_changes(p_organization_id, p_status, p_limit)
Endpoint 2: Database function it_get_change_detail(p_organization_id, p_change_id)
Provider: IT (IT-09 Change Management)
Consumer: IT Change Management UI
Status: ✅ Implemented
Last Verified: 2026-02-15
Purpose: Query change requests with requester/implementer names, and get full detail with approvals and implementations. Invocation Pattern:
React Hooks:
  • useITChanges(organizationId, status?, limit?) in src/cores/it/hooks/useITChanges.ts
  • useITChangeDetail(organizationId, changeId) in src/cores/it/hooks/useITChanges.ts
Authentication: JWT required. Validated via it_has_org_access(p_organization_id, auth.uid()) SECURITY DEFINER check.
Tenant Enforcement: p_organization_id validated against caller’s JWT via it_has_org_access().
Permissions: it.changes.view
Error Response Schema:
Rate Limiting: 100 req/min per organization (standard). Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
Caching: staleTime: 5min, gcTime: 10min (configured in React hooks).
Performance SLA: p95 < 500ms.
Idempotency: Read-only RPCs — safe to retry. it_get_change_detail returns NULL when change not found.

PII/PHI Logging Guidelines (IT-09)

The it_get_changes and it_get_change_detail RPCs (hooks: useITChanges, useITChangeDetail) return change request metadata including requester/implementer display names joined from pf_profiles. Compliant log line:
Non-compliant log line:
Reference: See FA-10/FA-11 PII/PHI Logging Policy in this document for the canonical policy. IT-09 follows the same standards.

IT-07: Report Generation (Documented)

Endpoint: supabase.functions.invoke('generate-it-report', { body })
Provider: IT (IT-07 Dashboard & Reporting)
Consumer: IT Report Builder
Status: ✅ Implemented (existing edge function)
Last Verified: 2026-02-15
Invocation Pattern:
Request Schema:
Response Schema:
Download: Use file_url from the response to download the generated report. Authentication: JWT required (Authorization header). organization_id validated server-side.
Tenant Enforcement: organization_id in body validated against caller’s JWT.
Permissions: it.reports.generate
Error Response Schema:
Rate Limiting: 100 req/min per organization (standard). Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
Performance SLA: p95 < 2s for report generation.
Idempotency: Idempotent by run_id — re-invoking with the same run_id returns cached result.

IT-08: Onboarding/Offboarding Hooks (Documented)

Provider: IT (IT-08 Onboarding/Offboarding)
Status: ✅ Implemented (existing hooks)
Last Verified: 2026-02-15
Hooks:
  • usePendingOnboarding(organizationId) — filters it_onboarding_instances by workflow_type = 'onboarding', status IN ('pending', 'in_progress')
  • usePendingOffboarding(organizationId) — filters it_onboarding_instances by workflow_type = 'offboarding', status IN ('pending', 'in_progress')
Location: src/cores/it/hooks/useOnboardingInstances.ts Authentication: JWT required. RLS enforces it_has_org_access(organization_id, auth.uid()).
Tenant Enforcement: organization_id filter applied in query + RLS policy.
Permissions: it.onboarding.view
Caching: staleTime: 5min, gcTime: 10min (configured in React hooks).
Performance SLA: p95 < 500ms.

FA-10/FA-11: Cross-Core Payroll Read Integrations (W-2 / Form 941)

Endpoint 1: Database function fa_generate_w2_forms(p_org_id UUID, p_tax_year INT) Endpoint 2: Database function fa_generate_form_941(p_org_id UUID, p_tax_year INT, p_quarter INT) Provider: FA (FA-10 Tax Compliance / FA-11 Payroll Tax) Consumer: FA Tax Compliance UI, 1099/W-2 Generation Status: ✅ Implemented Last Verified: 2026-02-16 Purpose: Generate W-2 and Form 941 data by reading cross-core HR payroll tables. These functions run with SECURITY DEFINER to access HR tables from the FA core via the Platform Integration Layer pattern. Cross-Core Tables Read (HR → FA):
  • hr_payroll_line_items — individual pay line items (wages, deductions, taxes)
  • hr_payroll_runs — payroll run metadata (period, status, finalized date)
  • hr_employees — employee demographic data (name, SSN for W-2 filing)
Data Fields Accessed:
  • wages_tips_compensation, federal_tax_withheld, ss_tax_withheld, medicare_tax_withheld, state_tax_withheld
  • social_security_wages, medicare_wages
  • Employee identifiers (for W-2 box population)
Tenant Isolation: All queries filter by organization_id = p_org_id. Only finalized payroll runs (status = 'finalized') for the specified tax year are included. RLS is bypassed via SECURITY DEFINER; the function validates org access internally. Security: Both functions use SECURITY DEFINER SET search_path = public. Caller’s org membership is validated before execution.

PII/PHI Logging Guidelines (FA-10/FA-11 Payroll)

Architecture Reference: These functions follow constitution.md §1.3 (Cross-Core Integration via SECURITY DEFINER RPCs). The FA core does not import HR code; it reads HR tables through documented database functions only.

Planned API Contracts (CL/PM EHR & Practice Management)

Source: EHR_PM_PLANNING_BUNDLE.md
Status: 📝 Planned (stubs); request/response schemas to be defined in respective CL/PM specs.

Eligibility Check

Endpoint: GET /api/v1/pm/eligibility or POST /api/v1/pm/eligibility (platform layer may wrap external 270/271)
Method: GET (by patient_id + payer_id) or POST (batch)
Content-Type: application/json (request/response)
Provider: PM (or platform layer wrapping external 270/271)
Consumers: CL, PM-07, PM-08
Status: 📝 Planned
Spec Reference: PM-02 – Insurance & Eligibility Verification
Purpose: Synchronous eligibility verification for a patient and payer. Request/response schema to be defined in PM-02; may wrap clearinghouse 270/271. Authentication & tenant context: JWT required; organization_id MUST be validated against the authenticated session/claims (do not trust client-provided org_id). All queries scoped by tenant. No cross-tenant access. TypeScript interfaces (required vs optional):
Rate limiting: 60 requests/minute per organization. Response headers:
  • X-RateLimit-Limit: 60
  • X-RateLimit-Remaining: remaining in current window
  • X-RateLimit-Reset: Unix timestamp (seconds) when the window resets
SLA & timeout: p50 < 2s, p95 < 5s, p99 < 10s; client timeout 15s. 504 if backend exceeds 15s. Retry & idempotency: GET is idempotent. For POST: send Idempotency-Key: <uuid> header to allow safe retries; same key returns same response. Retry 5xx and 429 with exponential backoff (e.g. 1s, 2s, 4s); do not retry 4xx (except 429). Error response: { error: { code, message, details? } }; status codes: 400 (invalid params), 401 (unauthorized), 403 (forbidden), 404 (patient/payer not found), 429 (rate limit), 500.
Permissions: pm.eligibility.read or equivalent; authorization rules in PM-02.
PII/PHI logging: Log only organization_id, request_id, duration; never log member ID, DOB, or payer-specific identifiers in plain text.
Caching: Short TTL (e.g. 5 min) keyed by organization_id + patient_id + payer_id + service_date; invalidate on eligibility update.
Example (consumer):

Claim Status Query

Endpoint: GET /api/v1/pm/claim-status?claim_id={id} or POST /api/v1/pm/claim-status (platform layer may wrap 276/277)
Method: GET or POST
Content-Type: application/json
Provider: PM (or platform layer wrapping clearinghouse 276/277)
Consumers: PM-08, PM-11
Status: 📝 Planned
Spec Reference: PM-15 – Clearinghouse Integration
Purpose: Query claim status from clearinghouse. Request/response to be defined in PM-15. Authentication & tenant context: JWT required; organization_id enforced; RLS/defense-in-depth on claim access.
Request: ClaimStatusRequestclaim_id: uuid, organization_id: uuid.
Response (200): ClaimStatusResponseclaim_id, status, adjudication_date?, denial_reason?, payer_ref?.
Error response: Same structure as above; 400, 401, 403, 404, 429, 500.
Permissions: pm.claims.read; scope by org and optional site.
PII/PHI logging: No claim numbers or patient identifiers in logs; only org_id, claim_id (UUID), duration.
Rate limiting & SLA: TBD (e.g. 120/min); p50/p95/p99 and timeout TBD in PM-15.
Retry & idempotency: GET idempotent; retry 5xx/429 with backoff.
Caching: Optional short TTL for status by claim_id.
Example:
const res = await fetch(\/api/v1/pm/claim-status?claim_id=&#123;claimId&#125;\`, { headers: { 'Authorization': \`Bearer {token}` } });
const data: ClaimStatusResponse = await res.json();`

Resident/Patient Lookup (RH–CL Boundary)

Endpoint: GET /api/v1/platform/patient-lookup?resident_id={uuid} or GET /api/v1/platform/patient-lookup?organization_id={uuid}&external_id={string}
Method: GET
Content-Type: application/json
Provider: PM or PF (platform layer)
Consumers: RH, CL
Status: 📝 Planned
Spec Reference: Cross-core; no direct RH→CL imports. See EHR_PM_PLANNING_BUNDLE.md §3 (API Contracts List).
Purpose: Resolve resident (RH) to patient (PM/CL) identity for cross-core workflows. Schema to be defined in integration spec. Authentication & tenant context: JWT required; organization_id enforced; caller must have access to resident and patient context.
Request: Query params resident_id or organization_id + external_id (e.g. RH resident id).
Response (200): PatientLookupResponsepatient_id?: uuid, resident_id?: uuid, match_type: 'exact'|'none', demographics_ref?: string.
Error response: 400, 401, 403, 404, 429, 500.
Permissions: platform.patient_lookup.read or equivalent; RH/CL scoped.
PII/PHI logging: Log only org_id, resident_id (UUID), match_type; no names or DOB.
Rate limiting & SLA: TBD; conservative limits for lookup.
Retry & idempotency: GET idempotent.
Caching: Optional by resident_id + org_id.
Example:
const res = await fetch(\/api/v1/platform/patient-lookup?resident_id=&#123;residentId&#125;\`, { headers: { 'Authorization': \`Bearer {token}` } });
const data: PatientLookupResponse = await res.json();`

Patient Demographics (Read)

Endpoint: GET /api/v1/pm/patients/:id (or platform layer equivalent)
Method: GET
Content-Type: application/json
Provider: PM
Consumers: CL, PM-02, PM-03, PM-07, PM-08
Status: 📝 Planned
Spec Reference: PM-01 – Patient Registration & Demographics
Purpose: Read-only patient demographics (USCDI v3–aligned). No PII in logs. Authentication & tenant context: JWT required; organization_id and RLS enforce tenant scope; path param :id is patient UUID.
Request: Path only; optional query fields for field set.
Response (200): PatientDemographicsResponse — USCDI v3–aligned demographics (name, DOB, gender, identifiers, etc.); shape in PM-01.
Error response: 400, 401, 403, 404, 429, 500.
Permissions: pm.patients.read or equivalent; org/site scoped.
PII/PHI logging: Do not log PHI; only org_id, patient_id (UUID), request_id, duration.
Rate limiting & SLA: 200 req/min per org; p95 < 500ms; timeout 10s.
Retry & idempotency: GET idempotent; retry 5xx/429 with backoff.
Caching: Short TTL for demographics by patient_id (e.g. 60s).
Example:
const res = await fetch(\/api/v1/pm/patients/&#123;patientId&#125;\`, { headers: { 'Authorization': \`Bearer {token}` } });
const data: PatientDemographicsResponse = await res.json();`

Endpoint: POST /api/v1/platform/consent/check (or internal CL service)
Method: POST
Content-Type: application/json
Provider: CL or platform consent service
Consumers: CL, PM (billing), FW
Status: 📝 Planned
Spec Reference: CL-11 – Consent Management & 42 CFR Part 2
Purpose: Evaluate whether a given access (e.g. TPO, SUD counseling notes, legal proceedings) is permitted for the current user/context. Request/response to be defined in CL-11. Authentication & tenant context: JWT required; organization_id and patient/resource context required; tenant-scoped policy evaluation.
Request: ConsentCheckRequestpatient_id: uuid, purpose_of_use: string, resource_type?: string, encounter_id?: uuid, organization_id: uuid, user_id?: uuid.
Response (200): ConsentCheckResponseallowed: boolean, obligations?: string[], denial_reason?: string, audit_id?: string.
Error response: 400, 401, 403, 404, 429, 500.
Permissions: Caller must have access to patient and consent service; rules in CL-11.
PII/PHI logging: Log only org_id, patient_id (UUID), purpose_of_use, allowed/denied, audit_id; no consent text or PHI.
Rate limiting & SLA: 300 req/min per org; p95 < 200ms; timeout 5s. Retry 5xx/429 with backoff.
Rate limiting & SLA: TBD; low latency required for UI/API gating.
Retry & idempotency: POST non-idempotent; use idempotency key if supported.
Caching: Policy result cache with short TTL; invalidate on consent change.
Example:
const res = await fetch('/api/v1/platform/consent/check', { method: 'POST', headers: { 'Authorization': \Bearer ${token}`, ‘Content-Type’: ‘application/json’ }, body: JSON.stringify({ patient_id, purpose_of_use: ‘TPO’, organization_id }) });
const data: ConsentCheckResponse = await res.json();`

CL-16-EN-01: TEFCA/QHIN Connectivity API (Planned) \

Provider: CL (Clinical & EHR) — CL-16-EN-01
Consumers: CL clinical workflows, compliance/audit services, downstream interoperability consumers
Status: 📝 Planned
Ownership: CL-16-EN-01 owns TEFCA/QHIN request orchestration and contract versioning.
Spec Reference: CL-16-EN-01
Integration Doc: CL-16-EN-01-tefca-qhin-connectivity-INTEGRATION.md
Contract requirements (all CL-16-EN-01 TEFCA endpoints):
  • Authentication: JWT required (Authorization: Bearer <token>).
  • Scopes: cl.tefca.view required for query/history endpoints; cl.tefca.exchange required for outbound exchange.
  • Tenant isolation: organization_id in request MUST match the authenticated session organization; mismatches fail with 403 TENANT_MISMATCH.
  • Correlation: every request/response includes correlation_id for traceability.
  • PHI/PII logging restrictions: log only correlation_id, organization_id, purpose_of_use, and status; never log patient identifiers, patient match attributes, or clinical payload content.
  • Error model: standardized responses include one of 400, 401, 403, 404, 429, 500 with stable machine-readable error codes.
  • Rate limiting/retry: high-volume exchanges are rate-limited per org; clients retry only 429/5xx with exponential backoff + jitter and honor Retry-After when present.
Standard error code expectations:
  • 400 INVALID_REQUEST - malformed payload, missing required fields, or unsupported enum values.
  • 401 UNAUTHENTICATED - missing/invalid JWT.
  • 403 CONSENT_DENIED - CL-11 consent is missing/restricted for requested disclosure.
  • 403 TENANT_MISMATCH - organization_id does not match authenticated tenant context.
  • 404 NOT_FOUND - requested exchange/query artifact does not exist in tenant scope.
  • 429 RATE_LIMITED - per-org throughput limit exceeded.
  • 500 QHIN_CONNECTIVITY_FAILURE - downstream QHIN/transport/service failure.

Endpoint: POST /tefca/qhin/query \

  • Purpose: Submit a TEFCA/QHIN patient query request.
  • Required fields: organization_id, patient match attributes, purpose_of_use.
  • Response shape: { status, correlation_id, match_candidates[] }.
  • Ownership: CL-16-EN-01.
  • TODO (schema/version): request/response JSON schema, enum normalization, version lifecycle.

Endpoint: POST /tefca/qhin/exchange \

  • Purpose: Execute outbound TEFCA/QHIN exchange with CL-11 consent gates.
  • Required fields: organization_id, patient_id, purpose_of_use, exchange payload reference.
  • Response shape: { status, correlation_id, exchange_log_id }.
  • Consent binding (required): request MUST execute a CL-11 ConsentCheckRequest with patient_id, purpose_of_use, and organization_id before outbound disclosure.
  • Fail-closed behavior: if consent is absent/restricted, return 403 CONSENT_DENIED and do not send any outbound payload.
  • Ownership: CL-16-EN-01.
  • TODO (schema/version): consent obligations schema, redisclosure notice metadata, versioned error model.

Endpoint: GET /tefca/qhin/exchanges \

  • Purpose: Retrieve org-scoped TEFCA/QHIN exchange history and compliance statuses.
  • Required fields: organization_id and filter parameters (status, direction, date range, qhin_identifier).
  • Response shape: { status, correlation_id, items[], pagination }.
  • Ownership: CL-16-EN-01.
  • TODO (schema/version): pagination contract, filter grammar, response projection profile by API version.

CL-16: FHIR R4 API — Edge Function Facade

Invocation: supabase.functions.invoke('fhir-r4', { body })
Content-Type: application/json (request) / application/fhir+json (response)
Provider: CL (Clinical & EHR)
Consumers: PM-12 (patient access), external FHIR clients, HIE partners
Status: 🟡 In Progress (Phase 1)
Spec Reference: CL-16 – FHIR Interoperability & Data Exchange
Integration Doc: CL-16-fhir-interoperability-data-exchange-INTEGRATION.md
Purpose: FHIR R4 facade exposing US Core resources (Patient, Condition, MedicationRequest, AllergyIntolerance, Encounter, Observation) via Supabase Edge Functions. Translates internal Encore OS data model to FHIR R4 JSON. Consent-gated (CL-11 Part 2). All calls logged to cl_data_exchange_log. Architecture Decision (Errata E-1): Supabase Edge Functions as FHIR facade for Phase 1, with migration path to Hybrid (Edge Functions + external FHIR server) for ONC certification.

Endpoint 1: GET /fhir/r4/Patient/:id

Action: get-patient
Required Fields: organization_id, patient_id
Source Table: pm_patients
FHIR Profile: US Core Patient (http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient)
Request:
Response (200):
Error Responses: All errors return FHIR OperationOutcome JSON with Content-Type: application/fhir+json.

Endpoint 2: GET /fhir/r4/Condition

Action: search-condition
Required Fields: organization_id, patient_id
Optional Fields: chart_id
Source Table: cl_problems
FHIR Profile: US Core Condition (http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition-problems-health-concerns)
Request:
Response (200): FHIR Bundle of Condition resources. Consent Gating: SUD-related conditions (ICD-10 F10–F19) are excluded if patient does not have active Part 2 consent (CL-11). Non-SUD conditions are always returned.

Endpoint 3: GET /fhir/r4/MedicationRequest

Action: search-medication-request
Required Fields: organization_id, patient_id
Source Table: cl_medications
FHIR Profile: US Core MedicationRequest (http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest)
Request:
Response (200): FHIR Bundle of MedicationRequest resources. Consent Gating: SUD medications (e.g., buprenorphine, methadone, naltrexone) are excluded without Part 2 consent.

Endpoint 4: GET /fhir/r4/Patient/:id/$everything

Action: patient-everything
Required Fields: organization_id, patient_id
Source Tables: pm_patients, cl_problems, cl_medications, cl_allergies, pm_appointments, cl_order_results
FHIR Profile: FHIR Bundle (type: searchset)
Request:
Response (200): FHIR Bundle containing all available resources for the patient: Patient, Condition(s), MedicationRequest(s), AllergyIntolerance(s), Encounter(s), Observation(s). Consent-gated per CL-11. Performance Note: For large charts, response may be paginated in future phases. Phase 1 returns all resources in a single bundle with withTimeout() protection.

Cross-Cutting Concerns (All CL-16 Endpoints)

Authentication: JWT required via Authorization: Bearer {token}. Validated by validateAuth().
Tenant Enforcement: organization_id required in every request. Verified via verifyOrgAccess() against pf_user_organizations. No cross-tenant data access.
RBAC: Caller must have cl.fhir.read permission (or equivalent clinical access).
Consent Enforcement (CL-11): Part 2 SUD data gated by checkSudConsent(). Fail-closed: if consent check fails, SUD data is excluded.
Rate Limiting: 100 req/min per organization for reads; 10 req/min for $everything. Enforced at Edge Function level.
Performance SLA: p95 < 500ms for single-resource reads; p95 < 2s for $everything.
Timeout: 25s per request via withTimeout().
Retry & Idempotency: All GET endpoints are idempotent. Clients may retry on 5xx/429 with exponential backoff.
Caching: No cache for live clinical data. cl_fhir_resources table available for pre-computed FHIR cache in future phases.
PII/PHI Logging Guidelines:
  • Allowed in logs: organization_id, patient_id (UUID), resource_type, record_count, exchange_type, status, correlation_id, consent_status (boolean), duration_ms
  • Prohibited in logs: Patient names, DOB, SSN, diagnosis codes, medication names, clinical note content, address details
  • Masking: No PHI fields are logged; only IDs and operational metadata
  • Retention: Exchange log entries in cl_data_exchange_log retained per org retention policy; Edge Function stdout/stderr logged for 7 days (Supabase default)
Audit Trail: Every API call writes one record to cl_data_exchange_log with: exchange_type, direction (outbound), resource_types, record_count, status, created_by. Example:

PM-49 API Contracts

Provider: PM-49 (Revenue Cycle Automation Rules Engine) Consumers: PM-49 edge functions (rcm-execute-rules) Status: 📝 Planned Integration Doc: PM-49-revenue-cycle-automation-rules-engine-INTEGRATION.md Spec Reference: PM-49 spec Purpose: Synchronous API contracts for PM-49 automated rule actions that interact with PM-09 (Payment Posting), PM-29 (Denial Management), and PM-45 (Collections).

PM-49 → PM-09: Post Adjustment

Callable Interface:
  • HTTP Endpoint: POST /rest/v1/rpc/pm_post_adjustment (SECURITY DEFINER RPC)
  • Alternative: Edge function at POST /functions/v1/pm-post-adjustment
  • Required Auth Scope: pm.adjustments.create
  • Expected Status Codes: 200 (success), 400 (validation error), 403 (forbidden), 404 (not found), 409 (duplicate), 500 (server error)
  • Idempotency Key Field: rule_execution_id (optional UUID)
Purpose: Auto-post small balance adjustments and contractual adjustments Request Schema:
Response Schema (200 OK):
Error Response (4xx/5xx):
Error Codes:
  • CLAIM_NOT_FOUND (404): Claim ID does not exist
  • INSUFFICIENT_BALANCE (400): Balance lower than adjustment amount
  • INVALID_ADJUSTMENT_CODE (400): Unrecognized adjustment code
  • ORG_MISMATCH (403): Claim does not belong to organization
  • DUPLICATE_REQUEST (409): Idempotent duplicate (returns existing adjustment_id in error_message)
Idempotency: Use rule_execution_id as idempotency key; duplicate requests return 409 with existing adjustment_id Authentication: Service role or JWT with pm.adjustments.create permission

PM-49 → PM-29: Trigger Denial Resubmission

Callable Interface:
  • HTTP Endpoint: POST /rest/v1/rpc/pm_trigger_denial_resubmission (SECURITY DEFINER RPC)
  • Alternative: Edge function at POST /functions/v1/pm-trigger-resubmission
  • Alternative Path: POST /pm/denials/{denial_id}/resubmit (RESTful route)
  • Required Auth Scope: pm.denials.resubmit
  • Expected Status Codes: 200 (success), 403 (forbidden), 404 (not found), 409 (already resubmitted), 422 (deadline passed), 500 (server error)
  • Idempotency Key Field: rule_execution_id (optional UUID)
Purpose: Auto-trigger denial re-submissions for correctable denial codes Request Schema:
Response Schema (200 OK):
Error Response (4xx/5xx):
Error Codes:
  • DENIAL_NOT_FOUND (404): Denial ID does not exist
  • FILING_DEADLINE_PASSED (422): Resubmission past filing deadline (resolved via PF-96)
  • ALREADY_RESUBMITTED (409): Denial already has pending resubmission
  • ORG_MISMATCH (403): Denial does not belong to organization
  • JURISDICTION_PROFILE_MISSING (500): Unable to resolve filing deadline via PF-96
Idempotency: Use rule_execution_id as idempotency key Filing Deadline Resolution: Uses PF-96 pf_resolve_jurisdiction_profile(org_id, site_id) to validate resubmission window. If profile unavailable, fails closed with JURISDICTION_PROFILE_MISSING error. Authentication: Service role or JWT with pm.denials.resubmit permission

PM-49 → PM-45: Transfer to Collections

Callable Interface:
  • HTTP Endpoint: POST /rest/v1/rpc/pm_transfer_to_collections (SECURITY DEFINER RPC)
  • Alternative: Edge function at POST /functions/v1/pm-transfer-collections
  • Alternative Path: POST /pm/collections/transfer (RESTful route)
  • Required Auth Scope: pm.collections.create
  • Expected Status Codes: 200 (success), 400 (invalid tier), 403 (forbidden), 404 (not found), 409 (already in collections), 422 (ineligible payer), 500 (server error)
  • Idempotency Key Field: rule_execution_id (optional UUID)
Purpose: Auto-transfer accounts to collections tiers based on aging rules Request Schema:
Response Schema (200 OK):
Error Response (4xx/5xx):
Error Codes:
  • ACCOUNT_NOT_FOUND (404): Account/claim ID does not exist
  • ALREADY_IN_COLLECTIONS (409): Account already assigned to collections tier
  • INELIGIBLE_PAYER (422): Payer type excluded from collections (e.g., Medicaid in some states)
  • ORG_MISMATCH (403): Account does not belong to organization
  • INVALID_TIER (400): Unrecognized collections tier
Idempotency: Use rule_execution_id as idempotency key Authentication: Service role or JWT with pm.collections.create permission

PM-49 Published Event: pm_rcm_rule_executed

Purpose: PM-11 dashboard consumes this event for RCM automation metrics Event Schema:
Consumers: PM-11 (Revenue Cycle Dashboard) Event Contract Reference: See EVENT_CONTRACTS.md for full event contract details

PF-63: Entra ID Deep Integration Edge Functions

Status: ✅ Implemented
Spec Reference: PF-63 (Entra ID Integration)
Last Updated: 2026-02-21

Edge Function 1: entra-employee-presence

Invocation: supabase.functions.invoke('entra-employee-presence', { body })
Provider: PF (Platform Foundation)
Consumer: HR employee views, dashboards
JWT Required: Yes
Actions: Security: JWT + verifyOrgAccess(). OOO message content redacted (PHI).
Performance SLA: p95 < 2s (single), < 5s (batch up to 650).
Rate Limit: Per-organization, delegated to Graph API limits.

Edge Function 2: entra-teams-notify

Invocation: supabase.functions.invoke('entra-teams-notify', { body })
Provider: PF (Platform Foundation)
Consumer: event-consumer, internal services
JWT Required: Yes (typically service-role)
Request:
Response: { success: true, correlationId }
Security: JWT + verifyOrgAccess(). HTML escaping on all template values.
Performance SLA: p95 < 3s.

Edge Function 3: entra-sharepoint-documents

Invocation: supabase.functions.invoke('entra-sharepoint-documents', { body })
Provider: PF (Platform Foundation)
Consumer: SharePoint document browser UI
JWT Required: Yes
Actions: Security: JWT + verifyOrgAccess(). Max upload: 50MB. Action parameter sanitized.
Performance SLA: p95 < 3s (list/search), < 10s (upload).

Edge Function 4: entra-teams-activity

Invocation: supabase.functions.invoke('entra-teams-activity', { body })
Provider: PF (Platform Foundation)
Consumer: Notification pipeline
JWT Required: Yes
Request:
Response: { success: true } or { success: true, skipped: true, reason: string }
Skip Conditions: Feature disabled, below priority threshold, no Entra ID for user.
Security: JWT + verifyOrgAccess(). Requires Azure Bot registration.
Performance SLA: p95 < 3s.

HR-34: Contractor 1099 Totals RPC

Provider: HR (Workforce) — HR-34 Consumer: HR-PAY-04 (Tax Forms), HR-34 UI (1099 dashboard) Status: ✅ Implemented Integration Doc: HR-34 Integration Database function: hr_get_contractor_1099_totals(p_organization_id uuid, p_tax_year integer) Returns: SETOF RECORD (contractor_id uuid, total_amount numeric, entry_count bigint) Purpose: Aggregates all approved (approval_status = 'approved') time entry amounts for contractors in a given organization and tax year. Used by HR-PAY-04 to produce 1099-NEC input data. Auth: authenticated role + hr_has_org_access(p_organization_id, auth.uid()) enforced inside the SECURITY DEFINER function. Callers must also hold hr.contractor.tax_id.read permission for tax ID display in the UI. Request (logical):
  • p_organization_id (UUID): Tenant context
  • p_tax_year (INTEGER): Calendar year to aggregate (e.g. 2025)
Response: One row per contractor with approved time entries in the given year. total_amount is the sum of hr_contractor_time_entries.amount where approval_status = 'approved'. entry_count is the count of such entries. Idempotency: Read-only function; re-runs return same results for same inputs. Hook: useContractor1099Totals(taxYear) in src/cores/hr/hooks/contractors/useContractor1099Totals.ts

HR-44 / FA-43: Proliant (ReadyPay) Company API — Outbound Integration

Provider: Proliant / ReadyPay Online “Company API” (externalhttps://www.readypayonline.com/CompanyAPI)
Consumer: Encore HR-44 (payroll engine + run orchestration) and FA-43 (GL bridge), via Supabase edge functions
Status: 📝 Planned
Spec References: HR-44 Proliant HCM Integration · FA-43 Payroll Journal Entry Auto-Generation
Full Integration Contract: HR-44-PROLIANT-READYPAY-INTEGRATION.md
Purpose: Outbound, synchronous request-response contract for the Proliant Company API. Proliant is the system-of-record payroll engine; Encore pushes inputs, pulls results, and drives the run. Detailed endpoint catalog, data flows, events, and operational/PHI rules live in the integration contract above — this section is the API-contract index entry. Authentication: OAuth 2.0 Client Credentials. Token URL https://www.readypayonline.com/CompanyAPI/identity/connect/token, scope Company. Client secret stored in the PF-76 credential vault; rotation via POST /utility/clientSecret. Bearer token cached in memory; never logged. Sandbox/prod via environment toggle. Tenant context: Every business endpoint is scoped …/Company/{companyId}/…. The companyIdorganization_id mapping is resolved and validated server-side; client-supplied companyId is never trusted. GET /Company lists companies available to the client. Surface (summary — see integration contract for full catalog): Envelope: responses wrapped in ResponseDataWrapper[…]; paged lists use PagedDataResult[…]. Eventing: No webhooks — inbound state is polled (scheduled edge functions). See the integration contract §6 for poll cadence, idempotency keys, pagination, and retry/backoff. Rate limits: UNKNOWN — not documented in the Proliant Swagger spec; to confirm with the vendor in Phase 0. Apply conservative client-side throttling + 429-aware backoff until confirmed.

PII/PHI Logging Guidelines (Proliant)

Open item: money-movement & filing ownership (tax deposits/filing + direct-deposit funding) is unconfirmed with Proliant and gates the disposition of HR-PAY-06/07/08 + FA-40. See integration contract §9 (gap #2).

CL-51-EN-02: ECDS / FHIR Da Vinci DEQM Digital Quality-Measure API

Status: 📝 Planned Spec Reference: CL-51-EN-02 (parent CL-51) Consumer: PM-77 (Payer Quality / Supplemental-Data Submission) Anchor: CL-PM-GR-CARE-QUALITY-GRANT-INTEGRATION.md CL-51-EN-02 adds the FHIR Da Vinci DEQM digital-measure surface beside the CL-51 engine’s existing QRDA I/III export, plus NCQA ECDS data-source tagging, aligned with CL-16 US Core / USCDI v3. This is the synchronous facade PM-77 calls to obtain measure artifacts for outbound payer submission; CL-51-EN-02 produces, PM-77 transmits.

Resources & Operations

Contract (platform-layer facade for PM-77)

  • Authorization: organization-scoped; caller (PM-77) must hold org access and cl.quality.read. SUD-derived MeasureReport content is DS4P-labeled (CL-63); redisclosure to a payer via PM-77 requires CL-11 Part 2 consent per receiving entity.
  • Idempotency / caching: MeasureReports are deterministic for a finalized period; cache by (organization_id, measure_id, period).
  • Relationship to events: the engine also emits cl_quality_measure_period_calculated (see EVENT_CONTRACTS.md) on period finalization; PM-77 may consume the event to know when a fresh MeasureReport is available, then call $collect-data / fetch the report. $submit-data itself is invoked by PM-77 against the payer’s DEQM endpoint — see EXTERNAL_INTEGRATIONS.md.

Next Review: 2026-06-30 (Quarterly)