Skip to main content

Documentation Index

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

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

Clearinghouse Integration — Integration

Version: 2.0.0
Status: 📝 Planned
Last Updated: 2026-03-27
Spec (Phase 1): PM-15 Clearinghouse Integration
Spec (Phase 2): PM-15-P2 Production Clearinghouse Transport
Constitution Reference: Section 1.3 (Integration Patterns), Section 2.1 (Integration points documented in /docs/architecture/integrations/)

Quick Reference

I need to…PatternLocation
Submit a claim batch (SFTP)ClearinghouseAdapter.submitBatchFile()supabase/functions/_shared/transport/ (Phase 2a)
Submit a single claim (REST API)ClearinghouseAdapter.submitClaim()src/cores/pm/types/clearinghouse.ts
Retrieve ERA/835 from clearinghouseClearinghouseAdapter.fetchRemittance()Same adapter interface
Check eligibility via 270/271ClearinghouseAdapter.checkEligibility()Same adapter interface
Generate X12 837P from claimsgenerate837P(claims, config)supabase/functions/_shared/x12/generate-837p.ts (Phase 2b)
Parse 835 ERA fileparse835(x12Content)supabase/functions/_shared/x12/parse-835.ts (Phase 2b)
Configure clearinghouse connectionpm_clearinghouse_config tablePM-15-T1 migration
Schedule batch submissionpm_clearinghouse_config.submission_schedulePhase 2a migration
View connection healthpm_clearinghouse_config.health_statusPhase 2c; gated by pm.clearinghouse.view
View transaction audit logpm_transaction_log (append-only)RLS: SELECT only

Decision Tree

  • Need to submit a claim electronically? → Use PM-15 ClearinghouseAdapter.submitClaim() via PM-08 claim workflow. → Credentials resolved from vault refs at runtime; never stored in code.
  • Need to process an ERA/835? → Use ClearinghouseAdapter.fetchRemittance() → parse → feed PM-09 payment posting. → 835 SVC segments map to pm_claim_lines.
  • Need to check eligibility in real-time? → Use ClearinghouseAdapter.checkEligibility() for 270/271 (PM-02 integration).

Common Mistakes

SymptomFix
Credentials in code or migrationStore as vault refs in pm_clearinghouse_config; resolve at runtime in edge function
Missing org filter on batch queriesAdd .eq('organization_id', orgId) to all mutations
Editing pm_transaction_log rowsTable is append-only; RLS denies UPDATE/DELETE
Direct import from PM-08 hooksUse adapter interface; PM-15 is invoked by PM-08, not the reverse

Pre-Flight Checklist

  • PM-08 and PM-09 schemas exist (claims, claim_lines, payments)
  • Vault refs configured for clearinghouse credentials
  • RLS enabled with FORCE on all four tables
  • UPDATE policies include WITH CHECK
  • No secrets in code, migrations, or docs

Overview

PM-15 provides clearinghouse connectivity for ANSI X12 transactions (837P/I, 835, 270/271, 276/277, 278, 999/277CA). It defines the data model (pm_clearinghouse_config with clearinghouse_provider, sender_id, receiver_id, connection_type, vault refs, host/port/paths or api_endpoint; pm_transaction_batches, pm_transaction_log, pm_payer_enrollments), RLS, and the generic ClearinghouseAdapter interface. Implementations (Waystar first, then Availity, Change Healthcare, etc.) are configured per organization; credentials are stored in vault only.

Integration Points

DependencyPatternPurpose
PM-08Data / APIClaim submission: PM-08 builds claim and invokes adapter submitClaim(); 999/277CA and line-level rejections feed PM-08 denial workflow.
PM-09Data / APIERA retrieval: adapter fetchRemittance() returns 835; SVC segments map to pm_claim_lines; line-level posting.
PM-02APIEligibility: adapter checkEligibility() for 270/271 (when supported by clearinghouse).
PM-10APIPrior auth: adapter submitPriorAuth() for 278 (when supported).
External clearinghouseAPI / SFTPWaystar (and future adapters): REST or sFTP per pm_clearinghouse_config.connection_type; credentials via vault refs.

API / Adapter Contract

Clearinghouse adapter interface is defined in X12 EDI Technical Design.

Phase 1 methods (adapter interface)

MethodDescription
submitClaim(claimId, payload)837P/I REST per-claim submission; returns trace/reference
fetchRemittance(config)Retrieve 835 file; returns raw X12 content for Phase 2b parser
checkEligibility(config, request)270/271 eligibility inquiry/response (PM-02)
checkClaimStatus(config, traceId)276/277 claim status inquiry/response
submitPriorAuth(config, request)278 prior auth (PM-10; Phase 3)
validateCredentials(config)SFTP handshake or API ping for health check
getSubmissionWindow()Submission window per clearinghouse

Phase 2a additions (transport layer)

