56 min readDeterministic automation

Implementation Guide: Trigger billing codes and claims submission from finalized visit notes

Step-by-step implementation guide for deploying AI to trigger billing codes and claims submission from finalized visit notes for Healthcare clients.

Hardware Procurement

Tower Server for On-Premise EHR/PM Hosting

Dell TechnologiesPowerEdge T360Qty: 1

$3,500–$5,000 per unit (MSP cost, configured with Windows Server 2025 Standard) / $4,500–$6,500 suggested resale

Hosts on-premise EHR/PM database and runs the local integration broker service that monitors visit note finalization events. Required only if the practice runs a locally-hosted EHR (e.g., on-premise Dentrix, NextGen, or AdvancedMD local install). Not needed for fully cloud-hosted EHR environments like athenaOne. Spec: Intel Xeon E-2400 series, 32 GB ECC RAM, 2×2 TB SATA SSD in RAID 1, Windows Server 2025 Standard, iDRAC for remote management.

HIPAA-Compliant Next-Gen Firewall

SonicWallTZ470Qty: 1

$800–$900 (hardware) + $400–$600/yr TotalSecure Essential subscription (MSP cost) / $1,400–$1,800 hardware resale + $600–$900/yr subscription resale

Provides network perimeter security with deep packet inspection, intrusion prevention (IPS), gateway antivirus, content filtering, and VPN for remote management. Enforces network segmentation between billing/EHR VLAN and general office traffic. Required for HIPAA Security Rule compliance — all ePHI in transit must be protected. TZ470 supports up to 25 users and 3 Gbps firewall throughput, suitable for most small-to-medium practices.

Business Continuity and Disaster Recovery Appliance

Datto (Kaseya)SIRIS 5 2TBQty: 1

$995–$1,500/month all-inclusive (MSP cost, includes hardware, cloud replication, and support) / $1,300–$2,000/month suggested resale

Provides image-based backup of the on-premise server with instant local and cloud virtualization capability. Ensures the billing automation pipeline can be restored within minutes of a hardware failure or ransomware event. HIPAA requires documented disaster recovery plans with tested restore procedures. Datto SIRIS is sold exclusively through MSP partners.

Managed Network Switch

CiscoCBS250-8T-E-2GQty: 1

$180–$250 (MSP cost) / $300–$400 suggested resale

Layer 2+ managed switch for creating VLANs to segment the billing/EHR network from general office traffic. Supports 802.1Q VLAN tagging, QoS for prioritizing claim submission traffic, and SNMP for remote monitoring. Required for HIPAA network segmentation compliance.

UPS Battery Backup

APC (Schneider Electric)SMT1500RM2UCQty: 1

$550–$700 (MSP cost) / $800–$950 suggested resale

Provides 15–20 minutes of battery runtime for the server and firewall during power outages, allowing graceful shutdown or generator switchover. Prevents claim submission interruptions and data corruption. SmartConnect cloud monitoring enables remote UPS health checks.

Software Procurement

athenaOne EHR/PM Platform (Medical) or Dentrix Ascend (Dental)

athenahealth / Henry Schein OneathenaOne / Dentrix Ascend

athenaOne: $140/provider/month + 4%–7% of collections; Dentrix Ascend: $500–$1,200/month per practice

Primary EHR and Practice Management system — the source of finalized visit notes and the destination for auto-assigned billing codes. The practice likely already has one of these; if not, selection and migration is a Phase 0 project. athenaOne offers 800+ RESTful API endpoints; Dentrix Ascend and Open Dental both provide APIs for claims management. License type: SaaS — per-provider/month (athenaOne: percentage-of-collections model; Dentrix Ascend: subscription).

Fathom Autonomous Medical Coding Engine

Fathom (acquired by Commure)

Quote-based; typically $2–$8 per encounter depending on volume and specialty. Estimated $500–$2,000/month for a 3–5 provider practice processing 400–1,000 encounters/month

AI-powered autonomous coding engine that reads finalized clinical documentation and assigns CPT, ICD-10-CM, and HCPCS codes with modifiers. Achieves 95.5% encounter-level automation rate. Recognized as #1 for Reducing Cost of Care in KLAS 2025 Emerging Solutions Top 20. You only pay for fully autonomous results — partial or suggested codes are not billed. For dental practices, this is replaced by built-in CDT coding in Dentrix/Open Dental or Ventus AI for DSOs.

$0.15–$0.25 per claim; approximately $45–$75/month for 300 claims. No monthly minimums or setup fees.

Electronic claims clearinghouse for formatting, scrubbing, and submitting 837P (professional) and 837D (dental) claims to insurance payers. Also receives 835 ERA remittance advice for auto-posting. Claim.MD offers a REST API for programmatic claim submission, status checking, and ERA retrieval. Best value option for small-to-medium practices.

Microsoft Power Automate Premium

MicrosoftSaaS — per-user/monthQty: per user/month

$15/user/month for cloud flows; $215/bot/month add-on for hosted RPA if needed

HIPAA-compliant workflow automation middleware that orchestrates the visit-finalized → auto-code → claim-submit pipeline. Microsoft signs a BAA covering Power Automate. Used to build the event-driven flows that poll or receive webhooks from the EHR, invoke the coding engine API, and trigger claim submission. Alternative: Keragon for a healthcare-specific platform.

Usage-based pricing; all paid plans include HIPAA compliance, BAA, and SOC 2 Type II certification. Contact for quote.

Purpose-built healthcare workflow automation platform — alternative to Power Automate for practices wanting a no-code healthcare-specific integration tool. Pre-built connectors for common EHRs and clearinghouses. HIPAA-compliant by default with built-in audit logging.

Compliancy Group - The Guard HIPAA Compliance Platform

Compliancy GroupThe GuardQty: Per practice

$200–$500/month per practice

Automated HIPAA compliance management including risk assessments, policy generation, BAA tracking, employee training tracking, and incident management. Generates the documentation required for HIPAA audits. MSP can resell as Compliance-as-a-Service offering.

Windows Server 2025 Standard

MicrosoftWindows Server 2025 StandardQty: 16-core minimum

$1,069 MSRP for 16-core license (often bundled with server purchase at $600–$800 OEM)

Server operating system for on-premise EHR/PM host and integration broker. Required only for on-premise deployments. Includes Hyper-V for potential VM isolation of billing services. License type: Perpetual — per-core licensing (16-core minimum).

SentinelOne Singularity for Endpoints

SentinelOneSingularity for EndpointsQty: per-endpoint/month

$5–$8/endpoint/month (MSP pricing); suggested resale $8–$12/endpoint/month

EDR/XDR endpoint protection for all workstations and servers touching ePHI. HIPAA requires technical safeguards including malware protection. SentinelOne signs BAAs and provides HIPAA-appropriate audit logging of endpoint activity.

Prerequisites

  • Active EHR/PM system with API access enabled (athenahealth athenaOne, AdvancedMD, NextGen, Tebra for medical; Dentrix Ascend, Open Dental, or Eaglesoft for dental). Confirm API/interface tier is included in the practice's current license.
  • Dedicated internet connection with minimum 25 Mbps symmetric bandwidth. Redundant internet (failover LTE/5G) strongly recommended for claim submission continuity.
  • Valid NPI (National Provider Identifier) numbers for all billing providers and the practice group.
  • Active payer enrollment and EDI enrollment for all insurance companies the practice bills — claims cannot be submitted electronically to a payer the practice is not enrolled with.
  • Signed Business Associate Agreements (BAAs) with every vendor in the chain: EHR vendor, coding engine vendor, clearinghouse, workflow automation platform, backup provider, and MSP.
  • Current HIPAA Security Risk Assessment completed within the last 12 months. If not current, this must be performed as part of Phase 1 Discovery.
  • Multi-Factor Authentication (MFA) enforced on all user accounts that access ePHI — this includes EHR logins, clearinghouse portals, workflow automation admin accounts, and server RDP access. Required under the 2025 HIPAA Security Rule update.
  • Domain-joined workstations with current EDR/antivirus protection (SentinelOne or equivalent) and full-disk encryption (BitLocker) enabled.
  • Designated billing staff member or office manager to serve as the practice-side subject matter expert for coding rules validation and testing sign-off.
  • Administrative credentials for the EHR/PM system to configure API keys, webhook endpoints, and user roles for the automation service account.
  • Clearinghouse account (Claim.MD, Availity, or Waystar) with API access enabled and payer routing configured for all active insurance companies.
  • Practice's current CPT, ICD-10-CM (medical), and CDT (dental) code sets documented, including any specialty-specific or frequently used code combinations.

Installation Steps

...

Step 1: Conduct Discovery Audit and Document Current Billing Workflow

Before touching any technology, meet with the practice's billing staff, office manager, and providers to map the current end-to-end billing workflow. Document: (1) How visit notes are currently finalized (saved, signed, locked) in the EHR. (2) Who assigns billing codes and how (manual lookup, templates, copy-forward). (3) How claims are currently submitted (manual entry in clearinghouse portal, batch export, integrated submission). (4) Current first-pass claim acceptance rate and top denial reasons. (5) Average time from visit completion to claim submission (coding lag). (6) Specialty-specific coding patterns and most commonly used CPT/ICD-10 code pairs. This information is critical for configuring the rules engine and measuring post-implementation improvement.

Note

Allocate 2–3 days for this phase. Request read-only access to the EHR reporting module to pull denial analytics and coding lag metrics. Document everything in a shared spreadsheet that will become the rules engine configuration source. Take screenshots of the current EHR workflow screens for reference during integration build.

Step 2: Perform HIPAA Security Risk Assessment

If the practice does not have a current (within 12 months) HIPAA Security Risk Assessment, conduct one now. This is both a compliance requirement and a practical necessity — you need to identify security gaps before connecting automated billing systems that will process ePHI at scale. Use the Compliancy Group platform or equivalent tool to: (1) Inventory all systems that store, process, or transmit ePHI. (2) Identify threats and vulnerabilities. (3) Assess current security controls. (4) Determine risk levels. (5) Document remediation plan. The billing automation system itself must be included in the risk assessment scope.

Note

