54 min readContent generation

Implementation Guide: Draft prior authorization letters for insurance carriers

Step-by-step implementation guide for deploying AI to draft prior authorization letters for insurance carriers for Healthcare clients.

Hardware Procurement

HIPAA-Compliant Business Workstation

DellOptiPlex 7020 Micro (i5-14500T, 16GB DDR5, 512GB NVMe SSD, Windows 11 Pro)Qty: 5

$950 per unit MSP cost / $1,300 suggested resale

Staff workstations for PA workflow. Windows 11 Pro required for BitLocker full-disk encryption. 16GB RAM supports browser-based SaaS tools, EHR client, and AI assistant concurrently. One per PA-handling staff member plus one clinician review station.

Privacy Screen Filter 24-inch

3M3M Gold Privacy Filter GF240W9B (24-inch widescreen 16:9)Qty: 5

$45 per unit MSP cost / $75 suggested resale

HIPAA physical safeguard. Prevents shoulder-surfing of PHI displayed during PA letter drafting. Required for any workstation in a shared or semi-public area of the practice.

Next-Generation Firewall

FortinetFortiGate 60F (FG-60F) with FortiGuard UTM BundleQty: 1

$900 hardware + $450/year UTM subscription MSP cost / $1,400 hardware + $700/year suggested resale

NGFW with IDS/IPS, TLS inspection, application control, and VLAN support. Segments clinical network (PHI traffic to Azure OpenAI, EHR systems) from guest Wi-Fi and IoT devices. Required for HIPAA technical safeguard compliance.

Managed Wi-Fi Access Point (VLAN-capable)

UbiquitiUniFi U6 Pro (U6-Pro)Qty: 2

$150 per unit MSP cost / $250 suggested resale

Enterprise-grade Wi-Fi with VLAN support to separate clinical, administrative, and guest networks. Two APs cover a typical 2,000–5,000 sq ft practice. Managed via UniFi Network Controller hosted on-site or in UniFi Cloud.

HIPAA-Compliant Backup Appliance

Datto (Kaseya)Datto SIRIS 5 S5-1 (1TB usable)Qty: 1

$199/month MSP cost / $350/month suggested resale

Image-based backup with instant virtualization for disaster recovery. Backs up EHR server, audit logs, PA letter archives, and prompt template databases. HIPAA-compliant cloud replication included. Essential for HIPAA contingency planning requirements.

UPS Battery Backup

APCAPC Smart-UPS SMT1500RM2UC (1500VA/1000W, 2U rackmount, SmartConnect)Qty: 1

$650 MSP cost / $900 suggested resale

Protects firewall, switch, and backup appliance from power events. SmartConnect enables remote monitoring via APC cloud portal. Ensures PA workflow continuity during power fluctuations common in older medical office buildings.

Software Procurement

Doximity GPT (DoxGPT)

DoximityDoxGPT

$0/month

Phase 1 quick-win tool. HIPAA-compliant AI writing assistant purpose-built for clinicians. Drafts prior authorization letters, appeal letters, and clinical correspondence. Zero data retention policy. Supports PHI in prompts. Available to all verified U.S. physicians, NPs, PAs, pharmacists, podiatrists, CRNAs, and medical students.

$0/month

Phase 1 pharmacy/medication PA platform. Largest ePA network — processed 43M+ prior authorizations in Q1 2025. Integrated with 350+ EHRs and 500+ health plans. Handles medication/prescription prior authorizations electronically. Not suitable for medical/procedural PAs.

$2.50/million input tokens + $10/million output tokens for GPT-5.4; ~$0.02 per PA letter; estimated $10–$50/month for typical practice volume of 200–500 letters

Phase 3 core LLM engine for custom PA letter generation. HIPAA-compliant under Microsoft BAA. Deployed within Azure subscription with private endpoints, RBAC, and audit logging. GPT-5.4 provides best quality for medical letter generation; GPT-5.4 mini at $0.15/$0.60 per million tokens available as cost-optimized fallback.

$200–$500/month for App Service, Cosmos DB, Key Vault, and networking

Phase 3 hosting platform for custom PA letter generation tool. Includes Azure App Service (B2 tier for web app), Azure Cosmos DB (serverless for audit logs and templates), Azure Key Vault (API key and encryption key management), and Azure Virtual Network with private endpoints. All services covered under Microsoft HIPAA BAA.

Microsoft 365 Business Premium

MicrosoftPer-seat SaaS

$22/user/month MSP cost / $30/user/month suggested resale

Foundation identity and productivity platform. Provides Microsoft Entra ID P1 (Conditional Access + MFA), Microsoft Defender for Endpoint P1 (EDR), BitLocker management via Intune, and Exchange Online with data loss prevention policies. Required for centralized identity management across all AI tools.

SentinelOne Singularity Complete

SentinelOneSingularity Complete

$5/endpoint/month MSP cost / $10/endpoint/month suggested resale

AI-powered EDR for all workstations and servers. Provides real-time threat detection, automated response, and forensic investigation capabilities. Required for HIPAA Security Rule audit defensibility. Deploys via MSP multi-tenant console.

$3/user/month MSP cost / $7/user/month suggested resale

Multi-factor authentication for all PHI-accessing accounts. Integrates with Microsoft Entra ID, EHR web portals, Azure Portal, and custom applications. Required HIPAA technical safeguard. Alternative: use Entra ID P1 MFA if already included in M365 Business Premium.

DNSFilter

DNSFilter

$1.15/user/month MSP cost / $3.50/user/month suggested resale

DNS-layer security filtering to block malicious domains, prevent data exfiltration, and enforce acceptable use policies. Lightweight defense-in-depth layer that protects against phishing and C2 callbacks targeting PA workflow credentials.

$500–$1,500/month estimated per practice

Phase 2 option for practices on athenahealth. Purpose-built referral and PA automation platform with native Athena integration. Most practices go live in 4 weeks. Automates end-to-end referral coordination from fax receipt through booked appointment using AI agents.

$500–$2,000/month estimated per practice

Phase 2 option for specialty practices (dermatology, rheumatology, oncology). Automates medication prior authorizations and form-based authorization submissions. Portal-based workflow with AI-assisted clinical documentation.

$300–$1,000/month estimated per practice

Dental-specific AI platform. Automates insurance verification across 300+ payers — verifies entire day's patient list in under 5 seconds. AI-powered analysis of dental radiographs supports clinical documentation for PA justification.

Prerequisites

  • Active EHR/PM system with API access enabled (FHIR R4 preferred). Supported systems: athenahealth, eClinicalWorks, NextGen, DrChrono, Epic (medical); Open Dental, Dentrix Ascend, Eaglesoft (dental). Verify API licensing with EHR vendor before project start.
  • Stable internet connection: minimum 50 Mbps symmetrical, 100+ Mbps recommended. SaaS-dependent workflow requires high reliability — consider redundant ISP or cellular failover for practices with single-provider internet.
  • All clinicians who will review PA letters must have verified Doximity accounts (free). Verification requires active NPI number and U.S. medical license. Allow 1–5 business days for new account verification.
  • Signed HIPAA Business Associate Agreements (BAAs) with all technology vendors that will handle PHI: Microsoft (Azure/M365), OpenAI (if using direct API), SaaS PA platform vendor, backup vendor (Datto/Axcient), and the MSP itself must have a BAA with the practice.
  • Windows 11 Pro on all workstations (required for BitLocker management via Intune). Verify all existing machines meet Windows 11 hardware requirements (TPM 2.0, Secure Boot, 4GB+ RAM, 64GB+ storage).
  • Designated Clinical Champion: One provider at the practice who will own template review, workflow validation, and serve as the escalation point for AI output quality issues. This person must commit 2–4 hours/week during implementation.
  • Practice must have a current HIPAA Risk Assessment on file (updated within the last 12 months). If not, conduct one before implementation — this is a CMS audit requirement and the foundation for all technical safeguard decisions.
  • Admin credentials for EHR/PM system, domain controller (if applicable), Microsoft 365 tenant, and firewall. Collect and verify all credentials during the pre-implementation discovery call.
  • Existing or new Microsoft 365 Business Premium tenant with Microsoft Entra ID configured. If practice uses Google Workspace, plan for Entra ID overlay or migration before Phase 3.
  • Practice leadership must approve an AI Acceptable Use Policy that mandates human review of all AI-generated PA letters before submission. Draft policy provided in client handoff documentation.

Installation Steps

...

Step 1: Conduct Site Assessment and HIPAA Gap Analysis

Before any technical work begins, perform a comprehensive site assessment of the practice environment. Document current network topology, EHR system and version, number of PA-handling staff, monthly PA volume by payer, current PA workflow (manual vs. partially electronic), and existing security controls. Cross-reference findings against HIPAA Security Rule requirements to identify gaps that must be remediated before deploying AI tools that will process PHI.

Note

Use a standardized assessment template. Key items to document: (1) EHR vendor, version, and API availability; (2) current PA volume and top 5 payers; (3) average time per PA letter currently; (4) network diagram with IP ranges; (5) existing firewall, AV, and backup solutions; (6) BAA inventory. This assessment typically takes 2–4 hours on-site. Schedule during a low-patient-volume period.

Step 2: Deploy and Configure HIPAA-Compliant Network Infrastructure