MethodDescriptionLocation
submitBatchFile(config, x12Content, fileName)SFTP batch file upload; returns remote file pathsupabase/functions/_shared/transport/sftp-transport.ts
downloadFile(config, remoteFileName)SFTP file download; returns raw file contentSame
Waystar OAuth2 tokenAcquired and cached per invocation via waystar-rest-transport.tssupabase/functions/_shared/transport/waystar-rest-transport.ts
Retry logicExponential backoff (2s, 4s, 8s); max 3 retries; dead-letter → clearinghouse_batch_error eventsupabase/functions/_shared/transport/retry.ts

Phase 2b additions (X12 utilities — shared, pure TypeScript)

UtilityInputOutputLocation
generate837P(claims, config)pm_claims + pm_claim_lines + COB (PM-30)X12 837P string_shared/x12/generate-837p.ts
parse835(x12Content)Raw 835 X12 stringpm_era_files metadata + CLP/SVC payment data_shared/x12/parse-835.ts
generate270(config, request)Eligibility inquiry paramsX12 270 string_shared/x12/generate-270.ts
parse271(x12Content)Raw 271 X12 stringStructured eligibility response_shared/x12/parse-271.ts
parse999(x12Content)Raw 999 X12 stringFunctional ack status + error codes_shared/x12/parse-999.ts
parse277CA(x12Content)Raw 277CA X12 stringLine-level rejection statuses_shared/x12/parse-277ca.ts
buildEnvelope(params)ISA/GS/ST paramsX12 envelope segments_shared/x12/envelope.ts

835 parser → PM-09 interface

The Phase 2b 835 parser writes to PM-09-owned tables:
  1. Creates pm_era_files record: file_name, received_date, payer_id (from GS/TRN), check_eft_number, total_paid, claim_count, status = 'received'
  2. Emits clearinghouse_era_received event (payload below) for PM-09 to pick up
  3. PM-09 payment posting pipeline consumes the event and creates pm_payments + pm_payment_applications
// clearinghouse_era_received event payload
interface ClearinghouseEraReceivedPayload {
  era_file_id: string;        // pm_era_files.id just created
  organization_id: string;
  batch_id: string;           // pm_transaction_batches.id
  claim_count: number;
  total_paid: number;
}
Downstream consumers must resolve the Supabase storage object path from era_file_id.

Pattern Library

  • Adapter abstraction pattern: PM workflows call ClearinghouseAdapter methods, not vendor SDKs directly.
  • Vault reference pattern: runtime credential resolution from pm_clearinghouse_config, never plaintext secrets in code.
  • Append-only ledger pattern: pm_transaction_log is immutable and used for traceability.
// Example: PM workflow invocation using tenant-scoped config + adapter
const { data: config } = await supabase
  .from('pm_clearinghouse_config')
  .select('*')
  .eq('organization_id', orgId)
  .single();

const credentials = await resolveVaultCredentials(config); // resolves vault refs at runtime

await clearinghouseAdapter.submitClaim(claimId, payload, { credentials });
await clearinghouseAdapter.fetchRemittance({ organizationId: orgId, credentials });
await clearinghouseAdapter.checkEligibility({ organizationId: orgId, credentials }, eligibilityRequest);

Event Contracts

Events are defined in EVENT_CONTRACTS.md. Phase 2 adds the following:
EventEmitterConsumersPayload
clearinghouse_batch_submittedclearinghouse-submit edge fnPM-08 (batch status tracking){ batch_id, organization_id, claim_count, transport_type: 'sftp' | 'api' }
clearinghouse_batch_errorclearinghouse-submit (dead-letter after 3 retries)PM-08 (denial routing), PF-10 (billing admin alert){ batch_id, organization_id, transport_error: string, retry_count: number }
clearinghouse_era_receivedclearinghouse-retrieve after creating pm_era_filesPM-09 (payment posting){ era_file_id, organization_id, batch_id, claim_count, total_paid }
clearinghouse_health_changedclearinghouse-health-check on status transitionPF-10 (alert on down/degraded; throttled recovery on 2× healthy){ config_id, organization_id, old_status, new_status, checked_at }
Note: clearinghouse_batch_submitted and clearinghouse_batch_error were defined in Phase 1 (PM-15 Errata F-5). clearinghouse_era_received and clearinghouse_health_changed are new in Phase 2.

Phase 2c: Connection Health Monitoring

The clearinghouse-health-check edge function runs every 30 minutes (Supabase cron) and performs a connection test (SFTP handshake via validateCredentials() or Waystar API ping):
  • On status change → updates pm_clearinghouse_config.health_status and last_health_check_at
  • On down or degraded → emits clearinghouse_health_changed immediately; PF-10 alert fired
  • On recovery to healthy → requires 2 consecutive healthy checks before emitting recovery event (anti-flap throttle)
  • Health badge in UI is gated by pm.clearinghouse.view

Security & Tenant Isolation

  • All clearinghouse tables are scoped by organization_id; RLS and SECURITY DEFINER helpers enforce isolation.
  • Credentials stored in vault only; config holds vault refs; resolution at runtime in edge function or backend.
  • PHI in 837/835: encrypt in transit (TLS); audit access to batch and transaction log.

References