This step is non-negotiable. HIPAA requires a documented risk assessment before implementing new systems that handle ePHI. If the practice has never had one, expect 1–2 weeks to complete. If they have a recent one, update it to include the new automation components (coding engine, clearinghouse API, workflow automation platform). Bill this as a separate compliance engagement if needed.

Step 3: Deploy and Configure Network Infrastructure

Install and configure the HIPAA-compliant network foundation. This includes: (1) Install SonicWall TZ470 firewall, replacing any consumer-grade router. (2) Configure VLANs on the Cisco CBS250 switch — create a dedicated 'Billing/EHR' VLAN (VLAN 10) isolated from the general office network (VLAN 1) and a management VLAN (VLAN 99). (3) Configure firewall rules to restrict Billing/EHR VLAN traffic to only required destinations (EHR cloud endpoints, clearinghouse API endpoints, coding engine endpoints). (4) Enable IPS, gateway antivirus, and content filtering on the SonicWall. (5) Configure site-to-site VPN or SSL-VPN for MSP remote management. (6) Install UPS and connect server and firewall to protected outlets. (7) Configure SNMP monitoring on switch and firewall for RMM integration.

SonicWall TZ470 CLI setup and Cisco CBS250 VLAN configuration
shell
# SonicWall TZ470 initial setup via CLI (after connecting to X0 management interface at 192.168.168.168)
# Access via https://192.168.168.168 in browser, complete setup wizard
# Create VLANs on Cisco CBS250
configure terminal
vlan 10
name BILLING-EHR
exit
vlan 99
name MANAGEMENT
exit
interface range GigabitEthernet1-4
switchport mode access
switchport access vlan 10
exit
interface GigabitEthernet8
switchport mode access
switchport access vlan 99
exit
# Configure SonicWall firewall rules (via web GUI)
# Network > Interfaces > X0 > Add Sub-Interface for VLAN 10 (192.168.10.0/24)
# Firewall > Access Rules > From BILLING-EHR to WAN:
#   Allow: HTTPS (443) to EHR cloud endpoints, clearinghouse API endpoints
#   Allow: DNS (53) to designated DNS servers only
#   Deny: All other outbound
# Security Services > Enable Gateway Anti-Virus, IPS, Content Filter
Note

If the practice already has a HIPAA-compliant firewall and network segmentation, skip the hardware deployment and focus on adding firewall rules for the new clearinghouse and coding engine API endpoints. Document all firewall rules in the HIPAA security documentation. Ensure the SonicWall firmware is updated to the latest version before production deployment.

Step 4: Deploy On-Premise Server (If Required)

If the practice runs a locally-hosted EHR (e.g., Dentrix G7, NextGen on-premise, Eaglesoft), deploy the Dell PowerEdge T360 server. If the practice uses a fully cloud-hosted EHR (athenaOne, Dentrix Ascend, Tebra), skip this step. Server deployment includes: (1) Rack or position the tower server in a physically secure, climate-controlled location (locked server closet or cabinet). (2) Install Windows Server 2025 Standard. (3) Configure RAID 1 on the 2×2TB SSDs. (4) Join to domain or configure local security policies. (5) Enable BitLocker drive encryption. (6) Install EHR/PM server application per vendor requirements. (7) Configure Windows Firewall to allow only required ports. (8) Set up Datto SIRIS agent for backup. (9) Enable Windows Event Log forwarding to SIEM/RMM.

powershell
# After Windows Server 2025 installation and domain join:
# Enable BitLocker on system drive
Enable-BitLocker -MountPoint 'C:' -EncryptionMethod XtsAes256 -UsedSpaceOnly -RecoveryPasswordProtector
# Store recovery key in AD or secure location
Get-BitLockerVolume -MountPoint 'C:' | Select-Object -ExpandProperty KeyProtector
# Configure Windows Firewall - allow only required EHR ports (example for Dentrix)
New-NetFirewallRule -DisplayName 'Dentrix SQL' -Direction Inbound -Protocol TCP -LocalPort 1433 -Action Allow -Profile Domain
New-NetFirewallRule -DisplayName 'Dentrix App' -Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow -Profile Domain
# Block all other inbound by default
Set-NetFirewallProfile -Profile Domain,Private,Public -DefaultInboundAction Block
# Install Datto SIRIS agent
# Download from partner portal: https://partner.datto.com
# Run installer: DattoWindowsAgent_x64.msi /quiet REGISTRATION_TOKEN=<your-token>
# Configure NTP
w32tm /config /manualpeerlist:'time.nist.gov' /syncfromflags:manual /reliable:yes /update
Restart-Service w32time
Note

Physical server security is a HIPAA requirement. The server must be in a locked room or cabinet with restricted key/badge access. Document who has physical access. If the practice has no suitable server room, consider a lockable server cabinet like the Tripp Lite SRW12US (wall-mount) or StarTech RK1236BKF (floor-standing). Temperature should stay below 80°F/27°C.

Step 5: Configure EHR API Access and Service Account

Set up the API credentials and service account that the automation pipeline will use to interact with the EHR/PM system. This is vendor-specific but follows a common pattern: (1) Create a dedicated service account in the EHR with minimum necessary permissions (read visit notes, read/write charge records, read patient demographics). (2) Generate API keys or OAuth2 credentials. (3) Configure webhook or polling endpoint for visit-finalized events. (4) Test API connectivity from the automation platform.

EHR API connectivity tests
bash
# athenahealth, Open Dental, and Dentrix Ascend

# athenahealth API setup example:
# 1. Register at https://developer.athenahealth.com
# 2. Create an application and obtain client_id and client_secret
# 3. Test OAuth2 token acquisition:
curl -X POST 'https://api.athenahealth.com/oauth2/v1/token' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&scope=athena/service/Athenanet.MDP.*'

# 4. Test encounter retrieval:
curl -X GET 'https://api.athenahealth.com/v1/{practiceid}/chart/encounters?departmentid={deptid}&status=CLOSED' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'

# Open Dental API setup example:
# 1. Enable API module in Open Dental (Setup > Advanced Setup > API)
# 2. Generate API key
# 3. Test connectivity:
curl -X GET 'https://api.opendental.com/api/v1/appointments?aptStatus=2' \
  -H 'Authorization: ODFHIR YOUR_API_KEY'

# Dentrix Ascend API:
# Contact Henry Schein One partner support for API access credentials
# API documentation at https://developer.henryschein.com
Note

API access may require an upgraded EHR license tier. Verify with the EHR vendor before starting. For athenahealth, the API is included but requires developer registration and approval. For Open Dental, API access is included in the standard subscription. For Dentrix G7 (on-premise), API access is limited — you may need to use the Dentrix Connector or HL7 interface instead. Always use a dedicated service account, never a staff member's personal credentials. Store credentials in a secrets manager (Azure Key Vault if using Power Automate).

Step 6: Set Up Clearinghouse Account and API Integration

Establish the Claim.MD clearinghouse account (or configure the existing clearinghouse) with API access for programmatic claim submission. Steps: (1) Create Claim.MD account at claim.md. (2) Complete payer enrollment for all insurance companies the practice bills. (3) Generate API credentials. (4) Configure ERA (835) auto-delivery. (5) Test connectivity with a sample claim submission to the Claim.MD test environment.

Claim.MD API connectivity and submission tests
bash
# Claim.MD API setup:
# 1. Register at https://www.claim.md and obtain AccountKey
# 2. Test API connectivity with a ping:
curl -X POST 'https://svc.claim.md/services/claimstatus/' \
  -H 'Content-Type: application/json' \
  -d '{"AccountKey": "YOUR_ACCOUNT_KEY", "Claim": {"PayerID": "00001", "ClaimNumber": "TEST001"}}'

# 3. Test 837P claim submission (test mode):
curl -X POST 'https://svc.claim.md/services/upload/' \
  -H 'Content-Type: application/json' \
  -d '{
    "AccountKey": "YOUR_ACCOUNT_KEY",
    "TestMode": true,
    "File": {
      "FileType": "837P",
      "FileContent": "BASE64_ENCODED_837P_CONTENT"
    }
  }'

# 4. Retrieve ERA (835) responses:
curl -X POST 'https://svc.claim.md/services/era/' \
  -H 'Content-Type: application/json' \
  -d '{"AccountKey": "YOUR_ACCOUNT_KEY", "StartDate": "2025-01-01", "EndDate": "2025-01-31"}'
Note

Payer enrollment can take 2–6 weeks per payer. Start this process immediately after project kickoff. The practice may already have a clearinghouse — if so, verify it has API access and consider whether migration to Claim.MD is worth the effort. If the practice uses Waystar or Availity, those can also be integrated. Claim.MD is recommended for new setups due to its simple per-claim pricing, no minimums, and clean API.

Step 7: Deploy and Configure Fathom Autonomous Coding Engine (Medical) or Rules Engine (Dental)

For medical practices: Integrate the Fathom autonomous coding engine to automatically assign CPT and ICD-10-CM codes from finalized visit notes. For dental practices: Configure the built-in CDT coding rules in Dentrix/Open Dental, or deploy Ventus AI for DSOs. Fathom integration steps: (1) Contact Fathom sales at fathom.health for practice onboarding. (2) Configure EHR integration (Fathom has native integrations with athenahealth, AdvancedMD, and others). (3) Map specialty-specific coding preferences. (4) Configure confidence thresholds and human-review routing. (5) Run parallel coding validation (Fathom codes alongside human coder for 2–4 weeks).

  • Fathom integration is primarily configured through their admin portal
  • For practices where Fathom is not available or not cost-effective, deploy a deterministic rules engine (see custom_ai_components section)
  • For Open Dental CDT auto-coding, configure procedure code defaults: Lists > Procedure Codes > Set default codes per treatment type
  • Enable auto-code feature in Open Dental: Setup > Auto Codes
  • Map diagnosis descriptions to CDT codes in the auto-code table
  • For Dentrix, configure Quick Pick lists and treatment plan templates: Dentrix Office Manager > Maintenance > Practice Setup > Procedure Code Setup
  • Create treatment plan templates that auto-populate CDT codes based on diagnosis
Note