Install the FortiGate 60F firewall, replacing or supplementing the existing edge device. Configure network segmentation with at minimum three VLANs: Clinical (VLAN 10, for EHR workstations and PA workflow), Administrative (VLAN 20, for billing and front-desk non-PHI tasks), and Guest (VLAN 30, for patient Wi-Fi). Enable FortiGuard UTM bundle including IPS, application control, web filtering, and antivirus. Configure TLS 1.2+ inspection for outbound HTTPS traffic on the Clinical VLAN. Deploy UniFi U6 Pro access points and assign SSIDs to appropriate VLANs.

FortiGate CLI
bash
# Create VLANs and enable UTM features on Clinical VLAN outbound policy

# FortiGate CLI - Create VLANs
config system interface
  edit "clinical-vlan"
    set vdom "root"
    set ip 10.10.10.1 255.255.255.0
    set allowaccess ping https ssh
    set interface "internal"
    set vlanid 10
  next
  edit "admin-vlan"
    set vdom "root"
    set ip 10.10.20.1 255.255.255.0
    set allowaccess ping https ssh
    set interface "internal"
    set vlanid 20
  next
  edit "guest-vlan"
    set vdom "root"
    set ip 10.10.30.1 255.255.255.0
    set allowaccess ping
    set interface "internal"
    set vlanid 30
  next
end

# Enable UTM features on Clinical VLAN outbound policy
config firewall policy
  edit 1
    set name "clinical-to-internet"
    set srcintf "clinical-vlan"
    set dstintf "wan1"
    set srcaddr "all"
    set dstaddr "all"
    set action accept
    set schedule "always"
    set service "ALL"
    set utm-status enable
    set av-profile "default"
    set ips-sensor "default"
    set application-list "default"
    set ssl-ssh-profile "deep-inspection"
    set logtraffic all
    set nat enable
  next
end
Note

Ensure the FortiGate firmware is updated to the latest stable release before configuration. Register the device on FortiCloud for centralized MSP management. The deep-inspection SSL profile requires installing the FortiGate CA certificate on all clinical workstations — push via Intune GPO. Guest VLAN should have bandwidth throttling (10 Mbps) and no access to internal subnets.

Step 3: Configure Microsoft 365 Business Premium and Entra ID

Set up or verify the Microsoft 365 Business Premium tenant. Configure Microsoft Entra ID with Conditional Access policies requiring MFA for all users, blocking legacy authentication protocols, and requiring compliant devices for access to PHI-related applications. Enable Microsoft Intune for device management. Create security groups: PA-Staff, PA-Clinicians, IT-Admins. Configure BitLocker encryption policy via Intune to enforce full-disk encryption on all enrolled Windows devices.

PowerShell
powershell
# Connect to Microsoft Graph and configure basic policies

# PowerShell - Connect to Microsoft Graph and configure basic policies
Install-Module Microsoft.Graph -Scope CurrentUser
Connect-MgGraph -Scopes 'Policy.ReadWrite.ConditionalAccess','DeviceManagementConfiguration.ReadWrite.All'

# Create Security Groups
New-MgGroup -DisplayName 'PA-Staff' -MailEnabled:$false -MailNickname 'pa-staff' -SecurityEnabled:$true
New-MgGroup -DisplayName 'PA-Clinicians' -MailEnabled:$false -MailNickname 'pa-clinicians' -SecurityEnabled:$true
New-MgGroup -DisplayName 'IT-Admins' -MailEnabled:$false -MailNickname 'it-admins' -SecurityEnabled:$true

# Intune BitLocker Policy (via Endpoint Security in Intune portal)
# Navigate to: Endpoint security > Disk encryption > Create policy
# Platform: Windows 10 and later
# Profile: BitLocker
# Settings:
#   Require device encryption: Yes
#   BitLocker OS drive policy: Require
#   Startup authentication: Require TPM + PIN
#   Recovery key rotation: Enabled
#   Encryption method: XTS-AES 256-bit
Note

If the practice already has Microsoft 365, verify the plan level supports Conditional Access (Business Premium, E3, or E5). If on Business Basic or Standard, an upgrade is required. Enroll all existing workstations in Intune before proceeding. Conditional Access policies should be tested in Report-Only mode for 48 hours before enforcement to avoid locking out staff.

Step 4: Deploy Endpoint Security Stack

Install SentinelOne Singularity Complete on all workstations and any on-premise servers (EHR server, file server). Configure the SentinelOne management console with a site for this practice under the MSP's multi-tenant account. Set threat response to 'Protect' mode (auto-quarantine). Deploy DNSFilter agent on all endpoints and configure the practice's DNS policy to block malware, phishing, botnet C2, and unapproved AI tools (block consumer ChatGPT, Gemini, Claude web interfaces to prevent staff from using non-BAA-covered AI with PHI). Install 3M privacy screens on all PA workflow workstations.

SentinelOne and DNSFilter deployment via Intune
shell
# SentinelOne deployment via Intune
# 1. Download SentinelOne MSI installer from Management Console > Sentinels > Packages
# 2. Upload MSI to Intune: Apps > Windows > Add > Line-of-business app
# 3. Assign to 'All Devices' group
# 4. Command-line arguments for silent install:
SentinelOneInstaller.msi /q SITE_TOKEN=<your_site_token>

# DNSFilter deployment via Intune
# 1. Download DNSFilter agent MSI from dnsfitler.com/dashboard/deployment
# 2. Upload to Intune as LOB app
# 3. Silent install:
msiexec /i DNSFilter_Agent.msi /qn ORGANIZATION_ID=<your_org_id>

# Verify deployments
# SentinelOne: Check Management Console > Sentinels for all endpoints reporting
# DNSFilter: Check Dashboard > Roaming Clients for all endpoints reporting
Critical

CRITICAL: Add the following domains to DNSFilter's BLOCK list to prevent staff from using non-HIPAA-compliant AI tools with PHI: chat.openai.com, gemini.google.com, claude.ai, bard.google.com, copilot.microsoft.com (unless using M365 Copilot with BAA). Add the following to the ALLOW list: doximity.com, *.openai.azure.com (for Phase 3 custom tool). Document the block policy in the practice's AI Acceptable Use Policy.

Step 5: Phase 1 Deployment — Doximity GPT for Clinician PA Letter Drafting

Set up verified Doximity accounts for all eligible clinicians at the practice. For each clinician, verify their Doximity profile includes current NPI number, practice address, and specialty. Enable the Doximity GPT (DoxGPT) feature in their account settings. Create a shared document (stored in HIPAA-compliant SharePoint) containing standardized prompt templates for the practice's most common PA letter scenarios. Conduct a 60-minute training session with all clinicians on using DoxGPT effectively for PA letters.

1
Navigate to doximity.com/register for each clinician
2
Complete verification (NPI + license lookup)
3
Access DoxGPT via the Doximity writing tools interface
4
Test with a de-identified sample PA letter prompt
Note

Doximity verification typically takes 1–5 business days for new accounts. Start this process at the beginning of the project. DoxGPT is HIPAA-compliant with zero data retention — PHI can be included in prompts. However, advise clinicians to use only the minimum necessary PHI. For dental practices where providers may not have Doximity accounts (dentists are not currently eligible for Doximity), skip to Phase 2 or Phase 3 approaches.

Step 6: Phase 1 Deployment — CoverMyMeds Integration for Pharmacy PAs

Enable CoverMyMeds electronic prior authorization (ePA) within the practice's EHR system. Most major EHRs (athenahealth, eClinicalWorks, DrChrono, NextGen, Epic) have native CoverMyMeds integration available. Navigate to the EHR's integration marketplace or module settings to activate CoverMyMeds. Register the practice on CoverMyMeds if no account exists. Map provider NPIs and practice TIN to the CoverMyMeds account. Test the workflow with a sample medication PA request.

  • athenahealth: Navigate to Admin > Clinical > eRx Settings → Enable CoverMyMeds ePA integration → Map each provider's NPI
  • eClinicalWorks: Navigate to Admin > eRx Configurations → Enable electronic Prior Authorization → CoverMyMeds will activate automatically for eRx-enabled providers
  • DrChrono: Navigate to Account > Settings > Integrations → Enable CoverMyMeds → Follow OAuth authorization flow
  • Open Dental (dental): CoverMyMeds does not natively integrate with dental PM systems — Use CoverMyMeds web portal directly at covermymeds.com
Note

CoverMyMeds is free for providers — it is funded by pharmacies and payers. It handles medication/prescription PAs only, NOT medical/procedural PAs (e.g., imaging, surgeries, DME). For medical PAs, proceed to Phase 2 (SaaS platform) or Phase 3 (custom build). Allow 1–2 weeks for CoverMyMeds activation and payer network verification.

Step 7: Phase 2 Deployment — Select and Onboard Turnkey PA SaaS Platform

Based on the practice's EHR, specialty, and PA volume, select and deploy one of the recommended turnkey SaaS platforms. For athenahealth practices: Linear Health. For specialty medical practices: Tandem AI or Ethermed. For dental practices: Overjet for insurance verification plus custom letter tool (Phase 3). Engage the vendor's onboarding team, provide EHR API credentials, and configure the integration. Map the practice's top payers and most common PA procedure/medication codes. Configure the workflow: (1) PA need identified → (2) platform pulls patient data from EHR → (3) AI generates draft letter → (4) clinician reviews in platform → (5) submission to payer via fax/portal.

