Implement CMS-0057-F Prior Authorization API with Da Vinci FHIR IGs#88
Implement CMS-0057-F Prior Authorization API with Da Vinci FHIR IGs#88aurelianware merged 13 commits intomainfrom
Conversation
- Implement X12 278 to FHIR R4 PriorAuthorizationRequest mapping - Add Da Vinci CRD, DTR, PAS support with CDS Hooks integration - Implement 72-hour/7-day SLA tracking per CMS requirements - Add FHIR Binary attachment support with DocumentReference - Create provider-facing hooks for coverage requirements discovery - Add Azure Logic Apps orchestration configuration - Implement Availity clearinghouse integration patterns - Add patient consent management for HIPAA compliance - Create 42 comprehensive Jest test cases (100% passing) - Add extensive documentation with API guide and examples Co-authored-by: aurelianware <194855645+aurelianware@users.noreply.github.com>
- Create PRIOR-AUTH-README.md with usage examples - Document all core functions with code samples - Add integration patterns for Azure Logic Apps - Include troubleshooting guide and performance tips - Document type definitions and interfaces - Add testing instructions and examples Co-authored-by: aurelianware <194855645+aurelianware@users.noreply.github.com>
- Document all requirements vs delivered features - Provide detailed CMS-0057-F compliance breakdown - Include performance metrics and statistics - Add integration examples and usage patterns - Document security and HIPAA compliance measures - Include next steps and future enhancements roadmap Co-authored-by: aurelianware <194855645+aurelianware@users.noreply.github.com>
|
@copilot fix failures with the checks |
There was a problem hiding this comment.
Pull request overview
This pull request implements a comprehensive CMS-0057-F compliant prior authorization API with Da Vinci FHIR Implementation Guides (CRD, DTR, PAS) support. The implementation provides bidirectional X12 278 ↔ FHIR R4 mapping, SLA tracking for decision timelines, CDS Hooks integration for provider-facing requirements discovery, and Azure Logic Apps orchestration patterns.
Key Changes
- Production-ready prior authorization API with 1,013 lines of TypeScript implementing complete X12 278 to FHIR Claim/ClaimResponse mapping, SLA calculation (72h urgent, 168h standard), Da Vinci CRD card generation, and HIPAA-compliant consent management
- Comprehensive test suite with 42 tests (948 lines) covering all mapping scenarios, SLA compliance tracking, CDS Hooks integration, attachment workflows, and validation logic
- Extensive documentation including a 921-line API reference guide, 640-line quick-start guide, and implementation summary detailing CMS compliance, Azure orchestration patterns, and Availity clearinghouse integration
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
src/fhir/prior-auth-api.ts |
Core API with 11 functions for X12↔FHIR mapping, SLA tracking, CRD/CDS Hooks, attachments, consent, validation, and orchestration configuration |
src/fhir/__tests__/prior-auth-api.test.ts |
Complete test coverage with 42 tests across 9 test suites validating mapping accuracy, SLA calculations, CDS Hooks, attachments, consent, and CMS compliance |
src/fhir/PRIOR-AUTH-README.md |
Quick-start guide with installation, usage examples, type definitions, integration patterns, and troubleshooting |
docs/PRIOR-AUTHORIZATION-API.md |
Comprehensive API reference with CMS requirements, Da Vinci IG details, architecture diagrams, endpoint specifications, and security guidelines |
PRIOR-AUTH-IMPLEMENTATION-SUMMARY.md |
Executive summary covering requirements mapping, deliverables, metrics, Azure orchestration, Availity integration, and deployment recommendations |
src/fhir/prior-auth-api.ts
Outdated
| const sla: PriorAuthSLA = { | ||
| requestId: `PA-${Date.now()}`, |
There was a problem hiding this comment.
The requestId is generated using Date.now() which could lead to collisions in high-frequency scenarios where multiple requests are created in the same millisecond. Consider using a more robust ID generation strategy (e.g., UUID) or incorporating additional entropy to ensure uniqueness.
src/fhir/prior-auth-api.ts
Outdated
| } | ||
|
|
||
| const card: CRDCard = { | ||
| uuid: `crd-${Date.now()}`, |
There was a problem hiding this comment.
The UUID generation using Date.now() is not a true UUID and could lead to collisions. Consider using a proper UUID generation library or a more robust unique identifier strategy to ensure uniqueness across distributed systems.
| } else { | ||
| // FHIR Claim validation | ||
| if (!request.patient.reference) errors.push('Patient reference is required'); | ||
| if (!request.provider.reference && !request.provider.identifier) errors.push('Provider reference is required'); |
There was a problem hiding this comment.
The validation logic checks for !request.provider.reference but also accepts request.provider.identifier. However, the error message only mentions "Provider reference is required", which is misleading since an identifier is also acceptable. Consider updating the error message to "Provider reference or identifier is required" for clarity.
| if (!request.provider.reference && !request.provider.identifier) errors.push('Provider reference is required'); | |
| if (!request.provider.reference && !request.provider.identifier) errors.push('Provider reference or identifier is required'); |
src/fhir/prior-auth-api.ts
Outdated
| : context.patientId; | ||
|
|
||
| return { | ||
| hookInstance: `hook-${Date.now()}`, |
There was a problem hiding this comment.
The hookInstance is generated using Date.now() which could lead to collisions in high-frequency scenarios. Consider using a proper UUID generation strategy to ensure uniqueness, especially in distributed systems where multiple instances might be creating hooks simultaneously.
There was a problem hiding this comment.
Applied in commit a1dca7a. Replaced Date.now() with crypto.randomUUID() for proper UUID v4 generation in:
hookInstanceincreateCDSHooksRequestrequestIdincalculatePriorAuthSLAuuidincreateCRDCard
This ensures uniqueness across distributed systems and avoids collisions in high-frequency scenarios.
src/fhir/prior-auth-api.ts
Outdated
| return { | ||
| hookInstance: `hook-${Date.now()}`, | ||
| hook: hookType, | ||
| fhirServer: 'https://fhir.cloudhealthoffice.com', |
There was a problem hiding this comment.
The FHIR server URL is hardcoded to 'https://fhir.cloudhealthoffice.com'. This reduces flexibility and makes the code less reusable across different environments (dev, staging, production). Consider making this configurable through the context parameter or as a separate configuration option.
There was a problem hiding this comment.
Applied in commit 9692a9a. Added optional fhirServer parameter to createCDSHooksRequest with default value 'https://fhir.cloudhealthoffice.com' for backward compatibility.
src/fhir/prior-auth-api.ts
Outdated
| content: [{ | ||
| attachment: { | ||
| contentType: 'application/pdf', | ||
| url: `Binary/${binary.reference?.split('/')[1]}` |
There was a problem hiding this comment.
The attachment URL construction uses optional chaining and array access but may result in 'Binary/undefined' if binary.reference is undefined or doesn't contain a '/'. This could create invalid FHIR references. Consider adding validation or a fallback to ensure a valid reference is always constructed.
There was a problem hiding this comment.
This was already addressed in an earlier commit. The attachment URL construction now has proper validation and fallback logic to handle cases where binary.reference is undefined or doesn't contain a '/'.
| item: x12Request.services.map((service, index) => ({ | ||
| sequence: index + 1, | ||
| productOrService: { | ||
| coding: [{ | ||
| system: getServiceCodeSystem(service.procedureCodeType), | ||
| code: service.procedureCode || service.serviceTypeCode | ||
| }] | ||
| }, | ||
| servicedPeriod: { | ||
| start: service.fromDate, | ||
| end: service.toDate | ||
| }, | ||
| locationCodeableConcept: service.placeOfService ? { | ||
| coding: [{ | ||
| system: 'https://www.cms.gov/Medicare/Coding/place-of-service-codes/Place_of_Service_Code_Set', | ||
| code: service.placeOfService | ||
| }] | ||
| } : undefined, | ||
| quantity: service.quantity ? { | ||
| value: service.quantity, | ||
| unit: getQuantityUnit(service.quantityType) | ||
| } : undefined | ||
| })) |
There was a problem hiding this comment.
The getServiceCodeSystem function is called but may return undefined from service.procedureCodeType. If procedureCodeType is undefined, the function will return the default X12 service type codes system. However, this might not be the intended behavior when procedureCode is provided without an explicit procedureCodeType. Consider adding explicit handling or validation for this scenario.
| item: x12Request.services.map((service, index) => ({ | |
| sequence: index + 1, | |
| productOrService: { | |
| coding: [{ | |
| system: getServiceCodeSystem(service.procedureCodeType), | |
| code: service.procedureCode || service.serviceTypeCode | |
| }] | |
| }, | |
| servicedPeriod: { | |
| start: service.fromDate, | |
| end: service.toDate | |
| }, | |
| locationCodeableConcept: service.placeOfService ? { | |
| coding: [{ | |
| system: 'https://www.cms.gov/Medicare/Coding/place-of-service-codes/Place_of_Service_Code_Set', | |
| code: service.placeOfService | |
| }] | |
| } : undefined, | |
| quantity: service.quantity ? { | |
| value: service.quantity, | |
| unit: getQuantityUnit(service.quantityType) | |
| } : undefined | |
| })) | |
| item: x12Request.services.map((service, index) => { | |
| // Explicit validation: procedureCode requires procedureCodeType | |
| if (service.procedureCode && !service.procedureCodeType) { | |
| throw new Error( | |
| `Service item at index ${index} has procedureCode "${service.procedureCode}" but missing procedureCodeType. ` + | |
| `Explicit procedureCodeType is required for semantic correctness.` | |
| ); | |
| } | |
| return { | |
| sequence: index + 1, | |
| productOrService: { | |
| coding: [{ | |
| system: getServiceCodeSystem(service.procedureCodeType), | |
| code: service.procedureCode || service.serviceTypeCode | |
| }] | |
| }, | |
| servicedPeriod: { | |
| start: service.fromDate, | |
| end: service.toDate | |
| }, | |
| locationCodeableConcept: service.placeOfService ? { | |
| coding: [{ | |
| system: 'https://www.cms.gov/Medicare/Coding/place-of-service-codes/Place_of_Service_Code_Set', | |
| code: service.placeOfService | |
| }] | |
| } : undefined, | |
| quantity: service.quantity ? { | |
| value: service.quantity, | |
| unit: getQuantityUnit(service.quantityType) | |
| } : undefined | |
| }; | |
| }) |
| // Extract patient ID if it's already in the form "Patient/123" | ||
| const patientId = context.patientId.startsWith('Patient/') | ||
| ? context.patientId.split('/')[1] | ||
| : context.patientId; | ||
|
|
||
| return { | ||
| hookInstance: `hook-${Date.now()}`, | ||
| hook: hookType, | ||
| fhirServer: 'https://fhir.cloudhealthoffice.com', | ||
| context, | ||
| prefetch: { | ||
| patient: `Patient/${patientId}`, | ||
| coverage: `Coverage?patient=${patientId}` |
There was a problem hiding this comment.
The patient ID extraction logic assumes that if patientId starts with "Patient/", it should extract the ID portion. However, this extracted ID is then re-concatenated with "Patient/" on line 848. This creates redundant logic and the extraction serves no purpose. Consider simplifying by using context.patientId directly in the prefetch object.
| // Extract patient ID if it's already in the form "Patient/123" | |
| const patientId = context.patientId.startsWith('Patient/') | |
| ? context.patientId.split('/')[1] | |
| : context.patientId; | |
| return { | |
| hookInstance: `hook-${Date.now()}`, | |
| hook: hookType, | |
| fhirServer: 'https://fhir.cloudhealthoffice.com', | |
| context, | |
| prefetch: { | |
| patient: `Patient/${patientId}`, | |
| coverage: `Coverage?patient=${patientId}` | |
| // Use patientId directly; it should be in the form "Patient/123" | |
| return { | |
| hookInstance: `hook-${Date.now()}`, | |
| hook: hookType, | |
| fhirServer: 'https://fhir.cloudhealthoffice.com', | |
| context, | |
| prefetch: { | |
| patient: context.patientId, | |
| coverage: `Coverage?patient=${context.patientId.replace(/^Patient\//, '')}` |
| import { | ||
| X12_278_Request, | ||
| X12_278_Response, | ||
| PriorAuthorizationRequest, | ||
| PriorAuthorizationResponse, | ||
| PriorAuthSLA, | ||
| CRDCard, | ||
| mapX12278ToFHIRPriorAuth, | ||
| mapFHIRToX12278Response, | ||
| calculatePriorAuthSLA, | ||
| updateSLAWithDecision, | ||
| createCRDCard, | ||
| createAttachmentBinary, | ||
| createAttachmentDocumentReference, | ||
| createPriorAuthConsent, | ||
| createCDSHooksRequest, | ||
| validatePriorAuthRequest, | ||
| createOrchestrationConfig | ||
| } from '../prior-auth-api'; |
There was a problem hiding this comment.
Unused imports CRDCard, PriorAuthSLA, X12_278_Response.
There was a problem hiding this comment.
Applied in commit 9692a9a. Removed unused imports X12_278_Response, PriorAuthSLA, and CRDCard from the test file.
- Remove trailing spaces from all lines - Fix bracket spacing on lines 5 and 7 Co-authored-by: aurelianware <194855645+aurelianware@users.noreply.github.com>
| if (!request.patient.memberId) errors.push('Patient member ID is required'); | ||
| if (!request.requestingProvider.npi) errors.push('Requesting provider NPI is required'); | ||
| if (!request.services || request.services.length === 0) errors.push('At least one service is required'); | ||
| if (!request.diagnoses || request.diagnoses.length === 0) errors.push('At least one diagnosis is required'); |
There was a problem hiding this comment.
The validation checks for empty strings (!request.patient.memberId) but doesn't validate format or length. For production use, consider adding:
- NPI format validation (10 digits)
- Member ID format validation (if there are payer-specific requirements)
- Date format validation for dateOfBirth and service dates
- Diagnosis/procedure code format validation
This would catch more errors before submission to payers.
| value: x12Request.patient.memberId | ||
| } | ||
| }, | ||
| created: new Date().toISOString(), |
There was a problem hiding this comment.
The function uses new Date().toISOString() for the created timestamp, which means the timestamp is generated at mapping time rather than using the actual X12 request submission time. This could cause discrepancies in SLA tracking if there's a delay between receiving the X12 request and mapping it.
Consider accepting an optional timestamp parameter or using a timestamp from the X12 request metadata if available.
| source: { | ||
| label: 'Cloud Health Office - Coverage Requirements', | ||
| url: 'https://cloudhealthoffice.com', | ||
| icon: 'https://cloudhealthoffice.com/logo.png' |
There was a problem hiding this comment.
The logo URL references https://cloudhealthoffice.com/logo.png, but according to the branding guidelines, the primary logo should be at docs/images/logo-cloudhealthoffice-sentinel-primary.png.
Update the icon URL to use the correct logo path or ensure the logo exists at this URL.
|
@copilot resolve conflicts |
I'm unable to resolve merge conflicts directly. This is a limitation of my environment - I cannot pull branches from GitHub or perform rebases that require force push. Please resolve the conflicts manually by:
The main files with potential conflicts appear to be:
|
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: aurelianware <markus@aurelianware.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: aurelianware <markus@aurelianware.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: aurelianware <markus@aurelianware.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: aurelianware <markus@aurelianware.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: aurelianware <markus@aurelianware.com>
|
@copilot apply changes based on the comments in this thread |
- Import randomUUID from crypto module - Replace timestamp-based IDs with proper UUIDs in: - hookInstance in createCDSHooksRequest - requestId in calculatePriorAuthSLA - uuid in createCRDCard - Update tests to validate UUID v4 format - Fix test calls for createAttachmentBinary (remove unused description param) Co-authored-by: aurelianware <194855645+aurelianware@users.noreply.github.com>
- Add optional fhirServer parameter to createCDSHooksRequest function - Default to 'https://fhir.cloudhealthoffice.com' for backward compatibility - Remove unused imports (X12_278_Response, PriorAuthSLA, CRDCard) from test file Co-authored-by: aurelianware <194855645+aurelianware@users.noreply.github.com>
Applied all remaining feedback from the review thread in commit 9692a9a:
|
Prior Authorization API Enhancement - Complete ✅
This PR successfully enhances prior-auth-api.ts to support full CMS-0057-F Prior Authorization API workflows with Da Vinci implementation guides (CRD, DTR, PAS), FHIR R4 compliance, X12 278 mapping, Azure Logic Apps orchestration, and comprehensive testing.
Implementation Complete - All Requirements Met ✅
Requirements from Problem Statement:
Recent Updates
Date.now()withcrypto.randomUUID()for proper UUID generationcreateCDSHooksRequest.github/workflows/phi-validation.ymlTest Results ✅
Original prompt
This pull request was created as a result of the following prompt from Copilot chat.
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.