Skip to main content

Claims Service — Security Model

Status: populated Owner: TBD Last updated: 2026-04-18 Companion: SERVICE_OVERVIEW · Service Template · 02 DDD

Authentication and Authorization

All API requests must present a valid JWT issued by the tenant's Keycloak realm. The tenant_id claim in the JWT is validated against the X-Tenant-ID header and used to set the Postgres app.tenant_id session variable for RLS enforcement.

RBAC Matrix

RoleCreate ClaimSubmit ClaimView ClaimCreate CoverageVerify EligibilityProcess ERAFile AppealAdmin
billing-staffYesYesYesYesYesNoYesNo
billing-managerYesYesYesYesYesYesYesNo
registration-staffNoNoNoYesYesNoNoNo
clinicianNoNoRead-ownNoNoNoNoNo
patient-portalNoNoRead-own EOBNoNoNoNoNo
billing-service (service account)YesYesYesNoNoNoNoNo
platform-adminNoNoYes (audit)NoNoNoNoYes

Notes:

  • patient-portal role may only read ExplanationOfBenefit and Coverage records for the authenticated patient. No claim detail is exposed.
  • Service accounts use client credentials OAuth2 flow with sub claim identifying the service.

Module Entitlement

Write operations (POST, PATCH, PUT, DELETE) on all claims and coverage endpoints return 403 MODULE_NOT_LICENSED when the tenant lacks the ehr.claims entitlement in their license record. Read operations (GET) for existing data are permitted to avoid data loss on license expiry.

Tenant Isolation via RLS

All tables (claims, coverages, eligibility_transactions, prior_authorizations, remittances, denial_cases) have:

  • tenant_id TEXT NOT NULL column
  • Row-Level Security policy: USING (tenant_id = current_setting('app.tenant_id'))
  • app.tenant_id is set at connection/transaction start from the validated JWT claim

Cross-tenant access is architecturally impossible via the application layer. Direct DB access is restricted to the service's dedicated Postgres role.

Encryption

Data ClassAt RestIn Transit
Claim financial dataAES-256 (Postgres encrypted volume)TLS 1.3
Coverage / PIIAES-256TLS 1.3
Raw EDI payloads (X12 835/837)AES-256TLS 1.3
Payer API credentialsVault (HashiCorp) — never in application configTLS 1.3
NATS event payloadsTLS 1.3 in transit; encrypted at rest on JetStream storageTLS 1.3

Payer credentials (API keys, EDI sender IDs, clearinghouse credentials) are stored in HashiCorp Vault and injected at runtime via the Vault agent sidecar.

Audit Events

All PHI-touching mutations emit an audit record to audit-service.

OperationAudit Event
Create claimclaims.claim.created with actor, patientId, claimId
Submit claimclaims.claim.submitted with channel, clearinghouseRef
View claim (billing staff)claims.claim.read with actorId
View EOB (patient portal)claims.eob.read with patientId, actorId
Create coverageclaims.coverage.created with actorId, patientId
Update coverageclaims.coverage.updated with changed fields
Process ERAclaims.remittance.applied with remittanceId, claimsAffected count
File appealclaims.denial.appealed with actorId, denialCode

Audit records are immutable and retained for 7 years per healthcare regulatory requirements.

EDI Security

  • X12 837/835 files transmitted over SFTP+TLS or HTTPS to clearinghouse
  • Payer REST API calls use OAuth2 client credentials or API key stored in Vault
  • Inbound ERA payloads validated for structure before processing (reject malformed files)
  • No clearinghouse credentials stored in application database or source control

GDPR / Data Subject Rights

The claims-service holds financial PHI. Data subject access requests (export) and erasure requests are handled via the platform-admin service. Claims data cannot be erased if it is subject to healthcare retention requirements (minimum 7 years); a legal hold flag prevents deletion and instead marks records as restricted.

Penetration Testing

A security penetration test targeting:

  1. Cross-tenant RLS bypass attempts
  2. RBAC privilege escalation
  3. Payer credential exposure
  4. EDI payload injection

is required before production launch of the ehr.claims licensed module.