1
Linear Health onboarding (athenahealth): Contact Linear Health sales for practice evaluation → Provide athenahealth Practice ID and API credentials → Linear Health configures Athena Marketplace integration → Map provider NPIs, payer IDs, and common CPT/ICD codes → Linear Health deploys AI agents within 4 weeks
2
Tandem AI onboarding: Contact Tandem AI for specialty practice demo → Provide EHR system details and PA volume estimates → Complete vendor security questionnaire and sign BAA → Configure medication-specific PA automation rules → Train PA staff on Tandem AI portal
3
Overjet onboarding (dental): Contact Overjet sales for dental practice demo → Provide practice management system details (Dentrix, Eaglesoft, Open Dental) → Configure imaging integration for clinical documentation support → Enable insurance verification module (300+ payer network) → Train front desk and clinical staff
Note

Vendor selection decision matrix: (1) If on athenahealth → Linear Health (purpose-built, 4-week go-live). (2) If specialty medical practice → Tandem AI (strong medication PA automation). (3) If multi-specialty or procedure-heavy → Ethermed (broad medical PA coverage). (4) If dental → Overjet + Phase 3 custom tool. Always require a signed BAA from the SaaS vendor before providing any EHR API credentials. Verify the vendor's SOC 2 Type II report and HIPAA compliance documentation.

Step 8: Phase 3 Setup — Provision Azure Environment for Custom AI Build

Create a dedicated Azure subscription for the practice (or a shared MSP subscription with resource group isolation). Sign the Microsoft HIPAA BAA via the Azure portal Trust Center. Deploy the foundational Azure resources: Resource Group, Virtual Network with private endpoints, Azure Key Vault, Azure Cosmos DB (serverless, for audit logs and prompt templates), and Azure App Service (B2 tier). Apply for Azure OpenAI Service access if not already approved. Once approved, deploy a GPT-5.4 model instance in the Azure OpenAI resource within a HIPAA-eligible region (East US or West US 2).

Azure CLI - Provision core infrastructure
bash
# Azure CLI - Provision core infrastructure
az login
az account set --subscription "<subscription-id>"

# Create Resource Group
az group create --name rg-pa-lettergen --location eastus

# Create Virtual Network with subnet for private endpoints
az network vnet create --resource-group rg-pa-lettergen --name vnet-pa --address-prefix 10.0.0.0/16 --subnet-name snet-private-endpoints --subnet-prefix 10.0.1.0/24

# Create Azure Key Vault
az keyvault create --resource-group rg-pa-lettergen --name kv-pa-lettergen --location eastus --sku standard --enable-purge-protection true --enable-soft-delete true

# Create Cosmos DB account (serverless)
az cosmosdb create --resource-group rg-pa-lettergen --name cosmos-pa-lettergen --kind GlobalDocumentDB --capabilities EnableServerless --default-consistency-level Session --locations regionName=eastus

# Create Cosmos DB database and containers
az cosmosdb sql database create --resource-group rg-pa-lettergen --account-name cosmos-pa-lettergen --name pa-letter-db
az cosmosdb sql container create --resource-group rg-pa-lettergen --account-name cosmos-pa-lettergen --database-name pa-letter-db --name audit-logs --partition-key-path /practiceId
az cosmosdb sql container create --resource-group rg-pa-lettergen --account-name cosmos-pa-lettergen --database-name pa-letter-db --name prompt-templates --partition-key-path /templateCategory

# Create Azure OpenAI resource
az cognitiveservices account create --resource-group rg-pa-lettergen --name aoai-pa-lettergen --kind OpenAI --sku S0 --location eastus --custom-domain aoai-pa-lettergen

# Deploy GPT-5.4 model
az cognitiveservices account deployment create --resource-group rg-pa-lettergen --name aoai-pa-lettergen --deployment-name gpt-5.4 --model-name gpt-5.4 --model-version 2024-08-06 --model-format OpenAI --sku-name Standard --sku-capacity 30

# Create App Service Plan and Web App
az appservice plan create --resource-group rg-pa-lettergen --name asp-pa-lettergen --sku B2 --is-linux
az webapp create --resource-group rg-pa-lettergen --plan asp-pa-lettergen --name app-pa-lettergen --runtime "PYTHON:3.11"

# Store Azure OpenAI key in Key Vault
AOAI_KEY=$(az cognitiveservices account keys list --resource-group rg-pa-lettergen --name aoai-pa-lettergen --query key1 -o tsv)
az keyvault secret set --vault-name kv-pa-lettergen --name aoai-api-key --value $AOAI_KEY
Note

Azure OpenAI access requires an application — apply at https://aka.ms/oai/access. Approval typically takes 1–5 business days for healthcare use cases. Ensure the subscription has the Microsoft HIPAA BAA signed BEFORE deploying any resources — navigate to Azure Portal > Trust Center > Regulatory Compliance > HIPAA BAA. Deploy all resources in the same region (East US recommended) to minimize latency and avoid cross-region data transfer.

Step 9: Phase 3 Build — Deploy Custom PA Letter Generation Application

Deploy the custom PA letter generation web application to Azure App Service. The application is a Python Flask/FastAPI web app that: (1) Accepts structured patient data input from staff (or pulls from EHR via FHIR API), (2) Selects the appropriate prompt template based on payer and procedure type, (3) Calls Azure OpenAI GPT-5.4 to generate the PA letter draft, (4) Presents the draft to a clinician for review and editing, (5) Logs all activity to Cosmos DB for HIPAA audit trail, (6) Exports the final approved letter as PDF for fax/portal submission. See the custom_ai_components section for complete prompt templates and application code.

Clone, configure, and deploy the PA letter generation app to Azure App Service with managed identity and Key Vault integration
bash
# Clone the application repository (MSP internal repo)
git clone https://github.com/<msp-org>/pa-letter-generator.git
cd pa-letter-generator

# Create Python virtual environment
python3.11 -m venv venv
source venv/bin/activate

# Install dependencies
pip install -r requirements.txt
# requirements.txt contents:
# fastapi==0.115.0
# uvicorn==0.30.0
# openai==1.52.0
# azure-identity==1.18.0
# azure-cosmos==4.7.0
# azure-keyvault-secrets==4.8.0
# python-multipart==0.0.9
# jinja2==3.1.4
# weasyprint==62.0
# python-dotenv==1.0.1
# pydantic==2.9.0

# Configure environment variables (stored in App Service Configuration)
az webapp config appsettings set --resource-group rg-pa-lettergen --name app-pa-lettergen --settings \
  AZURE_OPENAI_ENDPOINT=https://aoai-pa-lettergen.openai.azure.com/ \
  AZURE_OPENAI_DEPLOYMENT=gpt-5.4 \
  AZURE_OPENAI_API_VERSION=2024-08-06 \
  COSMOS_ENDPOINT=https://cosmos-pa-lettergen.documents.azure.com:443/ \
  COSMOS_DATABASE=pa-letter-db \
  KEY_VAULT_URL=https://kv-pa-lettergen.vault.azure.net/ \
  PRACTICE_NAME="Example Medical Associates" \
  REQUIRE_CLINICIAN_APPROVAL=true

# Deploy to Azure App Service
az webapp deployment source config-zip --resource-group rg-pa-lettergen --name app-pa-lettergen --src app.zip

# Enable managed identity for Key Vault access
az webapp identity assign --resource-group rg-pa-lettergen --name app-pa-lettergen
PRINCIPAL_ID=$(az webapp identity show --resource-group rg-pa-lettergen --name app-pa-lettergen --query principalId -o tsv)
az keyvault set-policy --name kv-pa-lettergen --object-id $PRINCIPAL_ID --secret-permissions get list

# Configure custom domain and SSL (optional but recommended)
# az webapp config hostname add --resource-group rg-pa-lettergen --webapp-name app-pa-lettergen --hostname pa.example-practice.com
# az webapp config ssl bind ...
Note

The application MUST enforce clinician approval — the REQUIRE_CLINICIAN_APPROVAL flag should never be set to false in production. Use Azure Managed Identity instead of hardcoded API keys wherever possible. The App Service should have IP restrictions configured to allow access only from the practice's public IP and the MSP's management IPs. Enable App Service authentication via Entra ID to require Azure AD login for all users.

Step 10: Phase 3 Build — Configure EHR FHIR Integration for Auto-Population

For practices with FHIR-capable EHRs, configure the PA letter generator to pull patient demographic data, diagnoses (ICD-10 codes), procedures (CPT codes), medications, and relevant clinical notes directly from the EHR via FHIR R4 APIs. This eliminates manual data entry and reduces errors. Register the PA letter generator as a SMART on FHIR application in the EHR's app marketplace. Configure OAuth 2.0 authorization flow so staff can pull patient data with a single click.

Example FHIR data pull integrated into the main PA letter generator application
python
# Example FHIR data pull (Python - using requests library)
# This code is integrated into the main application

import requests

def get_patient_data_for_pa(fhir_base_url, patient_id, access_token):
    headers = {
        'Authorization': f'Bearer {access_token}',
        'Accept': 'application/fhir+json'
    }
    
    # Get patient demographics
    patient = requests.get(f'{fhir_base_url}/Patient/{patient_id}', headers=headers).json()
    
    # Get active conditions (diagnoses)
    conditions = requests.get(
        f'{fhir_base_url}/Condition?patient={patient_id}&clinical-status=active',
        headers=headers
    ).json()
    
    # Get recent medications
    medications = requests.get(
        f'{fhir_base_url}/MedicationRequest?patient={patient_id}&status=active',
        headers=headers
    ).json()
    
    # Get relevant procedures/service requests pending PA
    service_requests = requests.get(
        f'{fhir_base_url}/ServiceRequest?patient={patient_id}&status=active',
        headers=headers
    ).json()
    
    # Get recent clinical notes (last 90 days)
    from datetime import datetime, timedelta
    date_90_days_ago = (datetime.now() - timedelta(days=90)).strftime('%Y-%m-%d')
    notes = requests.get(
        f'{fhir_base_url}/DocumentReference?patient={patient_id}&date=ge{date_90_days_ago}&type=http://loinc.org|11506-3',
        headers=headers
    ).json()
    
    return {
        'patient': patient,
        'conditions': conditions,
        'medications': medications,
        'service_requests': service_requests,
        'clinical_notes': notes
    }