Fathom requires a contract and onboarding process that takes 2–4 weeks. For practices that cannot justify the per-encounter cost, use the deterministic rules engine described in the custom_ai_components section. For dental practices, built-in CDT auto-coding in Dentrix or Open Dental is usually sufficient without a third-party coding engine, since dental coding is less complex than medical coding (CDT has ~900 codes vs 80,000+ CPT/ICD-10 combinations).

Step 8: Build the Workflow Automation Pipeline in Power Automate

Create the core automation flow in Microsoft Power Automate that orchestrates the entire visit-finalized → auto-code → claim-submit pipeline. The flow has three main stages: (1) TRIGGER — Poll the EHR API every 5 minutes for newly finalized encounters (or receive webhook if EHR supports it). (2) PROCESS — For each new encounter, extract clinical data, invoke the coding engine (Fathom API or local rules engine), and write codes back to the EHR charge record. (3) SUBMIT — Format the coded encounter as an 837P/D claim, invoke the clearinghouse API to submit, and log the result. All ePHI must be handled within HIPAA-compliant connectors. Microsoft Power Automate is covered under Microsoft's BAA.

Power Automate flow pseudocode / flow definition for Auto-Bill-From-Finalized-Notes
plaintext
# Power Automate flow is built in the web designer at https://make.powerautomate.com
# Below is the pseudocode / flow definition:

# FLOW: Auto-Bill-From-Finalized-Notes
# TRIGGER: Recurrence (every 5 minutes)
# OR: When an HTTP request is received (if EHR supports webhooks)

# ACTION 1: HTTP - GET finalized encounters
#   Method: GET
#   URI: https://api.athenahealth.com/v1/{practiceid}/chart/encounters
#   Headers: Authorization: Bearer @{variables('access_token')}
#   Query: status=CLOSED&lastmodifieddate=@{utcNow(-5min)}

# ACTION 2: Parse JSON - Extract encounter list
# ACTION 3: Apply to each encounter:
#   3a: HTTP - GET encounter details (diagnoses, procedures, notes)
#   3b: HTTP - POST to Fathom API or invoke rules engine
#   3c: Parse coding response (CPT codes, ICD-10 codes, modifiers)
#   3d: HTTP - PUT codes back to EHR charge record
#   3e: HTTP - POST formatted 837P to Claim.MD API
#   3f: Log result to SharePoint list or SQL table (audit trail)

# ACTION 4: Send summary email to billing staff
# ACTION 5: Error handling - On failure, create alert in RMM/PSA

# Store all credentials in Azure Key Vault
# Enable flow run history retention for HIPAA audit trail
Note

Power Automate Premium license ($15/user/month) is required for HTTP connector actions used for API calls. The flow should run under a dedicated service account, not a staff member's account. Enable detailed logging and set up failure alerts that route to both the MSP NOC and the practice billing manager. For practices preferring a healthcare-specific no-code platform, use Keragon instead of Power Automate — it has pre-built healthcare connectors but higher cost. See the custom_ai_components section for the detailed flow specification.

Step 9: Configure the Claims Scrubbing Rules Engine

Before claims are submitted to the clearinghouse, they must pass through a scrubbing layer that checks for common errors that cause denials. Configure the scrubbing rules: (1) Verify all required 837P/D fields are populated (patient demographics, NPI, tax ID, payer ID, date of service, place of service, diagnosis pointers). (2) Apply CCI (Correct Coding Initiative) edit checks — flag code pairs that cannot be billed together. (3) Apply LCD/NCD (Local/National Coverage Determination) rules for Medicare patients. (4) Validate modifier usage (e.g., modifier 25 for significant E/M with procedure). (5) Check for duplicate claims. (6) Validate ICD-10 code specificity (no unspecified codes when specific codes exist). (7) Verify timely filing — flag claims approaching payer-specific filing deadlines.

CCI edit check implementation (Python pseudocode for rules engine)
python
# CCI edit check implementation (Python pseudocode for rules engine)
# Download CCI edit files from CMS: https://www.cms.gov/medicare/coding-billing/national-correct-coding-initiative-edits

# Example CCI check function:
def check_cci_edits(cpt_codes: list) -> list:
    violations = []
    for i, code1 in enumerate(cpt_codes):
        for code2 in cpt_codes[i+1:]:
            if (code1, code2) in CCI_EDIT_TABLE:
                edit = CCI_EDIT_TABLE[(code1, code2)]
                if edit['modifier_allowed']:
                    violations.append(f'CCI edit: {code1}/{code2} - modifier may override')
                else:
                    violations.append(f'CCI edit: {code1}/{code2} - HARD DENY, cannot bill together')
    return violations

# Claim.MD also provides built-in scrubbing — enable in account settings:
# Account > Settings > Enable Pre-Submission Edits > ON
# Account > Settings > CCI Edits > ON
# Account > Settings > Duplicate Claim Check > ON
Note

Claim.MD includes basic claim scrubbing in its service. For more comprehensive scrubbing, AdvancedMD's ClaimInspector or Waystar's claim editing engine can be used as the primary clearinghouse. The rules engine should be configurable by the MSP without code changes — use a lookup table in a database or spreadsheet that maps scrubbing rules. CCI edit files are updated quarterly by CMS and must be refreshed. Payer-specific rules vary and are the most common source of denials — build a payer-rule library over time from denial feedback.

Step 10: Configure 837P/D Claim Formatting

Build the module that transforms coded encounter data into ANSI X12 837P (professional/medical) or 837D (dental) format for clearinghouse submission. The 837 format is a strict HIPAA-mandated EDI standard with specific segment and loop requirements. Most clearinghouses accept either raw 837 files or structured JSON that they convert to 837 internally. Claim.MD accepts JSON payloads that it converts to 837, simplifying this step significantly.

Example 837P JSON payload for Claim.MD API (build in Power Automate HTTP action)
json
{
  "AccountKey": "YOUR_KEY",
  "Claims": [{
    "PatientFirstName": "John",
    "PatientLastName": "Doe",
    "PatientDOB": "1985-03-15",
    "PatientGender": "M",
    "SubscriberID": "XYZ123456",
    "PayerID": "62308",
    "PayerName": "Blue Cross Blue Shield",
    "BillingProviderNPI": "1234567890",
    "BillingProviderTaxID": "12-3456789",
    "RenderingProviderNPI": "0987654321",
    "PlaceOfService": "11",
    "DateOfService": "2025-07-10",
    "DiagnosisCodes": ["J06.9", "R05.9"],
    "ServiceLines": [{
      "CPTCode": "99213",
      "Modifier1": "",
      "DiagnosisPointer": "1,2",
      "Units": 1,
      "ChargeAmount": 150.00
    }]
  }]
}
  • For raw 837P file generation (if clearinghouse requires it), use a library like pyx12 (Python) or EDI-X12 (Node.js)
  • Install pyx12 via: pip install pyx12
  • Refer to ASC X12N 837P Implementation Guide for segment details
Note

Using Claim.MD's JSON API is strongly recommended over raw 837 file generation — it eliminates the complexity of EDI segment formatting and lets the clearinghouse handle X12 compliance. If the practice's existing clearinghouse requires raw 837 files, consider migrating to Claim.MD or using a library like pyx12 for Python-based 837 generation. The 837D format for dental claims uses CDT codes instead of CPT codes but follows a similar structure.

Step 11: Configure ERA (835) Auto-Posting Integration

Set up the reverse flow: when insurance payers respond with payment information via 835 Electronic Remittance Advice, automatically retrieve and post payments back to the EHR/PM system. Steps: (1) Configure Claim.MD to receive 835 ERAs from enrolled payers. (2) Build a Power Automate flow that polls Claim.MD for new ERAs daily. (3) Parse the ERA data to extract payment amounts, adjustments, denial codes, and patient responsibility. (4) Post payment data back to the EHR/PM system via API. (5) Flag denied line items for billing staff review.

Power Automate flow for ERA retrieval and posting
plaintext
# Power Automate flow for ERA retrieval and posting:
# TRIGGER: Recurrence (daily at 7:00 AM)

# ACTION 1: HTTP - GET new ERAs from Claim.MD
#   Method: POST
#   URI: https://svc.claim.md/services/era/
#   Body: {"AccountKey": "KEY", "StartDate": "@{addDays(utcNow(),-1)}", "EndDate": "@{utcNow()}"}

# ACTION 2: Parse JSON - Extract ERA records
# ACTION 3: Apply to each ERA:
#   3a: Match ERA to original claim by claim number
#   3b: For paid claims - POST payment to EHR via API
#   3c: For denied claims - Create work item in billing queue
#   3d: Log ERA processing result

# ACTION 4: Send daily ERA summary to billing manager
# Include: total payments posted, denials flagged, discrepancies found
Note

ERA auto-posting is a high-value automation that saves significant billing staff time but carries risk — incorrect payment posting can cause reconciliation nightmares. Start with a 'review and approve' mode where the automation prepares the posting but requires a human click to confirm. After 30 days of validated accuracy, switch to full auto-posting with exception-based review. Always maintain an audit trail of every ERA transaction.

Step 12: Implement Audit Logging and HIPAA Compliance Monitoring

Configure comprehensive audit logging across all components of the billing automation pipeline. HIPAA requires that all access to ePHI be logged, and the 2025 Security Rule update mandates enhanced audit controls. Set up: (1) Power Automate flow run history (retained automatically for 28 days; configure extended retention). (2) EHR audit log for all API-initiated changes to patient records. (3) Clearinghouse submission and response logs. (4) Firewall logs for all traffic to/from billing VLAN. (5) Server event logs (Windows Security, Application, System). (6) Centralized log aggregation in MSP's SIEM or RMM platform.

Windows Event Log forwarding, PowerShell script block logging, and Power Automate audit log export configuration
shell
# Configure Windows Event Log forwarding to SIEM:
wecutil qc /q
# Create subscription for Security events
wecutil cs AuditForward.xml

# Enable PowerShell script block logging:
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging' -Name 'EnableScriptBlockLogging' -Value 1

# Configure Power Automate audit log export:
# In Power Platform Admin Center > Environments > Settings > Auditing > Enable
# Flow run history is available via the Management Connector:
# https://learn.microsoft.com/en-us/connectors/flowmanagement/

