Prior Authorization Testing: Validating CRD, DTR, and PAS End-to-End
Most prior authorization implementations pass ONC Inferno conformance suites in isolation and immediately fail in production. Inferno validates static schema conformance at single endpoints, whereas production prior authorization under
CMS-0057-F requires stateful cryptographic and clinical continuity across three distinct HL7 Da Vinci specifications: Coverage Requirements Discovery, Documentation Templates and Rules, and Prior Authorization Support. When a clinician signs an order in an EHR, state must survive a synchronous CDS Hook, a SMART on FHIR contextual launch, client-side Clinical Quality Language execution, and an asynchronous Utilization Management (UM) translation loop.
Validating this pipeline requires testing the exact handoffs where state drops, tokens expire, and CQL engines choke on unprofiled EHR data.
1. Test Harness Architecture and Container Bottlenecks
A production-grade validation environment cannot rely on public sandboxes, which strip out OAuth 2.0 signature verification and use static mock responses.
Deploy a local multi-container topology using the unified HL7 Da Vinci Nx monorepo workspaces: HL7-DaVinci/br-provider (port 8080) and HL7-DaVinci/br-payer (port 8081), which supersede the deprecated standalone dtr, prior-auth, and CDS-Library repositories.
Orchestrate these via docker-compose.yml alongside the FAST Security (UDAP Tiered OAuth) server and the local Ruby/Docker runners for the ONC Inferno suites (davinci-crd-test-kit, davinci-dtr-test-kit, and davinci-pas-test-kit).
Both br-provider and br-payer house a Java 17+ HAPI FHIR JPA Starter backend (server/) and a Bun/Node 22+ frontend (frontend/) backed by PostgreSQL.
Under default settings, executing an end-to-end CRD-to-DTR transition triggers immediate database lock contention and connection pool exhaustion. When br-payer processes a CRD hook and immediately receives a DTR $questionnaire-package request alongside EHR FHIR callbacks, HAPI FHIR hammers the hfj_resource, hfj_res_link, and hfj_spidx_token tables.
Modify server/src/main/resources/application.yaml in both workspaces and export VSAC_API_KEY (required by br-payer to expand external NLM ValueSets) before running automated test suites:
YAML spring: datasource: hikari: maximum-pool-size: 40 minimum-idle: 10 connection-timeout: 15000 jpa: properties: hibernate.jdbc.batch_size: 50 hibernate.order_inserts: true hibernate.order_updates: true hapi: fhir: reuse_cached_search_results_millis: -1 cql: use_embedded_libraries: true compiler_options: EnableLocators,DisableListDemotion,DisableListPromotion cors: allow_Credentials: true allowed_origin: - "*"
Setting reuse_cached_search_results_millis: -1 is mandatory during automated testing.
Without it, HAPI serves stale search bundles from hfj_search for up to 60 seconds, causing DTR CQL pre-population queries to miss newly written Observation or Condition resources injected by your test runner milliseconds earlier.
2. Phase 1: Stress-Testing CRD Prefetch Fallbacks and State Handoffs
CRD validation spans the appointment-book, order-select, and order-sign CDS Hooks. While inferno-framework/davinci-crd-test-kit verifies structural card returns, your integration tests must target three production failure vectors:
- The Prefetch Hydration Trap: EHRs routinely omit or partially populate requested prefetch templates (especially deep references like ServiceRequest.requester resolving to PractitionerRole and Organization) to protect clinical database performance. Configure your test harness to send a POST /cds-services/order-sign-crd payload with an empty “prefetch”: {} object. This forces your payer CRD service to extract the short-lived fhirAuthorization.access_token and execute synchronous GET callbacks against the EHR fhirServer endpoint.
- JWT Cryptographic Validation (jku and aud): Verify that your CRD endpoint rejects unsigned tokens, expired exp claims, and jku (JWK Set URL) headers pointing to untrusted domains. Confirm clock skew tolerance is configured to less than 60 seconds between the EHR identity provider and the payer gateway.
- The Latency Ceiling: Clinicians trigger order-sign synchronously. If your CRD service takes greater than 1500 milliseconds to fetch missing EHR resources, evaluate payer coverage rules, and return a response, production EHRs (including Epic and Oracle Health) will time out and silently drop the CDS card.
Validating the CRD-to-DTR Bridge
The critical output of CRD is not the human-readable card summary; it is the ext-coverage-information system action extension attached to the draft order resource.
Your test assertions must parse the returned systemActions array and validate it against HL7 FHIR invariants crd-ci-q1 (questionnaire requires doc-needed) and crd-ci-q3 (conditional codes require info-needed), while ensuring mandatory date (1..1) and coverage-assertion-id (1..1) sub-extensions exist:
JSON { "type": "update", "description": "Add coverage information to order", "resource": { "resourceType": "ServiceRequest", "id": "SleepStudyOrder-104", "extension": [ { "url": "http://hl7.org/fhir/us/davinci-crd/StructureDefinition/ext-coverage-information", "extension": [ { "url": "coverage", "valueReference": { "reference": "Coverage/cov-992" } }, { "url": "covered", "valueCode": "covered" }, { "url": "pa-needed", "valueCode": "auth-needed" }, { "url": "doc-needed", "valueCode": "clinical" }, { "url": "questionnaire", "valueCanonical": "http://example.org/fhir/Questionnaire/HomeOxygenTherapy|2.0.1" }, { "url": "date", "valueDate": "2026-11-03" }, { "url": "coverage-assertion-id", "valueString": "CRD-ASSERT-884920-A" } ] } ] }
Verify that both the SMART v2 fhirContext array (mandatory in DTR STU 2.0+) and legacy SMART v1 appContext inside the OAuth 2.0 token response carry the Coverage reference, order reference, and canonical Questionnaire URL into the DTR launch.
3. Phase 2: Validating DTR CQL Execution and Questionnaire State
When the clinician launches DTR, the app invokes POST /Questionnaire/$questionnaire-package.
Run both the Light EHR and Full EHR tracks in davinci-dtr-test-kit using preset patient pat015. Ensure your input Parameters payload satisfies invariant dtr-1: alongside the mandatory coverage parameter (1..1), at least one of order, questionnaire, or context must be present.
The payer server must return a dtr-qpackage-output-parameters resource containing a PackageBundle parameter (Bundle of type = “collection”) and an optional outcome.
3 Critical DTR Validation Checks
- ELM Compilation and FHIRHelpers Drift: Payers write clinical rules in .cql files inside br-payer/server/src/main/resources/library/ and compile them to Expression Logical Model (application/elm+json). If the payer compiles CQL using FHIRHelpers version 4.0.1, and the client’s JavaScript cql-execution engine only bundles 4.0.0, every clinical expression returns null without throwing an HTTP error. Write unit tests that inspect the base64-decoded Library.content.data payload inside PackageBundle to assert that zero expressions resolve to undefined when evaluated against known US Core patient bundles.
- Offline ValueSet Expansion: Inside the DTR execution environment, the CQL engine cannot make unauthenticated external calls to tx.fhir.org to resolve canonical ValueSet URLs. Assert that every ValueSet referenced in Library.dataRequirement is pre-expanded inside PackageBundle with an explicit ValueSet.expansion.contains array.
- Adaptive Mutation and information-origin Preservation (§spec-71): When resuming a session or stepping through an Adaptive Questionnaire (POST /Questionnaire/$next-question), the DTR client re-evaluates CQL. Per DTR STU 2.2.0, every answer carries the [http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/information-origin](http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/information-origin) extension with a source code (auto-client, auto-server, override, or manual). Test this loop by manually modifying a CQL-populated answer so its source mutates from auto-client to override. Assert that subsequent CQL re-execution updates untouched auto-client answers with fresh EHR data while strictly preserving the clinician’s override and manual entries.
4. Phase 3: Validating PAS Bundles, X12 278 Mapping, and Pended Subscriptions
Once the DTR QuestionnaireResponse reaches status = “completed”, the system transitions to PAS. Even under CMS-0057-F enforcement discretion for native FHIR-to-FHIR exchange, most payer core UM engines still translate payloads to X12 278 (005010X217) internally.
PAS Bundle Referential Integrity (Claim/$submit)
The POST /Claim/$submit payload must validate against PASRequestBundle, which mandates Bundle. identifier (1..1) and type = “collection”. Entry 0 must be the PASClaim.
All clinical evidence (QuestionnaireResponse, DocumentReference) must use the additionalInformation slice in Claim.supportingInfo (never freeFormMessage, which only accepts valueString for X12 MSG segments) and bind explicitly to the service line via Claim.item.informationSequence so X12 translators can populate the 2000F Service Level PWK segment:
JSON { "resourceType": "Bundle", "id": "pas-submission-bundle-01", "meta": { "profile": ["http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-pas-request-bundle"] }, "identifier": { "system": "http://example.org/provider-transaction-ids", "value": "TX-9948102-PAS" }, "type": "collection", "timestamp": "2026-11-03T14:22:00Z", "entry": [ { "fullUrl": "urn:uuid:a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d", "resource": { "resourceType": "Claim", "meta": { "profile": ["http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-claim"] }, "supportingInfo": [ { "sequence": 1, "category": { "coding": [ { "system": "http://hl7.org/fhir/us/davinci-pas/CodeSystem/PASSupportingInfoType", "code": "additionalInformation" } ] }, "valueReference": { "reference": "urn:uuid:f9e8d7c6-b5a4-4f3e-9d8c-7b6a5f4e3d2c" } } ], "item": [ { "sequence": 1, "informationSequence": [1], "productOrService": { "coding": [{ "system": "http://www.ama-assn.org/go/cpt", "code": "95810" }] } } ] } } ] }
X12 278 Translation & Pended Subscription Tests
- The TRN02 Truncation Bug: The X12 TRN02 (Trace Number) element has a hard ceiling of 30 alphanumeric characters. If the provider EHR sends a 36-character UUID in Claim.identifier.value, naive X12 translators truncate the final 6 characters, breaking correlation when the response returns.
- Asynchronous Pended Lifecycle (A4 to A1): Configure a test case in br-payer that returns a pended PASClaimResponse (reviewAction code A4) within the 15-second synchronous SLA, then validate both resolution paths:
- Register an R5 Backport Subscription listening to [http://hl7.org/fhir/us/davinci-pas/SubscriptionTopic/PASSubscriptionTopic](http://hl7.org/fhir/us/davinci-pas/SubscriptionTopic/PASSubscriptionTopic).
- Trigger an out-of-band UM transition from A4 to A1 (Certified in total) on br-payer.
- Assert that the provider webhook receives a PASResponseBundle containing a PASClaimResponse (profile-claimresponse) with reviewAction = “A1” and an assigned preAuthRef (HCR02).
- Simulate a dropped webhook and execute fallback polling via POST /Claim/$inquire. Verify that your parser handles the profile shift: $inquire returns a PASInquiryResponseBundle containing a PASClaimInquiryResponse (profile-claiminquiryresponse), not a PASClaimResponse.
5. End-to-End State Traceability Matrix
| Stage | Endpoint | State Identifier | Primary Failure Mode | Test Assertion |
| CRD Hook | POST /cds-services/order-sign | fhirAuthorization.access_token | Prefetch dropped; callback 401 | Strip prefetch; verify 200 OK |
| CRD Response | systemActions.resource | coverage-assertion-id | Fails crd-ci-q1 or date (1..1) | Run validator_cli.jar; check fhirContext |
| DTR Package | POST /$questionnaire-package | PackageBundle | Fails dtr-1; override overwritten | Mutate answer; verify override persists |
| PAS Submit | POST /Claim/$submit | Claim.identifier (TRN02) | Orphaned supportingInfo; 36-char UUID | Bind informationSequence; <=30 chars |
| PAS Pend | POST /Subscription & $inquire | preAuthRef (HCR02) | Webhook vs. $inquire profile mismatch | Validate PASClaimResponse vs. Inquiry |
Wire the Inferno suites into CI/CD via their CLI runners alongside the Playwright E2E harness in br-provider. Only when a single transaction survives from order-sign to a resolved A1 PASClaimResponse with zero validator_cli.jar errors is the pipeline ready for production EHR integration.
Validate Your Prior Authorization Workflow
Passing individual API tests can still leave gaps between CRD, DTR, and PAS. Work with CapMinds to assess state handoffs, questionnaire behavior, and authorization response handling across your integration.