Note

FHIR integration complexity varies dramatically by EHR. athenahealth and Epic have mature FHIR APIs with good documentation. eClinicalWorks and NextGen support is improving but may require vendor support tickets. Dental PM systems (Dentrix, Eaglesoft) generally do NOT support FHIR — for dental practices, use manual data entry or file-based export from Open Dental's open-source database. FHIR integration is the highest-risk step in Phase 3; allocate 2–4 weeks and budget for EHR vendor consultation fees.

Step 11: Configure Audit Logging and Compliance Documentation

Implement comprehensive audit logging that captures every interaction with the PA letter generator. Every AI-generated letter must be logged with: timestamp, requesting user, reviewing clinician, patient identifier (MRN, not SSN), input data summary, AI model version used, generated output hash, clinician edits (diff), approval timestamp, and submission method. Store logs in Azure Cosmos DB with a 7-year retention policy (HIPAA requirement: 6 years minimum; 7 years provides buffer). Generate monthly compliance reports showing letter volume, average review time, edit rates, and any anomalies.

Cosmos DB audit log document structure (Python dataclass)
python
# Cosmos DB audit log document structure (Python dataclass)
from dataclasses import dataclass, asdict
from datetime import datetime
import hashlib
import json

@dataclass
class PAAuditLog:
    id: str                          # UUID
    practice_id: str                 # Practice identifier
    timestamp_created: str           # ISO 8601 UTC
    timestamp_approved: str          # ISO 8601 UTC (null until approved)
    requesting_user: str             # Staff member email
    reviewing_clinician: str         # Clinician email/NPI
    patient_mrn: str                 # Medical Record Number (not SSN)
    payer_name: str                  # Insurance carrier
    pa_type: str                     # medication | procedure | imaging | dme
    procedure_codes: list            # CPT codes
    diagnosis_codes: list            # ICD-10 codes
    prompt_template_id: str          # Template version used
    model_deployment: str            # e.g., gpt-5.4
    input_token_count: int           # Tokens sent to model
    output_token_count: int          # Tokens received
    generated_output_hash: str       # SHA-256 of raw AI output
    clinician_edits_made: bool       # Did clinician modify the draft?
    edit_diff_hash: str              # SHA-256 of final vs. original
    approval_status: str             # pending | approved | rejected
    submission_method: str           # fax | portal | electronic
    letter_pdf_hash: str             # SHA-256 of final PDF

# Write to Cosmos DB
from azure.cosmos import CosmosClient

def log_pa_event(audit_log: PAAuditLog):
    client = CosmosClient(COSMOS_ENDPOINT, COSMOS_KEY)
    db = client.get_database_client('pa-letter-db')
    container = db.get_container_client('audit-logs')
    container.create_item(body=asdict(audit_log))
Note

NEVER log the full text of the PA letter in the audit database — this creates an additional PHI repository that increases breach surface. Instead, store SHA-256 hashes and keep the actual letters in the practice's EHR document management system or a separate encrypted Azure Blob Storage container with strict access controls. Set Cosmos DB TTL to -1 (no auto-deletion) and implement a manual purge process after 7 years. Enable Cosmos DB diagnostic logging to Azure Monitor for infrastructure-level audit trail.

Step 12: Conduct Staff Training and Go-Live

Conduct two training sessions: (1) Technical training for PA staff (90 minutes) covering the PA letter workflow, data entry, template selection, and submission process. (2) Clinical review training for providers (60 minutes) covering AI output review best practices, hallucination detection, edit workflows, and approval process. Create laminated quick-reference cards for each workstation. Go live with a 2-week pilot period where AI-generated letters are created in parallel with the existing manual process to validate quality and catch issues.

Note

Training should include live demonstrations with de-identified patient data. Key training topics: (1) How to select the correct prompt template for each payer/procedure combination. (2) What hallucination looks like in a PA letter (fabricated study citations, incorrect drug dosages, made-up policy numbers). (3) How to edit and approve letters in the system. (4) When to escalate to the MSP (system errors, unexpected outputs, new payer requirements). (5) AI Acceptable Use Policy review and sign-off. All staff must sign the AI Acceptable Use Policy before gaining system access. Keep sign-off records for HIPAA documentation.

Custom AI Components

PA Letter Generation System Prompt

Type: prompt The core system prompt that instructs GPT-5.4 on how to generate prior authorization letters. This prompt establishes the AI's role, output format, clinical accuracy requirements, and compliance guardrails. It is stored in Azure Cosmos DB as a versioned template and retrieved at runtime by the application.

Implementation:

PA Letter Generation System Prompt (version 1.0)

You are a medical prior authorization letter drafting assistant working within a HIPAA-compliant healthcare environment. Your role is to generate professional, clinically appropriate prior authorization request letters addressed to insurance carriers on behalf of the requesting provider. CRITICAL RULES: 1. ONLY use the clinical information explicitly provided in the user message. NEVER fabricate, assume, or hallucinate any clinical details, study citations, drug dosages, policy numbers, or patient information. 2. If information needed for a complete letter is missing from the input, clearly mark it with [MISSING: description of needed information] so the reviewing clinician can fill it in. 3. NEVER invent or cite specific clinical studies, journal articles, or guideline references unless they are provided verbatim in the input. Instead, use phrases like "per current evidence-based guidelines" or "consistent with standard of care for this condition." 4. Every letter MUST include the following disclaimer at the bottom: "This letter was drafted with AI assistance and has been reviewed and approved by the undersigned provider." 5. Format the letter as a formal business letter with: date, provider letterhead placeholder, payer name and address, RE line with patient name and member ID, salutation, body paragraphs, and provider signature block. LETTER STRUCTURE: - Paragraph 1: Provider introduction, patient identification, and specific authorization request (procedure/medication/service with CPT/HCPCS codes) - Paragraph 2: Clinical history and diagnosis (ICD-10 codes with descriptions) - Paragraph 3: Medical necessity justification — why this specific treatment is needed for this specific patient, including failed alternatives if applicable - Paragraph 4: Treatment plan and expected outcomes - Paragraph 5: Urgency statement (if applicable) and request for expedited review - Closing: Provider contact information for peer-to-peer review, signature block TONE: Professional, clinical, assertive but respectful. Use medical terminology appropriate for a physician-to-medical-director communication. Be specific and evidence-based in medical necessity arguments. OUTPUT FORMAT: Plain text formatted as a letter. Do not use markdown headers or bullet points in the letter body — use proper paragraph structure.
Sonnet 4.6

Payer-Specific Prompt Templates

Type: prompt A library of payer-specific prompt template extensions that augment the system prompt with carrier-specific requirements, known approval criteria, and formatting preferences. These are stored in Cosmos DB and selected based on the payer identified in the PA request. MSP maintains and updates these templates as payer requirements change.

Implementation:

UnitedHealthcare (UHC) — tmpl-uhc-medical-v1

Payer: UnitedHealthcare Category: medical_procedure Additional Instructions: - UHC requires explicit reference to their Clinical Coverage Policy. Include the phrase: "This request is consistent with UnitedHealthcare's Medical Policy for [procedure name]." - UHC prefers quantitative failed-treatment documentation. State specific durations: "Patient completed X weeks of [conservative treatment] from [date] to [date] without adequate improvement." - Include Optum prior auth reference number if available: [MISSING: Optum PA Reference Number if applicable] - For imaging requests, cite specific clinical findings that meet medical necessity thresholds.
Sonnet 4.6

Aetna — tmpl-aetna-medical-v1

Payer: Aetna Category: medical_procedure Additional Instructions: - Aetna uses Clinical Policy Bulletins (CPBs). Reference: "This request meets the criteria outlined in Aetna Clinical Policy Bulletin [CPB number if known, otherwise state: applicable CPB for this condition]." - Aetna requires documentation of step therapy compliance for medications. List all prior medications tried, dosages, duration, and reason for discontinuation. - Include Precertification number if a phone auth was started: [MISSING: Aetna Precert Number if applicable]
Sonnet 4.6

Cigna — tmpl-cigna-medical-v1

Payer: Cigna Category: medical_procedure Additional Instructions: - Cigna uses Coverage Policy documents. Reference applicable Cigna Coverage Policy by name when possible. - Cigna values peer-reviewed literature. If the clinician provides specific references, include them. If not, state: "Supported by current peer-reviewed clinical evidence and professional society guidelines." - For specialty medications, include documentation of any required lab values or diagnostic criteria.
Sonnet 4.6

BlueCross BlueShield (Generic) — tmpl-bcbs-medical-v1

Payer: BlueCross BlueShield Category: medical_procedure Additional Instructions: - BCBS policies vary significantly by state plan. Include the specific BCBS plan name (e.g., "BCBS of Illinois" not just "BCBS"). - Reference applicable Medical Policy number: [MISSING: BCBS Medical Policy Number if known] - BCBS plans frequently require documentation of conservative treatment duration. Be specific with dates and outcomes.
Sonnet 4.6

Medicare Advantage — tmpl-medicare-adv-v1