# Set up daily audit report flow in Power Automate:
# Summarize: claims submitted, claims accepted, claims denied,
# encounters processed, API errors, unauthorized access attempts
Note

HIPAA audit log retention should be a minimum of 6 years. Power Automate's default 28-day flow run history is insufficient — export run details to a long-term storage location (Azure Blob Storage with immutable retention, or a SharePoint document library with retention policies). The Compliancy Group platform can help automate evidence collection for HIPAA audits. Ensure all logs include timestamps, user/service account identity, action performed, and data elements accessed.

Step 13: Parallel Testing and Go-Live

Run the automated billing pipeline in parallel with the existing manual process for 2–4 weeks before cutting over. During parallel testing: (1) The automation processes every finalized visit note and generates coded claims. (2) Claims are NOT submitted to payers — they are held in a 'review queue' in Claim.MD test mode. (3) Billing staff independently codes the same encounters manually. (4) Compare automated codes vs manual codes for accuracy. (5) Track: code match rate, coding specificity, modifier accuracy, and claim scrub pass rate. (6) Fix any rules engine errors or mapping gaps. (7) Target: 95%+ code match rate before go-live. (8) On go-live day, switch from test mode to production submission and disable manual coding for encounters the automation handles.

  • Claim.MD test mode flag in API calls: Set "TestMode": true in all claim submission payloads during parallel testing — Claims will be validated but NOT forwarded to payers
  • Create a comparison report in Power Automate: For each encounter, capture auto_codes (codes assigned by automation) and manual_codes (codes assigned by billing staff entered in EHR), evaluate match = auto_codes == manual_codes, and log to SharePoint: encounter_id, auto_codes, manual_codes, match, discrepancy_detail
  • Daily comparison summary email should include: Encounters processed, Perfect matches (count and %), Partial matches, Mismatches, and Top discrepancy reasons
Note

Do NOT skip parallel testing. The financial and compliance risk of submitting incorrectly coded claims is significant — practices can face payer audits, recoupments, and even fraud allegations for systematic coding errors. The billing staff SME must review every discrepancy and sign off on the rules engine configuration before go-live. Document the parallel testing results as part of the project deliverables. Expect 1–2 rounds of rules engine tuning before achieving the 95%+ target.

Custom AI Components

Visit Finalization Event Listener

Type: integration A polling-based integration component that monitors the EHR/PM system for newly finalized visit notes and triggers the downstream coding and claim submission pipeline. For EHRs with webhook support, this can be event-driven instead of polling. This component handles OAuth2 token management, encounter filtering, and deduplication to ensure each finalized visit is processed exactly once.

Implementation:

Power Automate Cloud Flow: Visit-Finalization-Listener

Flow Definition (JSON export format):

Power Automate Cloud Flow JSON export
json
# Visit-Finalization-Listener

{
  "name": "Visit-Finalization-Listener",
  "trigger": {
    "type": "Recurrence",
    "recurrence": {
      "frequency": "Minute",
      "interval": 5
    }
  },
  "actions": {
    "Get_OAuth_Token": {
      "type": "Http",
      "inputs": {
        "method": "POST",
        "uri": "https://api.athenahealth.com/oauth2/v1/token",
        "headers": {
          "Content-Type": "application/x-www-form-urlencoded"
        },
        "body": "grant_type=client_credentials&client_id=@{parameters('ehr_client_id')}&client_secret=@{parameters('ehr_client_secret')}&scope=athena/service/Athenanet.MDP.*"
      }
    },
    "Get_Finalized_Encounters": {
      "type": "Http",
      "runAfter": {"Get_OAuth_Token": ["Succeeded"]},
      "inputs": {
        "method": "GET",
        "uri": "https://api.athenahealth.com/v1/@{parameters('practice_id')}/chart/encounters",
        "headers": {
          "Authorization": "Bearer @{body('Get_OAuth_Token')['access_token']}"
        },
        "queries": {
          "status": "CLOSED",
          "lastmodifieddate": "@{formatDateTime(addMinutes(utcNow(), -6), 'MM/dd/yyyy')}",
          "limit": "50"
        }
      }
    },
    "Filter_New_Encounters": {
      "type": "Query",
      "runAfter": {"Get_Finalized_Encounters": ["Succeeded"]},
      "inputs": {
        "from": "@body('Get_Finalized_Encounters')['encounters']",
        "where": "@not(contains(variables('processed_encounter_ids'), item()['encounterid']))"
      }
    },
    "Process_Each_Encounter": {
      "type": "Foreach",
      "foreach": "@body('Filter_New_Encounters')",
      "actions": {
        "Add_To_Processed_List": {
          "type": "AppendToArrayVariable",
          "inputs": {
            "name": "processed_encounter_ids",
            "value": "@items('Process_Each_Encounter')['encounterid']"
          }
        },
        "Invoke_Coding_Pipeline": {
          "type": "Workflow",
          "inputs": {
            "host": {"workflow": {"id": "/flows/auto-code-and-submit"}},
            "body": {
              "encounter_id": "@items('Process_Each_Encounter')['encounterid']",
              "patient_id": "@items('Process_Each_Encounter')['patientid']",
              "provider_id": "@items('Process_Each_Encounter')['providerid']",
              "department_id": "@items('Process_Each_Encounter')['departmentid']"
            }
          }
        }
      }
    }
  },
  "variables": {
    "processed_encounter_ids": {
      "type": "Array",
      "value": []
    }
  }
}

Deduplication Strategy:

Maintain a SharePoint list or SQL table ProcessedEncounters with columns: encounter_id (unique), processed_timestamp, status, claim_id. Before processing any encounter, check if it exists in this table. This prevents double-billing if the polling interval overlaps or the flow reruns after a failure.

For Open Dental (Dental):

Replace the athenahealth API calls with Open Dental API:

  • Token: Static API key in header Authorization: ODFHIR {key}
  • Endpoint: GET /api/v1/appointments?aptStatus=2&DateStart={5min_ago}
  • Filter for appointments with status Complete (aptStatus=2) and ProcComplete procedures

For Webhook-Enabled EHRs:

Replace the Recurrence trigger with 'When an HTTP request is received' trigger and configure the EHR to send a POST to the flow's webhook URL when a visit is finalized. This reduces latency from 5 minutes to near-real-time.

Deterministic Medical Coding Rules Engine

Type: skill A rules-based coding engine that maps clinical documentation elements (diagnoses, procedures, visit type, time spent) to CPT, ICD-10-CM, and HCPCS billing codes using deterministic lookup tables. This component is used when the Fathom autonomous coding engine is not deployed (cost constraint or dental practice). It uses structured data from the EHR (not free-text NLP) to select codes based on configurable rules. It handles E/M level selection based on 2021 MDM guidelines, procedure-to-CPT mapping, diagnosis-to-ICD-10 mapping, and modifier assignment.

Implementation

File: coding_rules_engine.py
python
import json
import csv
from typing import List, Dict, Tuple, Optional
from dataclasses import dataclass, field
from datetime import datetime

@dataclass
class ServiceLine:
    cpt_code: str
    icd_codes: List[str]
    modifiers: List[str]
    units: int
    charge_amount: float
    diagnosis_pointers: List[int]

@dataclass
class CodingResult:
    encounter_id: str
    patient_id: str
    provider_npi: str
    date_of_service: str
    place_of_service: str
    service_lines: List[ServiceLine]
    warnings: List[str]
    auto_coded: bool
    requires_review: bool
    review_reasons: List[str]