Payer: Medicare Advantage Category: medical_procedure Additional Instructions: - Reference applicable National Coverage Determination (NCD) or Local Coverage Determination (LCD) by number when known. - Starting January 1, 2026, Medicare Advantage plans must respond to standard PA requests within 7 calendar days and urgent requests within 72 hours. - Include ABN (Advance Beneficiary Notice) status if applicable. - State explicit Medicare coverage criteria met by this patient.
Sonnet 4.6

Dental Insurance (Generic) — tmpl-dental-generic-v1

Payer: Generic Dental Carrier Category: dental_procedure Additional Instructions: - Dental PAs require ADA procedure codes (CDT codes) rather than CPT codes. - Include specific tooth numbers using Universal Numbering System. - Reference diagnostic radiographs: "Enclosed please find periapical/panoramic radiograph dated [date] demonstrating [findings]." - For crown/bridge PAs: document existing restoration history, fracture lines, and percentage of tooth structure compromised. - For implant PAs: document bone density assessment, adjacent tooth condition, and why alternative prosthetics are contraindicated. - For orthodontic PAs: include cephalometric analysis results, Angle classification, overjet/overbite measurements, and Salzmann or HLD score if required by carrier.
Sonnet 4.6

PA Letter Generation API Endpoint

Type: integration The core FastAPI application endpoint that orchestrates the PA letter generation workflow. Accepts structured patient and PA request data, retrieves the appropriate prompt template, calls Azure OpenAI, and returns the draft letter for clinician review. Includes audit logging and input validation.

Implementation:

main.py - FastAPI Application for PA Letter Generation
python
# main.py - FastAPI Application for PA Letter Generation

from fastapi import FastAPI, HTTPException, Depends, Request
from fastapi.responses import HTMLResponse
from fastapi.security import HTTPBearer
from pydantic import BaseModel, Field
from typing import Optional, List
from datetime import datetime
import hashlib
import uuid
import os
from openai import AzureOpenAI
from azure.cosmos import CosmosClient
from azure.keyvault.secrets import SecretClient
from azure.identity import DefaultAzureCredential

app = FastAPI(title='PA Letter Generator', version='1.0.0')

# Configuration from environment
AOAI_ENDPOINT = os.environ['AZURE_OPENAI_ENDPOINT']
AOAI_DEPLOYMENT = os.environ['AZURE_OPENAI_DEPLOYMENT']
AOAI_API_VERSION = os.environ['AZURE_OPENAI_API_VERSION']
COSMOS_ENDPOINT = os.environ['COSMOS_ENDPOINT']
KEY_VAULT_URL = os.environ['KEY_VAULT_URL']

# Initialize clients with Managed Identity
credential = DefaultAzureCredential()
kv_client = SecretClient(vault_url=KEY_VAULT_URL, credential=credential)
aoai_key = kv_client.get_secret('aoai-api-key').value

oai_client = AzureOpenAI(
    azure_endpoint=AOAI_ENDPOINT,
    api_key=aoai_key,
    api_version=AOAI_API_VERSION
)

cosmos_client = CosmosClient(COSMOS_ENDPOINT, credential=credential)
db = cosmos_client.get_database_client('pa-letter-db')
audit_container = db.get_container_client('audit-logs')
template_container = db.get_container_client('prompt-templates')

# Pydantic models
class PARequest(BaseModel):
    patient_name: str
    patient_dob: str
    patient_mrn: str
    member_id: str
    group_number: Optional[str] = None
    payer_name: str
    payer_address: Optional[str] = None
    provider_name: str
    provider_npi: str
    provider_specialty: str
    practice_name: str
    practice_address: str
    practice_phone: str
    practice_fax: str
    diagnosis_codes: List[str] = Field(..., description='ICD-10 codes with descriptions, e.g. ["M54.5 - Low back pain"]')
    procedure_codes: List[str] = Field(..., description='CPT/CDT codes with descriptions, e.g. ["72148 - MRI lumbar spine without contrast"]')
    clinical_history: str = Field(..., description='Relevant clinical history and exam findings')
    failed_treatments: Optional[str] = Field(None, description='Prior treatments attempted and outcomes')
    medical_necessity: str = Field(..., description='Clinical rationale for the requested service')
    urgency: Optional[str] = Field(None, description='If urgent, explain clinical urgency')
    additional_context: Optional[str] = Field(None, description='Any additional payer-specific info')
    pa_type: str = Field(..., description='medication | procedure | imaging | dme | dental')

class PAResponse(BaseModel):
    request_id: str
    draft_letter: str
    model_used: str
    input_tokens: int
    output_tokens: int
    generated_at: str
    approval_required: bool = True
    warnings: List[str]

def get_system_prompt():
    return """You are a medical prior authorization letter drafting assistant working within a HIPAA-compliant healthcare environment. Your role is to generate professional, clinically appropriate prior authorization request letters addressed to insurance carriers on behalf of the requesting provider.

CRITICAL RULES:
1. ONLY use the clinical information explicitly provided in the user message. NEVER fabricate, assume, or hallucinate any clinical details, study citations, drug dosages, policy numbers, or patient information.
2. If information needed for a complete letter is missing from the input, clearly mark it with [MISSING: description of needed information] so the reviewing clinician can fill it in.
3. NEVER invent or cite specific clinical studies, journal articles, or guideline references unless they are provided verbatim in the input. Instead, use phrases like "per current evidence-based guidelines" or "consistent with standard of care for this condition."
4. Every letter MUST include the following disclaimer at the bottom: "This letter was drafted with AI assistance and has been reviewed and approved by the undersigned provider."
5. Format the letter as a formal business letter with: date, provider letterhead placeholder, payer name and address, RE line with patient name and member ID, salutation, body paragraphs, and provider signature block.

LETTER STRUCTURE:
- Paragraph 1: Provider introduction, patient identification, and specific authorization request (procedure/medication/service with CPT/HCPCS/CDT codes)
- Paragraph 2: Clinical history and diagnosis (ICD-10 codes with descriptions)
- Paragraph 3: Medical necessity justification
- Paragraph 4: Treatment plan and expected outcomes
- Paragraph 5: Urgency statement (if applicable) and request for expedited review
- Closing: Provider contact information for peer-to-peer review, signature block

TONE: Professional, clinical, assertive but respectful.
OUTPUT FORMAT: Plain text formatted as a letter. Do not use markdown."""

def get_payer_template(payer_name: str, pa_type: str) -> str:
    query = f"SELECT * FROM c WHERE CONTAINS(LOWER(c.payer), LOWER('{payer_name}')) AND c.category = '{pa_type}'"
    templates = list(template_container.query_items(query=query, enable_cross_partition_query=True))
    if templates:
        return templates[0].get('additional_instructions', '')
    return ''

def build_user_message(req: PARequest, payer_template: str) -> str:
    msg = f"""Generate a prior authorization request letter with the following information:

PATIENT INFORMATION:
- Name: {req.patient_name}
- Date of Birth: {req.patient_dob}
- MRN: {req.patient_mrn}
- Member ID: {req.member_id}
- Group Number: {req.group_number or '[MISSING: Group Number]'}

INSURANCE CARRIER:
- Payer: {req.payer_name}
- Address: {req.payer_address or '[MISSING: Payer Address]'}

REQUESTING PROVIDER:
- Name: {req.provider_name}
- NPI: {req.provider_npi}
- Specialty: {req.provider_specialty}
- Practice: {req.practice_name}
- Address: {req.practice_address}
- Phone: {req.practice_phone}
- Fax: {req.practice_fax}

DIAGNOSES:
{chr(10).join('- ' + d for d in req.diagnosis_codes)}

REQUESTED SERVICES:
{chr(10).join('- ' + p for p in req.procedure_codes)}

CLINICAL HISTORY:
{req.clinical_history}
"""
    if req.failed_treatments:
        msg += f"\nPRIOR TREATMENTS ATTEMPTED:\n{req.failed_treatments}\n"
    msg += f"\nMEDICAL NECESSITY JUSTIFICATION:\n{req.medical_necessity}\n"
    if req.urgency:
        msg += f"\nURGENCY:\n{req.urgency}\n"
    if req.additional_context:
        msg += f"\nADDITIONAL CONTEXT:\n{req.additional_context}\n"
    if payer_template:
        msg += f"\nPAYER-SPECIFIC REQUIREMENTS:\n{payer_template}\n"
    return msg

@app.post('/api/generate-pa-letter', response_model=PAResponse)
async def generate_pa_letter(req: PARequest, request: Request):
    request_id = str(uuid.uuid4())
    warnings = []
    
    # Get payer-specific template
    payer_template = get_payer_template(req.payer_name, req.pa_type)
    if not payer_template:
        warnings.append(f'No payer-specific template found for {req.payer_name}. Using generic format.')
    
    # Build messages
    system_prompt = get_system_prompt()
    user_message = build_user_message(req, payer_template)
    
    # Call Azure OpenAI
    response = oai_client.chat.completions.create(
        model=AOAI_DEPLOYMENT,
        messages=[
            {'role': 'system', 'content': system_prompt},
            {'role': 'user', 'content': user_message}
        ],
        temperature=0.3,
        max_tokens=3000,
        top_p=0.95
    )
    
    draft_letter = response.choices[0].message.content
    input_tokens = response.usage.prompt_tokens
    output_tokens = response.usage.completion_tokens
    
    # Check for hallucination markers
    if '[MISSING:' in draft_letter:
        warnings.append('Letter contains [MISSING] placeholders that require clinician input.')
    
    # Log to Cosmos DB
    audit_entry = {
        'id': request_id,
        'practiceId': req.practice_name,
        'timestamp_created': datetime.utcnow().isoformat(),
        'timestamp_approved': None,
        'requesting_user': request.headers.get('X-User-Email', 'unknown'),
        'reviewing_clinician': None,
        'patient_mrn': req.patient_mrn,
        'payer_name': req.payer_name,
        'pa_type': req.pa_type,
        'procedure_codes': req.procedure_codes,
        'diagnosis_codes': req.diagnosis_codes,
        'prompt_template_id': f'system-v1.0 + {req.payer_name}-{req.pa_type}',
        'model_deployment': AOAI_DEPLOYMENT,
        'input_token_count': input_tokens,
        'output_token_count': output_tokens,
        'generated_output_hash': hashlib.sha256(draft_letter.encode()).hexdigest(),
        'clinician_edits_made': False,
        'edit_diff_hash': None,
        'approval_status': 'pending',
        'submission_method': None,
        'letter_pdf_hash': None
    }
    audit_container.create_item(body=audit_entry)
    
    return PAResponse(
        request_id=request_id,
        draft_letter=draft_letter,
        model_used=AOAI_DEPLOYMENT,
        input_tokens=input_tokens,
        output_tokens=output_tokens,
        generated_at=datetime.utcnow().isoformat(),
        approval_required=True,
        warnings=warnings
    )

@app.post('/api/approve-pa-letter/{request_id}')
async def approve_pa_letter(request_id: str, clinician_email: str, final_letter_text: str, submission_method: str, request: Request):
    # Retrieve audit log
    audit_entry = audit_container.read_item(item=request_id, partition_key=request.headers.get('X-Practice-Name', ''))
    
    # Update with approval
    original_hash = audit_entry['generated_output_hash']
    final_hash = hashlib.sha256(final_letter_text.encode()).hexdigest()
    
    audit_entry['timestamp_approved'] = datetime.utcnow().isoformat()
    audit_entry['reviewing_clinician'] = clinician_email
    audit_entry['clinician_edits_made'] = (original_hash != final_hash)
    audit_entry['edit_diff_hash'] = final_hash
    audit_entry['approval_status'] = 'approved'
    audit_entry['submission_method'] = submission_method
    
    audit_container.replace_item(item=request_id, body=audit_entry)
    
    return {'status': 'approved', 'request_id': request_id}

@app.get('/health')
async def health_check():
    return {'status': 'healthy', 'timestamp': datetime.utcnow().isoformat()}

PA Letter Generation System Prompt

You are a medical prior authorization letter drafting assistant working within a HIPAA-compliant healthcare environment. Your role is to generate professional, clinically appropriate prior authorization request letters addressed to insurance carriers on behalf of the requesting provider. CRITICAL RULES: 1. ONLY use the clinical information explicitly provided in the user message. NEVER fabricate, assume, or hallucinate any clinical details, study citations, drug dosages, policy numbers, or patient information. 2. If information needed for a complete letter is missing from the input, clearly mark it with [MISSING: description of needed information] so the reviewing clinician can fill it in. 3. NEVER invent or cite specific clinical studies, journal articles, or guideline references unless they are provided verbatim in the input. Instead, use phrases like "per current evidence-based guidelines" or "consistent with standard of care for this condition." 4. Every letter MUST include the following disclaimer at the bottom: "This letter was drafted with AI assistance and has been reviewed and approved by the undersigned provider." 5. Format the letter as a formal business letter with: date, provider letterhead placeholder, payer name and address, RE line with patient name and member ID, salutation, body paragraphs, and provider signature block. LETTER STRUCTURE: - Paragraph 1: Provider introduction, patient identification, and specific authorization request (procedure/medication/service with CPT/HCPCS/CDT codes) - Paragraph 2: Clinical history and diagnosis (ICD-10 codes with descriptions) - Paragraph 3: Medical necessity justification - Paragraph 4: Treatment plan and expected outcomes - Paragraph 5: Urgency statement (if applicable) and request for expedited review - Closing: Provider contact information for peer-to-peer review, signature block TONE: Professional, clinical, assertive but respectful. OUTPUT FORMAT: Plain text formatted as a letter. Do not use markdown.
Sonnet 4.6

Clinician Review Web Interface

Type: workflow A simple, clean web interface that PA staff use to input request data and clinicians use to review, edit, and approve AI-generated letters. Built with Jinja2 templates served by the FastAPI backend. Enforces the human-in-the-loop workflow.

Implementation

PA Request Form (Staff-Facing)

  • Form fields map 1:1 to the PARequest Pydantic model
  • Dropdown selectors for: Payer (populated from Cosmos DB template list), PA Type (medication/procedure/imaging/dme/dental), Provider (populated from practice roster)
  • Free-text fields for: clinical history, failed treatments, medical necessity, urgency, additional context
  • ICD-10 and CPT/CDT code lookup with autocomplete (use a client-side library like icd10-autocomplete or embed NLM API lookup)
  • 'Generate Draft' button submits to /api/generate-pa-letter
  • Requires Entra ID authentication; logs staff user identity

Clinician Review View (Provider-Facing)

  • Displays the AI-generated letter in an editable rich-text area
  • Side panel shows: patient summary, original input data, any [MISSING] warnings highlighted in yellow
  • RED BANNER at top: 'AI-GENERATED DRAFT — CLINICIAN REVIEW REQUIRED BEFORE SUBMISSION'
  • 'Approve and Generate PDF' button — disabled until all checklist items are checked
  • 'Reject and Return to Staff' button with required rejection reason
  • Submits to /api/approve-pa-letter/{request_id}

Audit Dashboard (MSP/Admin-Facing)

  • Table of all PA letters generated, with columns: Date, Patient MRN, Payer, PA Type, Status (pending/approved/rejected), Review Time, Edits Made
  • Filters by date range, payer, status, provider
  • Monthly summary: total letters, approval rate, average review time, edit rate, cost (token usage)
  • Export to CSV for compliance reporting

Implementation Notes

  • Use Jinja2 templates with Bootstrap 5 for responsive layout
  • All pages require Entra ID authentication via Azure App Service Easy Auth
  • Session timeout: 15 minutes of inactivity (HIPAA requirement)
  • All form submissions use CSRF protection tokens
  • PDF generation uses WeasyPrint library to convert approved letter to PDF with practice letterhead template
  • PDF is downloaded to clinician's machine for faxing or uploaded to payer portal; NOT stored in the web app (stored in EHR document management)

FHIR Patient Data Connector

Type: integration Middleware component that connects to the practice's EHR via FHIR R4 APIs to auto-populate PA request forms with patient demographics, diagnoses, medications, and recent clinical notes. Implements SMART on FHIR OAuth 2.0 authorization.

Implementation:

fhir_connector.py
python
# fhir_connector.py

import requests
from typing import Optional, Dict, Any
from datetime import datetime, timedelta
import logging

logger = logging.getLogger(__name__)