class MedicalCodingRulesEngine:
    def __init__(self, config_path: str = 'coding_config/'):
        self.em_rules = self._load_json(f'{config_path}em_level_rules.json')
        self.procedure_map = self._load_json(f'{config_path}procedure_to_cpt.json')
        self.diagnosis_map = self._load_json(f'{config_path}diagnosis_to_icd10.json')
        self.fee_schedule = self._load_json(f'{config_path}fee_schedule.json')
        self.modifier_rules = self._load_json(f'{config_path}modifier_rules.json')
        self.cci_edits = self._load_cci_edits(f'{config_path}cci_edits.csv')
        self.payer_rules = self._load_json(f'{config_path}payer_specific_rules.json')
    
    def _load_json(self, path: str) -> dict:
        with open(path, 'r') as f:
            return json.load(f)
    
    def _load_cci_edits(self, path: str) -> dict:
        edits = {}
        with open(path, 'r') as f:
            reader = csv.DictReader(f)
            for row in reader:
                key = (row['column1_cpt'], row['column2_cpt'])
                edits[key] = {
                    'modifier_allowed': row['modifier_indicator'] == '1',
                    'effective_date': row['effective_date']
                }
        return edits
    
    def code_encounter(self, encounter_data: dict) -> CodingResult:
        warnings = []
        review_reasons = []
        service_lines = []
        
        # Step 1: Map diagnoses to ICD-10 codes
        icd_codes = self._map_diagnoses(encounter_data.get('diagnoses', []), warnings)
        
        # Step 2: Determine E/M level if applicable
        visit_type = encounter_data.get('visit_type', '')
        if visit_type in ['office_visit_new', 'office_visit_established', 'telehealth']:
            em_code = self._determine_em_level(encounter_data, warnings)
            if em_code:
                em_line = ServiceLine(
                    cpt_code=em_code,
                    icd_codes=icd_codes,
                    modifiers=self._get_em_modifiers(encounter_data),
                    units=1,
                    charge_amount=self.fee_schedule.get(em_code, {}).get('amount', 0),
                    diagnosis_pointers=list(range(1, min(len(icd_codes)+1, 5)))
                )
                service_lines.append(em_line)
        
        # Step 3: Map procedures to CPT codes
        for procedure in encounter_data.get('procedures', []):
            proc_line = self._map_procedure(procedure, icd_codes, warnings)
            if proc_line:
                service_lines.append(proc_line)
        
        # Step 4: Apply modifier logic
        service_lines = self._apply_modifier_rules(service_lines, encounter_data, warnings)
        
        # Step 5: Run CCI edit checks
        cci_violations = self._check_cci_edits(service_lines)
        if cci_violations:
            for v in cci_violations:
                if v['hard']:
                    review_reasons.append(f"CCI HARD EDIT: {v['message']}")
                else:
                    warnings.append(f"CCI soft edit: {v['message']}")
        
        # Step 6: Apply payer-specific rules
        payer_id = encounter_data.get('payer_id', '')
        if payer_id in self.payer_rules:
            service_lines, payer_warnings = self._apply_payer_rules(
                service_lines, payer_id, encounter_data
            )
            warnings.extend(payer_warnings)
        
        # Step 7: Validate completeness
        if not service_lines:
            review_reasons.append('No service lines generated - manual coding required')
        if not icd_codes:
            review_reasons.append('No ICD-10 codes mapped - diagnoses may need manual review')
        
        requires_review = len(review_reasons) > 0
        
        return CodingResult(
            encounter_id=encounter_data['encounter_id'],
            patient_id=encounter_data['patient_id'],
            provider_npi=encounter_data['provider_npi'],
            date_of_service=encounter_data['date_of_service'],
            place_of_service=encounter_data.get('place_of_service', '11'),
            service_lines=service_lines,
            warnings=warnings,
            auto_coded=not requires_review,
            requires_review=requires_review,
            review_reasons=review_reasons
        )
    
    def _map_diagnoses(self, diagnoses: list, warnings: list) -> List[str]:
        icd_codes = []
        for dx in diagnoses:
            dx_key = dx.get('snomed_code') or dx.get('description', '').lower().strip()
            if dx_key in self.diagnosis_map:
                icd = self.diagnosis_map[dx_key]
                if isinstance(icd, list):
                    icd_codes.extend(icd)
                else:
                    icd_codes.append(icd)
            else:
                warnings.append(f"Unmapped diagnosis: {dx.get('description', dx_key)}")
        return list(dict.fromkeys(icd_codes))  # dedupe preserving order
    
    def _determine_em_level(self, encounter: dict, warnings: list) -> Optional[str]:
        # 2021 E/M guidelines: level based on MDM or total time
        mdm_level = encounter.get('mdm_level')  # low, moderate, high
        total_time = encounter.get('total_time_minutes', 0)
        patient_type = encounter.get('visit_type', '')
        
        if patient_type == 'office_visit_new':
            time_map = {0: None, 15: '99202', 30: '99203', 45: '99204', 60: '99205'}
            mdm_map = {'straightforward': '99202', 'low': '99203', 'moderate': '99204', 'high': '99205'}
        elif patient_type in ['office_visit_established', 'telehealth']:
            time_map = {0: None, 10: '99212', 20: '99213', 30: '99214', 40: '99215'}
            mdm_map = {'straightforward': '99212', 'low': '99213', 'moderate': '99214', 'high': '99215'}
        else:
            warnings.append(f'Unrecognized visit type for E/M: {patient_type}')
            return None
        
        # Determine code by MDM
        mdm_code = mdm_map.get(mdm_level)
        
        # Determine code by time
        time_code = None
        for threshold in sorted(time_map.keys(), reverse=True):
            if total_time >= threshold and time_map[threshold]:
                time_code = time_map[threshold]
                break
        
        # Use higher of MDM or time-based
        if mdm_code and time_code:
            return max(mdm_code, time_code)  # Higher code number = higher level
        return mdm_code or time_code
    
    def _map_procedure(self, procedure: dict, icd_codes: list, warnings: list) -> Optional[ServiceLine]:
        proc_key = procedure.get('procedure_code') or procedure.get('description', '').lower().strip()
        if proc_key in self.procedure_map:
            mapping = self.procedure_map[proc_key]
            cpt = mapping['cpt_code']
            return ServiceLine(
                cpt_code=cpt,
                icd_codes=icd_codes,
                modifiers=mapping.get('default_modifiers', []),
                units=procedure.get('units', 1),
                charge_amount=self.fee_schedule.get(cpt, {}).get('amount', 0),
                diagnosis_pointers=mapping.get('diagnosis_pointers', [1])
            )
        else:
            warnings.append(f"Unmapped procedure: {procedure.get('description', proc_key)}")
            return None
    
    def _get_em_modifiers(self, encounter: dict) -> List[str]:
        modifiers = []
        if encounter.get('telehealth', False):
            modifiers.append('95')  # Synchronous telehealth
        if encounter.get('place_of_service') == '02':  # Telehealth POS
            modifiers.append('GT')  # Via interactive telecommunications
        return modifiers
    
    def _apply_modifier_rules(self, lines: list, encounter: dict, warnings: list) -> list:
        # If E/M billed with procedure on same date, check for modifier 25
        em_codes = {'99202','99203','99204','99205','99212','99213','99214','99215'}
        has_em = any(sl.cpt_code in em_codes for sl in lines)
        has_procedure = any(sl.cpt_code not in em_codes for sl in lines)
        
        if has_em and has_procedure:
            for sl in lines:
                if sl.cpt_code in em_codes and '25' not in sl.modifiers:
                    sl.modifiers.append('25')
                    warnings.append(f'Auto-added modifier 25 to {sl.cpt_code} (significant separate E/M with procedure)')
        return lines
    
    def _check_cci_edits(self, lines: list) -> list:
        violations = []
        cpt_codes = [sl.cpt_code for sl in lines]
        for i, code1 in enumerate(cpt_codes):
            for code2 in cpt_codes[i+1:]:
                key = (code1, code2)
                rev_key = (code2, code1)
                edit = self.cci_edits.get(key) or self.cci_edits.get(rev_key)
                if edit:
                    violations.append({
                        'message': f'{code1} and {code2} are a CCI edit pair',
                        'hard': not edit['modifier_allowed']
                    })
        return violations
    
    def _apply_payer_rules(self, lines: list, payer_id: str, encounter: dict) -> Tuple[list, list]:
        warnings = []
        rules = self.payer_rules[payer_id]
        
        # Apply payer-specific code substitutions
        for sl in lines:
            if sl.cpt_code in rules.get('code_substitutions', {}):
                old_code = sl.cpt_code
                sl.cpt_code = rules['code_substitutions'][old_code]
                warnings.append(f'Payer {payer_id}: substituted {old_code} -> {sl.cpt_code}')
        
        # Apply payer-specific modifier requirements
        for sl in lines:
            if sl.cpt_code in rules.get('required_modifiers', {}):
                req_mod = rules['required_modifiers'][sl.cpt_code]
                if req_mod not in sl.modifiers:
                    sl.modifiers.append(req_mod)
                    warnings.append(f'Payer {payer_id}: added required modifier {req_mod} to {sl.cpt_code}')
        
        return lines, warnings


class DentalCodingRulesEngine:
    """Simplified rules engine for dental CDT coding."""
    
    def __init__(self, config_path: str = 'dental_config/'):
        self.procedure_map = self._load_json(f'{config_path}procedure_to_cdt.json')
        self.fee_schedule = self._load_json(f'{config_path}fee_schedule.json')
        self.surface_codes = self._load_json(f'{config_path}surface_codes.json')
    
    def _load_json(self, path: str) -> dict:
        with open(path, 'r') as f:
            return json.load(f)
    
    def code_dental_encounter(self, encounter_data: dict) -> CodingResult:
        warnings = []
        review_reasons = []
        service_lines = []
        
        for procedure in encounter_data.get('procedures', []):
            tooth = procedure.get('tooth_number', '')
            surface = procedure.get('surface', '')
            proc_type = procedure.get('procedure_type', '').lower()
            
            if proc_type in self.procedure_map:
                cdt = self.procedure_map[proc_type]
                # Surface-specific CDT code selection (e.g., D2391 vs D2392)
                if isinstance(cdt, dict) and 'by_surface_count' in cdt:
                    surface_count = len(surface)
                    cdt_code = cdt['by_surface_count'].get(str(surface_count), cdt['default'])
                elif isinstance(cdt, str):
                    cdt_code = cdt
                else:
                    cdt_code = cdt.get('default', '')
                
                if cdt_code:
                    service_lines.append(ServiceLine(
                        cpt_code=cdt_code,  # CDT code stored in cpt_code field
                        icd_codes=[],  # Dental claims typically don't require ICD codes
                        modifiers=[],
                        units=1,
                        charge_amount=self.fee_schedule.get(cdt_code, {}).get('amount', 0),
                        diagnosis_pointers=[]
                    ))
                else:
                    warnings.append(f'Could not determine CDT code for {proc_type} surface={surface}')
            else:
                review_reasons.append(f'Unmapped dental procedure: {proc_type}')
        
        requires_review = len(review_reasons) > 0
        return CodingResult(
            encounter_id=encounter_data['encounter_id'],
            patient_id=encounter_data['patient_id'],
            provider_npi=encounter_data['provider_npi'],
            date_of_service=encounter_data['date_of_service'],
            place_of_service='11',
            service_lines=service_lines,
            warnings=warnings,
            auto_coded=not requires_review,
            requires_review=requires_review,
            review_reasons=review_reasons
        )

Configuration Files Required

em_level_rules.json — E/M level determination rules (sample)
json
{
  "new_patient": {
    "99202": {"mdm": "straightforward", "min_time": 15},
    "99203": {"mdm": "low", "min_time": 30},
    "99204": {"mdm": "moderate", "min_time": 45},
    "99205": {"mdm": "high", "min_time": 60}
  },
  "established_patient": {
    "99212": {"mdm": "straightforward", "min_time": 10},
    "99213": {"mdm": "low", "min_time": 20},
    "99214": {"mdm": "moderate", "min_time": 30},
    "99215": {"mdm": "high", "min_time": 40}
  }
}
procedure_to_cpt.json
json
# Specialty-specific procedure-to-CPT mapping (sample for Family Medicine)