class FHIRConnector:
    def __init__(self, fhir_base_url: str, client_id: str, client_secret: str, token_url: str):
        self.fhir_base_url = fhir_base_url.rstrip('/')
        self.client_id = client_id
        self.client_secret = client_secret
        self.token_url = token_url
        self._access_token = None
        self._token_expiry = None

    def _get_token(self) -> str:
        if self._access_token and self._token_expiry and datetime.utcnow() < self._token_expiry:
            return self._access_token
        response = requests.post(self.token_url, data={
            'grant_type': 'client_credentials',
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'scope': f'{self.fhir_base_url}/.default'
        })
        response.raise_for_status()
        token_data = response.json()
        self._access_token = token_data['access_token']
        self._token_expiry = datetime.utcnow() + timedelta(seconds=token_data.get('expires_in', 3600) - 60)
        return self._access_token

    def _headers(self) -> Dict[str, str]:
        return {
            'Authorization': f'Bearer {self._get_token()}',
            'Accept': 'application/fhir+json'
        }

    def get_patient(self, patient_id: str) -> Dict[str, Any]:
        resp = requests.get(f'{self.fhir_base_url}/Patient/{patient_id}', headers=self._headers())
        resp.raise_for_status()
        patient = resp.json()
        name = patient.get('name', [{}])[0]
        return {
            'name': f"{name.get('given', [''])[0]} {name.get('family', '')}",
            'dob': patient.get('birthDate', ''),
            'mrn': next((i['value'] for i in patient.get('identifier', []) if i.get('type', {}).get('coding', [{}])[0].get('code') == 'MR'), ''),
            'member_id': next((i['value'] for i in patient.get('identifier', []) if 'insurance' in i.get('system', '').lower()), ''),
            'gender': patient.get('gender', ''),
            'address': self._format_address(patient.get('address', [{}])[0]) if patient.get('address') else ''
        }

    def get_active_conditions(self, patient_id: str) -> list:
        resp = requests.get(
            f'{self.fhir_base_url}/Condition?patient={patient_id}&clinical-status=active',
            headers=self._headers()
        )
        resp.raise_for_status()
        bundle = resp.json()
        conditions = []
        for entry in bundle.get('entry', []):
            resource = entry.get('resource', {})
            coding = resource.get('code', {}).get('coding', [{}])[0]
            conditions.append({
                'code': coding.get('code', ''),
                'display': coding.get('display', ''),
                'system': coding.get('system', ''),
                'onset': resource.get('onsetDateTime', '')
            })
        return conditions

    def get_active_medications(self, patient_id: str) -> list:
        resp = requests.get(
            f'{self.fhir_base_url}/MedicationRequest?patient={patient_id}&status=active',
            headers=self._headers()
        )
        resp.raise_for_status()
        bundle = resp.json()
        medications = []
        for entry in bundle.get('entry', []):
            resource = entry.get('resource', {})
            med_coding = resource.get('medicationCodeableConcept', {}).get('coding', [{}])[0]
            medications.append({
                'name': med_coding.get('display', ''),
                'code': med_coding.get('code', ''),
                'dosage': resource.get('dosageInstruction', [{}])[0].get('text', '') if resource.get('dosageInstruction') else '',
                'authored': resource.get('authoredOn', '')
            })
        return medications

    def get_pending_service_requests(self, patient_id: str) -> list:
        resp = requests.get(
            f'{self.fhir_base_url}/ServiceRequest?patient={patient_id}&status=active',
            headers=self._headers()
        )
        resp.raise_for_status()
        bundle = resp.json()
        requests_list = []
        for entry in bundle.get('entry', []):
            resource = entry.get('resource', {})
            coding = resource.get('code', {}).get('coding', [{}])[0]
            requests_list.append({
                'code': coding.get('code', ''),
                'display': coding.get('display', ''),
                'reason': resource.get('reasonCode', [{}])[0].get('text', '') if resource.get('reasonCode') else '',
                'authored': resource.get('authoredOn', '')
            })
        return requests_list

    def get_recent_notes(self, patient_id: str, days: int = 90) -> list:
        date_cutoff = (datetime.utcnow() - timedelta(days=days)).strftime('%Y-%m-%d')
        resp = requests.get(
            f'{self.fhir_base_url}/DocumentReference?patient={patient_id}&date=ge{date_cutoff}',
            headers=self._headers()
        )
        resp.raise_for_status()
        bundle = resp.json()
        notes = []
        for entry in bundle.get('entry', []):
            resource = entry.get('resource', {})
            for content in resource.get('content', []):
                attachment = content.get('attachment', {})
                if attachment.get('contentType') == 'text/plain' and attachment.get('data'):
                    import base64
                    notes.append({
                        'date': resource.get('date', ''),
                        'type': resource.get('type', {}).get('text', 'Clinical Note'),
                        'text': base64.b64decode(attachment['data']).decode('utf-8')
                    })
        return notes

    def build_pa_context(self, patient_id: str) -> Dict[str, Any]:
        """Aggregates all patient data needed for a PA letter into a single context object."""
        return {
            'patient': self.get_patient(patient_id),
            'conditions': self.get_active_conditions(patient_id),
            'medications': self.get_active_medications(patient_id),
            'service_requests': self.get_pending_service_requests(patient_id),
            'recent_notes': self.get_recent_notes(patient_id)
        }

    @staticmethod
    def _format_address(addr: dict) -> str:
        lines = addr.get('line', [])
        city = addr.get('city', '')
        state = addr.get('state', '')
        postal = addr.get('postalCode', '')
        return f"{', '.join(lines)}, {city}, {state} {postal}".strip(', ')

PDF Letter Generator with Practice Letterhead

Type: skill Converts approved PA letter text into a professional PDF document with the practice's letterhead, formatted for faxing or upload to payer portals. Uses WeasyPrint for HTML-to-PDF conversion.

Implementation:

pdf_generator.py
python
# pdf_generator.py

from weasyprint import HTML
from datetime import datetime
import os
import hashlib