{
  "flu_vaccine_injection": {"cpt_code": "90686", "default_modifiers": [], "diagnosis_pointers": [1]},
  "flu_vaccine_admin": {"cpt_code": "90471", "default_modifiers": [], "diagnosis_pointers": [1]},
  "ekg_12_lead": {"cpt_code": "93000", "default_modifiers": [], "diagnosis_pointers": [1]},
  "strep_rapid_test": {"cpt_code": "87880", "default_modifiers": [], "diagnosis_pointers": [1]},
  "ear_lavage": {"cpt_code": "69209", "default_modifiers": [], "diagnosis_pointers": [1]},
  "laceration_repair_simple_2.5cm": {"cpt_code": "12001", "default_modifiers": [], "diagnosis_pointers": [1]},
  "joint_injection_major": {"cpt_code": "20610", "default_modifiers": [], "diagnosis_pointers": [1]}
}
procedure_to_cdt.json — Dental CDT mapping (sample)
json
{
  "periodic_oral_eval": "D0120",
  "comprehensive_oral_eval": "D0150",
  "limited_oral_eval": "D0140",
  "bitewing_2_films": "D0272",
  "bitewing_4_films": "D0274",
  "panoramic_xray": "D0330",
  "prophylaxis_adult": "D1110",
  "prophylaxis_child": "D1120",
  "fluoride_treatment": "D1206",
  "sealant_per_tooth": "D1351",
  "amalgam_restoration": {
    "by_surface_count": {"1": "D2140", "2": "D2150", "3": "D2160", "4": "D2161"},
    "default": "D2140"
  },
  "composite_restoration_anterior": {
    "by_surface_count": {"1": "D2330", "2": "D2331", "3": "D2332", "4": "D2335"},
    "default": "D2330"
  },
  "composite_restoration_posterior": {
    "by_surface_count": {"1": "D2391", "2": "D2392", "3": "D2393", "4": "D2394"},
    "default": "D2391"
  },
  "extraction_simple": "D7140",
  "extraction_surgical": "D7210",
  "root_canal_anterior": "D3310",
  "root_canal_premolar": "D3320",
  "root_canal_molar": "D3330",
  "crown_pfm": "D2750",
  "crown_ceramic": "D2740"
}

Deployment Notes

  • Host this engine as an Azure Function (Python runtime) or AWS Lambda behind API Gateway
  • Alternatively, embed the logic directly in Power Automate using inline code actions (limited to simple mappings)
  • Configuration files should be stored in a version-controlled repository and deployed via CI/CD
  • The billing staff SME must review and approve all mapping files before production use
  • Update CPT/ICD-10 mappings annually (new codes released every October 1 for ICD-10 and January 1 for CPT)

Claim Submission Orchestrator

Type: workflow The core Power Automate workflow that takes a CodingResult from the rules engine or Fathom API, formats it into a clearinghouse-compatible claim payload, submits it to Claim.MD via API, handles the response, and logs everything for HIPAA audit compliance. This workflow also handles the ERA retrieval and denial routing.

Implementation

Flow Architecture

This flow is called as a child flow by the Visit-Finalization-Listener for each encounter.

Input Parameters

  • encounter_id (string)
  • patient_id (string)
  • provider_id (string)
  • department_id (string)

Step 1: Retrieve Full Encounter Data

HTTP GET to EHR API — retrieve full encounter data
http
GET https://api.athenahealth.com/v1/{practiceid}/chart/encounters/{encounter_id}
Authorization: Bearer {oauth_token}

Step 2: Retrieve Patient Demographics

HTTP GET to EHR API — retrieve patient demographics
http
GET https://api.athenahealth.com/v1/{practiceid}/patients/{patient_id}
Authorization: Bearer {oauth_token}

Step 3: Retrieve Insurance Information

HTTP GET to EHR API — retrieve insurance information
http
GET https://api.athenahealth.com/v1/{practiceid}/patients/{patient_id}/insurances
Authorization: Bearer {oauth_token}

Step 4: Invoke Coding Engine

HTTP POST to Azure Function (rules engine) or Fathom API — invoke coding engine
json
POST https://{your-function-app}.azurewebsites.net/api/code-encounter
Content-Type: application/json
x-functions-key: {function_key}

{
  "encounter_id": "{encounter_id}",
  "patient_id": "{patient_id}",
  "provider_npi": "{provider_npi}",
  "date_of_service": "{encounter_date}",
  "visit_type": "{visit_type}",
  "diagnoses": [{encounter_diagnoses}],
  "procedures": [{encounter_procedures}],
  "mdm_level": "{mdm_level}",
  "total_time_minutes": {total_time},
  "payer_id": "{primary_payer_id}",
  "place_of_service": "{pos_code}",
  "telehealth": {is_telehealth}
}

Step 5: Check if Manual Review Required

  • Condition: If coding_result.requires_review == true
  • YES: Send email to billing manager with review_reasons; create task in PM system; EXIT flow
  • NO: Continue to claim submission

Step 6: Write Codes Back to EHR

HTTP PUT to EHR API — write codes back to charge record
json
PUT https://api.athenahealth.com/v1/{practiceid}/chart/encounters/{encounter_id}/charges
Authorization: Bearer {oauth_token}
Content-Type: application/json

{
  "charges": [
    {
      "cptcode": "{cpt}",
      "icd10": "{icd_codes}",
      "modifier1": "{mod1}",
      "units": {units},
      "amount": {charge}
    }
  ]
}

Step 7: Build Claim.MD JSON Payload

Compose action — Claim.MD JSON payload
json
{
  "AccountKey": "{claim_md_key}",
  "TestMode": false,
  "Claims": [{
    "PatientFirstName": "{patient_first}",
    "PatientLastName": "{patient_last}",
    "PatientDOB": "{patient_dob}",
    "PatientGender": "{patient_gender}",
    "PatientAddress1": "{patient_address}",
    "PatientCity": "{patient_city}",
    "PatientState": "{patient_state}",
    "PatientZip": "{patient_zip}",
    "SubscriberID": "{subscriber_id}",
    "SubscriberFirstName": "{subscriber_first}",
    "SubscriberLastName": "{subscriber_last}",
    "SubscriberDOB": "{subscriber_dob}",
    "PayerID": "{payer_id}",
    "PayerName": "{payer_name}",
    "BillingProviderNPI": "{billing_npi}",
    "BillingProviderTaxID": "{tax_id}",
    "BillingProviderName": "{practice_name}",
    "RenderingProviderNPI": "{rendering_npi}",
    "RenderingProviderFirstName": "{provider_first}",
    "RenderingProviderLastName": "{provider_last}",
    "PlaceOfService": "{pos_code}",
    "FacilityNPI": "{facility_npi}",
    "DateOfService": "{date_of_service}",
    "DiagnosisCodes": ["{icd_code_1}", "{icd_code_2}"],
    "ServiceLines": [
      {
        "CPTCode": "{cpt_code}",
        "Modifier1": "{modifier_1}",
        "Modifier2": "{modifier_2}",
        "DiagnosisPointer": "{diag_pointers}",
        "Units": {units},
        "ChargeAmount": {amount}
      }
    ]
  }]
}

Step 8: Submit Claim to Claim.MD

HTTP POST — submit claim to Claim.MD clearinghouse
http
POST https://svc.claim.md/services/upload/
Content-Type: application/json

{Step 7 output}

Step 9: Parse Submission Response

  • Action: Parse JSON
  • Check for: submission_id, status, errors

Step 10: Log to Audit Table

  • Action: SharePoint — Create Item (or SQL Insert)
  • List: BillingAutomationAudit
  • EncounterID: {encounter_id}
  • PatientID: {patient_id} (last 4 only for log readability)
  • DateOfService: {dos}
  • CPTCodes: {comma-separated CPT codes}
  • ICDCodes: {comma-separated ICD codes}
  • ClaimMDSubmissionID: {submission_id}
  • SubmissionStatus: {status}
  • Warnings: {warnings_json}
  • ProcessedTimestamp: {utcNow()}
  • FlowRunID: {workflow().run.name}

Step 11: Error Handling

  • On HTTP failure: Retry 3 times with 30-second intervals
  • On persistent failure: Send alert email to MSP NOC + billing manager
  • On coding engine failure: Route encounter to manual coding queue
  • All errors logged to audit table with error details

Deployment

1
Build flow in Power Automate web designer
2
Create all connections (HTTP, SharePoint, Outlook)
3
Set up environment variables for all API keys and endpoints
4
Store secrets in Azure Key Vault connected via Power Automate Key Vault connector
5
Test with 5 sample encounters in Claim.MD test mode
6
Run parallel testing for 2–4 weeks
7
Switch TestMode to false for production go-live

Denial Feedback Loop and Analytics Dashboard

Type: workflow An automated workflow that monitors claim adjudication responses (835 ERAs and claim status updates), identifies denial patterns, and provides actionable analytics to both the practice billing team and the MSP. This closes the feedback loop by using denial data to improve the rules engine over time. Includes a Power BI dashboard template for visualizing billing automation KPIs.

Part 1: ERA Monitoring Flow

Flow: Daily-ERA-Monitor Trigger: Recurrence (daily at 7:00 AM)

Step 1: GET new ERAs from Claim.MD
http
POST https://svc.claim.md/services/era/
Body: {"AccountKey": "{key}", "StartDate": "{yesterday}", "EndDate": "{today}"}
  • Step 2 — For each ERA record: Parse payment/adjustment/denial details
  • Match to original claim in audit table by claim number
  • Update audit table record with adjudication result: PaidAmount, AdjustmentAmount, PatientResponsibility
  • Update audit table record with adjudication result: DenialCode (CARC/RARC codes), DenialReason
  • Update audit table record with adjudication result: AdjudicationDate
  • Step 3 — Denial Categories (based on CARC codes):
  • CARC 1-3: Deductible/Coinsurance/Copay (patient responsibility, not true denial)
  • CARC 4: Procedure modifier inconsistency → Flag for rules engine update
  • CARC 16: Missing information → Flag for data completeness check
  • CARC 18: Duplicate claim → Flag deduplication logic issue
  • CARC 29: Filing limit expired → Flag coding lag issue
  • CARC 50: Non-covered service → Flag for payer rules update
  • CARC 96-97: Non-covered/bundled → Flag for CCI edit update
  • CARC 197: Precertification required → Flag for prior auth workflow
  • Step 4 — Create denial work items:
  • For actionable denials: Create task in practice billing queue with suggested corrective action
  • For rules engine issues: Create ticket for MSP to update coding rules
  • For patient responsibility items: Route to patient billing workflow