def generate_pa_letter_pdf(
    letter_text: str,
    practice_name: str,
    practice_address: str,
    practice_phone: str,
    practice_fax: str,
    practice_logo_path: str = None
) -> tuple:
    """
    Generates a PDF from the approved PA letter text.
    Returns (pdf_bytes, sha256_hash).
    """
    logo_html = ''
    if practice_logo_path and os.path.exists(practice_logo_path):
        import base64
        with open(practice_logo_path, 'rb') as f:
            logo_b64 = base64.b64encode(f.read()).decode()
        logo_html = f'<img src="data:image/png;base64,{logo_b64}" style="max-height:80px; margin-bottom:10px;">'

    # Convert plain text letter to HTML paragraphs
    letter_paragraphs = letter_text.strip().split('\n\n')
    letter_html_body = '\n'.join(f'<p>{para.replace(chr(10), "<br>")}</p>' for para in letter_paragraphs)

    html_content = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <style>
            @page {{
                size: letter;
                margin: 1in;
            }}
            body {{
                font-family: 'Times New Roman', Times, serif;
                font-size: 12pt;
                line-height: 1.5;
                color: #000000;
            }}
            .letterhead {{
                text-align: center;
                border-bottom: 2px solid #003366;
                padding-bottom: 15px;
                margin-bottom: 25px;
            }}
            .letterhead h1 {{
                font-size: 18pt;
                color: #003366;
                margin: 5px 0;
            }}
            .letterhead p {{
                font-size: 10pt;
                color: #555;
                margin: 2px 0;
            }}
            .letter-body p {{
                margin-bottom: 12px;
                text-align: justify;
            }}
            .disclaimer {{
                font-size: 9pt;
                color: #777;
                border-top: 1px solid #ccc;
                padding-top: 10px;
                margin-top: 30px;
            }}
            .generated-info {{
                font-size: 8pt;
                color: #999;
                text-align: right;
                margin-top: 20px;
            }}
        </style>
    </head>
    <body>
        <div class="letterhead">
            {logo_html}
            <h1>{practice_name}</h1>
            <p>{practice_address}</p>
            <p>Phone: {practice_phone} | Fax: {practice_fax}</p>
        </div>
        <div class="letter-body">
            {letter_html_body}
        </div>
        <div class="disclaimer">
            This letter was drafted with AI assistance and has been reviewed and approved by the undersigned provider.
        </div>
        <div class="generated-info">
            Generated: {datetime.utcnow().strftime('%Y-%m-%d %H:%M UTC')} | Document ID: {{request_id}}
        </div>
    </body>
    </html>
    """

    pdf_bytes = HTML(string=html_content).write_pdf()
    pdf_hash = hashlib.sha256(pdf_bytes).hexdigest()

    return pdf_bytes, pdf_hash

Testing & Validation

  • NETWORK SECURITY TEST: From a workstation on the Clinical VLAN, attempt to access chat.openai.com and claude.ai — both should be blocked by DNSFilter. Verify by checking DNSFilter dashboard for blocked query logs. Then access doximity.com and *.openai.azure.com — both should resolve successfully.
  • ENCRYPTION VERIFICATION: On each enrolled workstation, open PowerShell as admin and run 'manage-bde -status C:' — verify BitLocker status shows 'Protection On' and 'Encryption Method: XTS-AES 256'. Check that all workstations appear as 'Compliant' in the Intune device compliance dashboard.
  • MFA ENFORCEMENT TEST: Attempt to log into Microsoft 365 and the PA letter generator web app from a new device/browser without completing MFA — access should be denied. Verify Conditional Access policy is in 'Enforced' mode (not Report-Only) in Entra ID portal.
  • DOXIMITY GPT FUNCTIONAL TEST: Have a clinician log into DoxGPT and generate a sample PA letter using de-identified test patient data. Verify the output is formatted as a professional letter, contains no hallucinated references, and includes appropriate clinical language. Time the process — target is under 3 minutes for draft generation.
  • COVERMYMEDS INTEGRATION TEST: From within the EHR, initiate an ePA request for a common medication (e.g., a biologic requiring PA). Verify the request flows through to the CoverMyMeds portal, the correct payer is identified, and the ePA form is pre-populated with patient and medication data from the EHR.
  • AZURE OPENAI API TEST: Using curl or Postman, send a test request to the deployed GPT-5.4 endpoint with a sample PA letter prompt (using de-identified data). Verify: (1) Response returns within 10 seconds, (2) HTTP 200 status, (3) Response content is a properly formatted PA letter, (4) No error messages or rate limiting.
  • CUSTOM APP END-TO-END TEST: Access the PA letter generator web app via browser. Log in with Entra ID credentials. Complete the PA request form with test data for a common scenario (e.g., MRI lumbar spine for chronic low back pain, UnitedHealthcare payer). Submit and verify: (1) Draft letter is generated within 15 seconds, (2) Letter includes correct patient demographics, diagnosis codes, and procedure codes, (3) Payer-specific template instructions are reflected in the output, (4) [MISSING] placeholders appear for any omitted fields, (5) Audit log entry appears in Cosmos DB.
  • CLINICIAN REVIEW WORKFLOW TEST: Generate a PA letter draft, then log in as a clinician user. Verify: (1) The approval checklist is displayed and all items must be checked, (2) The 'Approve' button is disabled until all checklist items are checked, (3) Edits to the letter text are preserved, (4) After approval, the audit log is updated with clinician email, approval timestamp, and edit hash, (5) PDF download generates a properly formatted document with practice letterhead.
  • HALLUCINATION DETECTION TEST: Submit a PA request with minimal clinical information and verify the AI output contains [MISSING] placeholders rather than fabricated details. Specifically test: (1) Submit without clinical history — letter should contain [MISSING: clinical history], (2) Submit for an unusual procedure — letter should NOT contain fabricated study citations, (3) Submit with a fictional payer name — system should warn 'No payer-specific template found' and use generic format.
  • HIPAA AUDIT LOG COMPLETENESS TEST: Generate 5 PA letters through the system. Query Cosmos DB and verify each letter has a complete audit record with all required fields populated: request_id, practice_id, timestamps, user identities, patient MRN, payer, codes, model deployment, token counts, output hash, and approval status. Export the audit data and verify it can be formatted into a compliance report.
  • FHIR INTEGRATION TEST (if applicable): From the PA letter generator, use the patient lookup feature to pull data from the EHR via FHIR. Verify: (1) Patient demographics are correctly retrieved, (2) Active diagnoses (ICD-10 codes) are listed, (3) Current medications are populated, (4) Pending service requests appear, (5) All data auto-populates the PA request form correctly. Test with 3 different patients to verify consistency.
  • BACKUP AND RECOVERY TEST: Verify the Datto SIRIS backup includes the EHR server and any local configuration files. Initiate a test restore of a single file from the latest backup. Confirm backup encryption is enabled and verify backup reports are being sent to the MSP monitoring dashboard. Document the RPO (Recovery Point Objective) and RTO (Recovery Time Objective) achieved.
Azure OpenAI API test request for GPT-5.4 PA letter endpoint
bash
curl -X POST https://aoai-pa-lettergen.openai.azure.com/openai/deployments/gpt-5.4/chat/completions?api-version=2024-08-06 -H 'Content-Type: application/json' -H 'api-key: <key>' -d '{"messages":[{"role":"system","content":"You are a PA letter assistant."},{"role":"user","content":"Draft a PA letter for a knee MRI."}],"temperature":0.3}'

Client Handoff

CLIENT HANDOFF DOCUMENTATION AND TRAINING AGENDA:

1
DOCUMENTATION PACKAGE (leave with practice): AI Prior Authorization Letter System User Guide (step-by-step with screenshots) | Quick Reference Cards (laminated, one per PA workstation) covering: form fill workflow, template selection guide, clinician review checklist, common troubleshooting steps | Payer-Specific Template Reference Guide (which template to use for which carrier) | AI Acceptable Use Policy (signed by all staff during training) | HIPAA BAA Inventory Document (list of all vendors with BAA status and renewal dates) | Emergency Contact Card: MSP help desk number, escalation path, after-hours support process | Network Diagram (sanitized) showing VLAN structure, firewall rules, and data flow | Monthly Compliance Report Template (for practice manager)
2
STAFF TRAINING SESSION (90 minutes — PA coordinators and front desk): System login and navigation (15 min) | Entering a PA request: patient data, diagnosis codes, procedure codes, clinical history (20 min) | Selecting the correct payer template (10 min) | Understanding AI output: what to look for, common issues, [MISSING] placeholders (15 min) | Submitting draft for clinician review (10 min) | PDF generation and fax/portal submission (10 min) | Troubleshooting and when to call the MSP (10 min)
3
CLINICIAN TRAINING SESSION (60 minutes — all reviewing providers): Why AI-assisted PA letters require human review (hallucination risks, liability) (10 min) | Review workflow walkthrough with live demo (15 min) | How to spot AI hallucinations: fabricated citations, incorrect dosages, wrong policy numbers (15 min) | Editing and approving letters (10 min) | State AI disclosure requirements and attestation language (10 min)
4
PRACTICE MANAGER BRIEFING (30 minutes): ROI review: time saved per PA letter, monthly volume, cost savings projections | Monthly compliance reporting: what to review, red flags to escalate | Vendor BAA tracking: renewal dates, what to do if a vendor changes terms | When to request template updates (new payers, changed requirements) | MSP SLA review: response times, included vs. billable support
5
SUCCESS CRITERIA REVIEW (jointly with practice leadership): PA letter drafting time reduced from 20-45 minutes to under 5 minutes of clinician review | 90%+ of generated letters require only minor edits (measured via audit log edit rates) | Zero HIPAA incidents related to AI tool usage | Staff satisfaction score of 4+/5 on usability survey (administered at 30-day mark) | PA approval rate maintained or improved vs. baseline (measure at 90-day mark)
6
30-DAY CHECK-IN SCHEDULED: Book a 60-minute follow-up visit for 30 days post-go-live to review usage metrics, address any workflow issues, collect staff feedback, and optimize prompt templates based on real-world performance.

Maintenance

1. Monitoring Cadence

  • Daily: Automated monitoring of Azure App Service health, API response times, and error rates via Azure Monitor alerts. Alert threshold: >5% error rate or >30-second response time triggers MSP notification.
  • Weekly: Review SentinelOne threat dashboard for the practice's endpoints. Review DNSFilter logs for any attempted access to blocked AI tools (potential policy violations). Check Datto backup success/failure reports.
  • Monthly: Generate and review PA letter audit report from Cosmos DB. Review metrics: total letters generated, approval rate, average review time, edit rate, token usage/cost. Share summary report with practice manager. Review Azure cost report and optimize if needed.
  • Quarterly: Conduct HIPAA Security Rule compliance spot-check. Verify all BAAs are current. Test backup restore procedure. Review and update payer-specific prompt templates based on staff feedback and payer policy changes. Conduct 30-minute refresher training if usage patterns indicate need.
  • Annually: Full HIPAA Risk Assessment update. Review all vendor contracts and BAAs for renewal. Assess Azure OpenAI model updates (new GPT versions) and test before upgrading. Review CMS-0057-F compliance timeline and plan for FHIR PA API integration by January 2027 deadline.

2. Update Schedule

  • Azure OpenAI model versions: Test new model versions (e.g., GPT-5.4 updates) in a staging deployment before updating production. Allow 2-week testing period with side-by-side comparison of output quality. Update quarterly or when significant model improvements are released.
  • Prompt templates: Review and update payer-specific templates when: (a) a payer changes their PA requirements or medical policies, (b) staff report consistent issues with a specific payer's letters, (c) new payers are added to the practice's panel. Template updates should be versioned in Cosmos DB with change logs.
  • Application code: Apply security patches within 72 hours of release. Feature updates deployed quarterly with practice approval. All deployments use Blue/Green deployment slots in Azure App Service for zero-downtime updates.
  • Security stack: SentinelOne, DNSFilter, and FortiGate signature updates are automatic. Firmware updates for FortiGate applied quarterly during maintenance windows (Saturday 6-8 AM). Windows updates managed via Intune with 7-day deferral for testing.

3. SLA Considerations

  • Response time — Critical issues (system down, PHI exposure): 1-hour response, 4-hour resolution target.
  • Response time — High (workflow disruption): 4-hour response, 1 business day resolution.
  • Response time — Medium (template updates, minor bugs): 1 business day response, 1 week resolution.
  • Response time — Low (feature requests, optimization): 1 week response, scheduled for next quarterly update.
  • Availability target: 99.5% uptime for the PA letter generator during business hours (M-F 7AM-7PM local time). Azure App Service SLA is 99.95%; the bottleneck is typically the practice's internet connection.
  • Data retention: Audit logs retained for 7 years per HIPAA. Cosmos DB serverless pricing means storage costs scale with volume (~$0.25/GB/month). At ~1KB per audit log entry and 500 letters/month, annual storage is negligible (<$1/year).

4. Escalation Paths

  • Level 1 (Practice Staff): Check quick reference guide, verify internet connectivity, try clearing browser cache, check Azure status page (status.azure.com).
  • Level 2 (MSP Help Desk): Remote troubleshooting, App Service restart, DNS/firewall rule verification, Entra ID access issues.
  • Level 3 (MSP Engineering): Azure resource configuration, FHIR integration issues, prompt template engineering, code deployments.
  • Level 4 (Vendor Escalation): Azure OpenAI service outages (Microsoft Support), EHR API issues (EHR vendor support), SaaS platform issues (vendor support team).

5. CMS-0057-F Compliance Roadmap

Alternatives

...

DoxGPT Only (Zero-Cost Quick Start)

Deploy Doximity GPT as the sole PA letter drafting tool. Each clinician uses their verified Doximity account to access DoxGPT, pastes patient information into the prompt, and receives a draft PA letter. No custom infrastructure, no API costs, no application development. MSP role is limited to HIPAA stack deployment (firewall, EDR, MFA, backup) and staff training.

Turnkey SaaS Platform Only (Tandem AI / Linear Health / Ethermed)

Skip the custom Azure OpenAI build entirely and rely on a purpose-built PA automation SaaS platform. The vendor handles all AI model hosting, EHR integration, payer connectivity, and compliance. MSP role is vendor selection, onboarding coordination, HIPAA stack management, and ongoing vendor relationship management.

Microsoft 365 Copilot with HIPAA Controls

If the practice is already on Microsoft 365 E3/E5 with Copilot licenses, use Microsoft 365 Copilot as the AI letter drafting engine. Staff draft PA letters in Word using Copilot, with custom prompt templates stored in SharePoint. Copilot is covered under the Microsoft HIPAA BAA when used within the M365 enterprise environment.

Open-Source LLM Self-Hosted (Llama 3 / Mistral on Azure)

Instead of using Azure OpenAI (GPT-5.4), deploy an open-source LLM (Meta Llama 3 70B or Mistral Large) on Azure Virtual Machines with GPU (NC-series). This eliminates per-token API costs and keeps all data processing within the MSP-controlled Azure environment with no third-party AI vendor dependency.

Hybrid Approach: Voice AI (Simbie AI / Asepha AI) + Letter Generation

Deploy a voice AI platform like Simbie AI or Asepha AI that automates the phone-based PA workflow (calling payers for benefit verification, status checks, peer-to-peer scheduling) alongside the letter generation system. This addresses both the written and verbal components of the PA process.

Want early access to the full toolkit?