Step 5: Send daily summary email Subject: Daily Billing Automation Report - {date}

  • Encounters auto-coded yesterday: {count}
  • Claims submitted: {count}
  • Claims accepted (no errors): {count} ({pct}%)
  • Claims with warnings: {count}
  • Claims routed to manual review: {count}
  • ERA payments received: ${total}
  • Denials received: {count}
  • Top denial reason: {reason} ({count})
  • Suggested action: {action}
  • First-pass acceptance rate (30-day rolling): {pct}%
  • Average coding lag: {hours} hours

Part 2: Power BI Dashboard

Data Source: SharePoint list 'BillingAutomationAudit' or SQL database

  • Page 1 — Executive Summary: KPI Cards: First-Pass Accept Rate, Auto-Code Rate, Average Coding Lag, Monthly Revenue Submitted
  • Page 1 — Executive Summary: Trend line: First-pass accept rate over time (target: >95%)
  • Page 1 — Executive Summary: Trend line: Claims submitted per day
  • Page 1 — Executive Summary: Bar chart: Revenue by payer
  • Page 2 — Denial Analysis: Pie chart: Denial reasons by CARC category
  • Page 2 — Denial Analysis: Table: Top 10 denied CPT codes with denial reasons
  • Page 2 — Denial Analysis: Bar chart: Denial rate by payer
  • Page 2 — Denial Analysis: Heatmap: Denial rate by provider x payer
  • Page 3 — Coding Engine Performance: KPI Cards: Auto-coded %, Manual review %, Unmapped procedures count
  • Page 3 — Coding Engine Performance: Table: Most frequently triggered warnings
  • Page 3 — Coding Engine Performance: Table: Encounters requiring manual review with reasons
  • Page 3 — Coding Engine Performance: Line chart: Auto-code rate trend over time
  • Page 4 — Financial Impact: KPI Cards: Revenue recovered (vs baseline), Coding lag reduction, Staff hours saved
  • Page 4 — Financial Impact: Bar chart: Monthly collections trend (pre vs post automation)
  • Page 4 — Financial Impact: Calculation: ROI = (Revenue increase + Staff cost savings - Automation cost) / Automation cost

Part 3: Rules Engine Feedback

1
MSP reviews denial pattern (e.g., CARC 4 on specific CPT+modifier combo)
2
MSP updates the relevant configuration file (modifier_rules.json, payer_specific_rules.json)
3
Change is tested with historical encounters to verify fix
4
Updated config deployed to Azure Function
5
Change documented in rules engine changelog
Note

This creates a continuous improvement cycle where the automation gets smarter over time through deterministic rule updates, not AI retraining.

HIPAA Audit Trail Service

Type: integration A centralized audit logging service that captures every action taken by the billing automation pipeline — every API call, every code assigned, every claim submitted, every payment posted. This satisfies the HIPAA Security Rule requirement for audit controls and provides evidence for compliance audits. Implements immutable logging with tamper-evident design.

Implementation:

HIPAA Audit Trail — SharePoint + Azure Implementation

Option A: SharePoint List (Simple, included with M365)

SharePoint List: BillingAutomationAudit

  • AuditID (Auto-increment, unique)
  • Timestamp (DateTime, UTC)
  • EventType (Choice: ENCOUNTER_DETECTED, CODING_STARTED, CODING_COMPLETED, CLAIM_FORMATTED, CLAIM_SUBMITTED, CLAIM_ACCEPTED, CLAIM_DENIED, ERA_RECEIVED, PAYMENT_POSTED, MANUAL_REVIEW_REQUIRED, ERROR)
  • EncounterID (Single line text)
  • PatientID_Last4 (Single line text, last 4 of MRN only)
  • ProviderNPI (Single line text)
  • ActionDetail (Multiple lines text, JSON blob with specifics)
  • ServiceAccount (Single line text, which automation account performed action)
  • FlowRunID (Single line text, Power Automate run ID for traceability)
  • SourceIP (Single line text)
  • Status (Choice: SUCCESS, FAILURE, PENDING_REVIEW)
  • ErrorDetail (Multiple lines text, populated only on failure)

Retention Policy:

  • Configure SharePoint retention policy: 7 years (exceeds HIPAA 6-year minimum)
  • Enable versioning (prevents silent edits)
  • Lock list permissions: only the automation service account can write; billing manager has read-only; no delete permissions for anyone

Power Automate Logging Action (insert into every flow step):

Power Automate SharePoint logging action payload
json
{
  "action": "SharePoint - Create Item",
  "site": "https://{tenant}.sharepoint.com/sites/BillingAutomation",
  "list": "BillingAutomationAudit",
  "fields": {
    "Timestamp": "@{utcNow()}",
    "EventType": "CLAIM_SUBMITTED",
    "EncounterID": "@{variables('encounter_id')}",
    "PatientID_Last4": "@{substring(variables('patient_id'), sub(length(variables('patient_id')), 4))}",
    "ProviderNPI": "@{variables('provider_npi')}",
    "ActionDetail": "@{json(concat('{\"cpt_codes\":', variables('cpt_codes_json'), ',\"claim_md_id\":\"', variables('submission_id'), '\"}'  ))}",
    "ServiceAccount": "billing-automation@practice.com",
    "FlowRunID": "@{workflow()['run']['name']}",
    "Status": "SUCCESS"
  }
}

Option B: Azure SQL + Immutable Storage (Enterprise-grade)

SQL Table: dbo.BillingAuditLog

Azure SQL table definition with tamper-evident hash and immutability trigger
sql
CREATE TABLE dbo.BillingAuditLog (
    AuditID BIGINT IDENTITY(1,1) PRIMARY KEY,
    Timestamp DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),
    EventType NVARCHAR(50) NOT NULL,
    EncounterID NVARCHAR(50),
    PatientID_Last4 NVARCHAR(4),
    ProviderNPI NVARCHAR(10),
    ActionDetail NVARCHAR(MAX),  -- JSON
    ServiceAccount NVARCHAR(100),
    FlowRunID NVARCHAR(100),
    SourceIP NVARCHAR(45),
    Status NVARCHAR(20),
    ErrorDetail NVARCHAR(MAX),
    RowHash AS HASHBYTES('SHA2_256', 
        CONCAT(CAST(AuditID AS NVARCHAR), Timestamp, EventType, EncounterID, ActionDetail)
    ) PERSISTED  -- Tamper detection
);

-- Prevent deletes and updates (immutable log)
CREATE TRIGGER tr_PreventAuditModification
ON dbo.BillingAuditLog
INSTEAD OF UPDATE, DELETE
AS
BEGIN
    RAISERROR('Audit log records cannot be modified or deleted.', 16, 1);
    ROLLBACK TRANSACTION;
END;

-- Index for common queries
CREATE INDEX IX_AuditLog_Encounter ON dbo.BillingAuditLog(EncounterID);
CREATE INDEX IX_AuditLog_Date ON dbo.BillingAuditLog(Timestamp);
CREATE INDEX IX_AuditLog_EventType ON dbo.BillingAuditLog(EventType);

Azure SQL Configuration:

  • SKU: Azure SQL Database S1 Standard (~$30/month)
  • Enable Transparent Data Encryption (TDE) — on by default
  • Enable Azure SQL Auditing to Azure Blob Storage
  • Configure long-term backup retention: 7 years
  • Enable Azure AD authentication; disable SQL auth
  • Restrict network access to Power Automate IPs and MSP management IPs only

Compliance Documentation

Generate quarterly HIPAA audit report from the audit log:

Quarterly HIPAA audit report query
sql
SELECT 
    EventType,
    COUNT(*) as EventCount,
    COUNT(CASE WHEN Status = 'FAILURE' THEN 1 END) as FailureCount,
    MIN(Timestamp) as FirstEvent,
    MAX(Timestamp) as LastEvent
FROM dbo.BillingAuditLog
WHERE Timestamp >= DATEADD(QUARTER, -1, GETUTCDATE())
GROUP BY EventType
ORDER BY EventCount DESC;

Testing & Validation

  • TEST 1 - API Connectivity: Verify OAuth2 token acquisition from EHR API returns a valid access token. Run: curl command from Step 5 and confirm 200 response with token in body. Expected: access_token field present, expires_in > 0.
  • TEST 2 - Encounter Detection: Finalize a test visit note in the EHR and wait for the next polling cycle (5 minutes). Verify the Visit-Finalization-Listener flow triggers and picks up the new encounter. Check flow run history in Power Automate for successful trigger and correct encounter_id extraction.
  • TEST 3 - Coding Engine Accuracy (E/M Levels): Submit 10 pre-coded sample encounters with known E/M levels (2 each of 99212-99215 and 99203) to the coding rules engine. Verify the engine returns the correct E/M code for each. All 10 must match expected codes.
  • TEST 4 - Coding Engine Accuracy (Procedure Mapping): Submit 20 sample encounters with common procedures from the practice's top-billed procedure list. Verify each procedure maps to the correct CPT or CDT code with appropriate modifiers. Target: 100% match rate for mapped procedures.
  • TEST 5 - CCI Edit Detection: Submit a test encounter with two CPT codes that are a known CCI edit pair (e.g., 99213 + 99214 same date). Verify the rules engine flags the CCI violation and routes the encounter to manual review.
  • TEST 6 - Claim Formatting Validation: Generate a claim payload for a test encounter and submit to Claim.MD in test mode (TestMode: true). Verify Claim.MD returns a successful validation response with no formatting errors. Check all required fields are populated correctly.
  • TEST 7 - End-to-End Happy Path: Finalize a real visit note in the EHR (using a test patient). Track the encounter through the entire pipeline: detection → coding → EHR charge update → claim submission → Claim.MD acceptance. Verify the audit log contains entries for every stage.
  • TEST 8 - Error Handling: Simulate an API failure by temporarily using an invalid API key. Verify the flow retries 3 times, then sends an alert email to the MSP NOC and billing manager, and logs the error in the audit table.
  • TEST 9 - Duplicate Prevention: Finalize the same visit note twice (or re-run the listener manually). Verify the deduplication logic prevents the encounter from being processed and billed a second time. Check the ProcessedEncounters table for the dedup record.
  • TEST 10 - Manual Review Routing: Submit an encounter with an unmapped diagnosis (one not in the diagnosis_to_icd10.json file). Verify the encounter is flagged as requires_review=true and an email is sent to the billing manager with the specific review reason.
  • TEST 11 - Parallel Coding Validation: Run the automation in parallel with manual billing staff for 2 full weeks (minimum 50 encounters). Calculate: (a) Code match rate (target: ≥95%), (b) Cases where automation caught codes the manual coder missed, (c) Cases where automation assigned incorrect codes. Document all discrepancies and resolve before go-live.
  • TEST 12 - ERA Retrieval and Parsing: After test claims have been adjudicated (allow 5-10 business days), verify the ERA monitoring flow retrieves 835 responses from Claim.MD, correctly parses payment and denial data, and updates the audit log with adjudication results.
  • TEST 13 - HIPAA Audit Log Completeness: Review the audit log after running 20+ test encounters through the pipeline. Verify every encounter has log entries for each pipeline stage (ENCOUNTER_DETECTED through CLAIM_SUBMITTED). Verify no PHI beyond last-4-of-MRN is stored in the log. Verify log records cannot be deleted or modified.
  • TEST 14 - Network Security Validation: Run a port scan (nmap) against the billing VLAN from the general office VLAN. Verify the firewall blocks all cross-VLAN traffic except explicitly allowed flows. Verify all API traffic is TLS 1.2+ encrypted (check with Wireshark or SSL Labs test).
  • TEST 15 - Failover Test: Disconnect the primary internet connection. Verify the automation pauses gracefully without data loss. Reconnect internet. Verify the automation resumes and processes any encounters that were finalized during the outage.

Client Handoff

Client Handoff Checklist

Training Sessions (Schedule 3 sessions, 1 hour each):

Session 1: Billing Staff Training (Office Manager + Billers)

  • How the automation pipeline works end-to-end (simplified diagram)
  • What encounters are auto-coded vs. routed to manual review
  • How to check the daily summary email and act on flagged items
  • How to review and approve encounters in the manual review queue
  • How to look up a specific claim's status in Claim.MD portal
  • How to escalate issues to the MSP (support email/phone/ticket portal)

Session 2: Provider Training (Doctors/Dentists)

  • How visit note finalization triggers billing (importance of timely note completion)
  • What data elements the automation uses from their notes (diagnoses, procedures, MDM level, time)
  • How structured documentation improves auto-coding accuracy
  • What NOT to change (don't modify charges after automation has processed them without notifying billing)

Session 3: Office Manager / Practice Administrator

  • How to read the Power BI dashboard (KPIs, denial trends, financial impact)
  • Monthly review cadence with MSP (first-pass rate, denial patterns, rules updates)
  • How annual CPT/ICD-10 code updates are handled (MSP responsibility)
  • HIPAA compliance responsibilities (staff training, incident reporting)
  • How to request changes (new procedure mappings, new payer enrollment, new provider onboarding)

Documentation Package (Leave Behind):

1
Solution Architecture Diagram — visual overview of all components and data flows
2
Runbook: Daily Operations — what billing staff should check each morning, how to handle common scenarios
3
Runbook: Troubleshooting — common issues and resolutions (API timeout, claim rejection, ERA mismatch)
4
Escalation Matrix — who to contact for what (MSP helpdesk for technical issues, clearinghouse for payer enrollment, EHR vendor for system issues)
5
HIPAA Documentation — updated risk assessment, BAA inventory, audit log access procedures
6
Credential Inventory — secure document listing all service accounts, API keys (stored in password manager, not plaintext), and their purposes
7
SLA Summary — MSP response times, maintenance windows, and after-hours support procedures

Success Criteria Review (Verify at 30 and 90 days post go-live):

Maintenance

Ongoing MSP Maintenance Responsibilities

Daily (Automated):

  • Monitor Power Automate flow run success/failure via RMM alerts
  • Review automated daily summary email for anomalies (sudden drop in encounters processed, spike in denials, API errors)
  • Verify Datto SIRIS backup completion (if on-premise server deployed)
  • Check SonicWall security dashboard for blocked threats or anomalous traffic patterns

Weekly (15-30 minutes):

  • Review Power Automate flow analytics: success rate, average run time, failure trends
  • Check Claim.MD dashboard for submission errors or payer connectivity issues
  • Review denial queue: identify any new systematic denial patterns that may require rules engine updates
  • Verify endpoint protection (SentinelOne) is current on all billing workstations
  • Check server health (CPU, memory, disk) via RMM dashboard

Monthly (1-2 hours):

  • Review Power BI dashboard with practice billing manager (scheduled 30-minute call)
  • Analyze denial trends and update payer-specific rules if patterns emerge
  • Apply Windows and application patches to server (during maintenance window, typically Saturday night)
  • Update SonicWall firmware and security signatures if new versions available
  • Review and rotate API keys/credentials if approaching expiration
  • Verify HIPAA audit log integrity and storage capacity
  • Generate monthly billing automation performance report for practice

Quarterly (2-4 hours):

  • Download and apply CMS CCI edit file updates (released quarterly)
  • Review and update payer enrollment status with clearinghouse
  • Conduct abbreviated HIPAA security review (access logs, terminated employee access removal, BAA status)
  • Power Automate platform updates and connector version checks
  • Review Datto SIRIS backup restore test (perform test restore quarterly)
  • Practice billing team check-in: gather feedback on automation accuracy, identify new procedure types that need mapping

Annually (Project-level effort, 1-2 weeks):

  • CPT Code Update (January 1): AMA releases new/revised/deleted CPT codes annually. Update procedure_to_cpt.json mapping file with all changes relevant to the practice's specialty. Test with sample encounters before production deployment.
  • ICD-10-CM Code Update (October 1): CMS releases annual ICD-10 code set updates. Update diagnosis_to_icd10.json mapping file. Validate all existing mappings against the new code set.
  • CDT Code Update (January 1 for dental): ADA releases annual CDT updates. Update procedure_to_cdt.json.
  • HIPAA Security Risk Assessment: Conduct full annual risk assessment including the billing automation system. Update remediation plan.
  • BAA Review: Verify all vendor BAAs are current and renewed. Add BAAs for any new vendors.
  • Fee Schedule Update: Update fee_schedule.json with current practice fee schedule.
  • Annual HIPAA training: Ensure all staff with system access complete training; document attendance.

SLA Considerations:

  • Severity 1 (Claims not submitting): 1-hour MSP response, 4-hour resolution target. This directly impacts practice revenue — every day of claim submission delay costs money.
  • Severity 2 (Coding errors/incorrect codes): 4-hour MSP response, 1 business day resolution. Incorrect codes can lead to payer audits.
  • Severity 3 (Dashboard/reporting issues): Next business day response, 3 business day resolution.
  • Severity 4 (Feature requests/enhancements): Scheduled for next monthly maintenance window.

Escalation Path:

1
Practice billing staff identifies issue → emails MSP helpdesk or submits ticket
2
MSP L1 technician triages (check flow status, API connectivity, recent changes)
3
If not resolved → MSP L2 engineer (integration specialist, reviews flow logic and API responses)
4
If vendor issue → escalate to EHR vendor support, Claim.MD support, or Fathom support as appropriate
5
If compliance concern → MSP compliance officer + practice privacy officer notified immediately

Model/Rules Retraining Triggers (Not AI retraining — deterministic rules updates):

  • First-pass acceptance rate drops below 90% for 2 consecutive weeks
  • New denial code appears more than 5 times in a month
  • Practice adds a new provider specialty requiring new code mappings
  • Practice contracts with a new insurance payer requiring new payer rules
  • CMS or AMA publishes mid-year coding guidance changes
  • Payer publishes updated medical policy or LCD affecting covered services

Alternatives

...

Fully Integrated EHR Billing Module (No External Coding Engine)

Instead of deploying a separate auto-coding engine (Fathom) and workflow automation (Power Automate), use the EHR/PM system's built-in billing and claims features. Most modern EHRs (athenahealth, AdvancedMD, Tebra) include integrated charge capture, claim scrubbing, and electronic claims submission. athenahealth's percentage-of-collections model specifically includes coding suggestions and a built-in clearinghouse. AdvancedMD includes ClaimInspector for automated scrubbing with near 100% first-pass rates.

RPA-Based Screen Automation (UiPath)

Instead of API-based integration, use Robotic Process Automation (UiPath) to automate the billing workflow through the EHR's user interface. The robot mimics human actions — clicking through screens, copying data, and entering codes — without requiring API access. This approach works with EHRs that lack APIs (legacy systems like older Dentrix G7, Eaglesoft, or small specialty EHRs). UiPath is HIPAA-compliant (signs BAA) and recognized as the leading RPA platform in 2025.

Outsourced RCM with AI-Augmented Coding Service

Instead of building the automation in-house, partner with a Revenue Cycle Management (RCM) service that already has AI-powered coding built in. Companies like athenahealth (via their RCM service), MediMobile/Genesis, or Collectly offer managed billing services that include AI-assisted coding, claim submission, denial management, and payment posting. The MSP's role shifts from building automation to managing the IT infrastructure that supports the RCM vendor's access to the practice's systems.

Keragon Healthcare-Native Integration Platform (Replace Power Automate)

Replace Microsoft Power Automate with Keragon, a healthcare-specific integration platform purpose-built for HIPAA-compliant workflows. Keragon provides pre-built connectors for common EHRs, clearinghouses, and healthcare APIs, with built-in HIPAA compliance, SOC 2 Type II certification, and BAA included in all paid plans. The no-code interface is designed specifically for healthcare data flows rather than general-purpose automation.

Open Dental + Direct Clearinghouse Integration (Dental-Specific)

For dental practices specifically, leverage Open Dental's comprehensive open-source API to build a direct integration with the clearinghouse, bypassing the need for external coding engines or middleware. Open Dental's API supports full claims management including setting ClaimStatus and creating Etrans entries. Combined with Open Dental's built-in auto-codes feature for CDT code assignment, this creates a streamlined dental-specific automation pipeline with minimal external dependencies.

Want early access to the full toolkit?