By Sagar Shankaran, Founder of CallSphere
A comprehensive guide to building HIPAA-compliant AI agents covering encryption, access controls, audit logging, Business Associate Agreements, data retention policies, and breach notification procedures.
Key takeaways
Every healthcare AI agent that touches protected health information (PHI) must comply with HIPAA. This is not optional and it is not a nice-to-have security layer. Violations carry penalties ranging from 100 to 50,000 dollars per incident, with annual maximums up to 1.5 million dollars per violation category. Criminal penalties can include imprisonment.
HIPAA's Security Rule defines three categories of safeguards: administrative, physical, and technical. AI agents primarily deal with technical safeguards, but the developers building them must understand all three categories to make sound architectural decisions.
HIPAA requires encryption for PHI at rest and in transit. Here is how to implement both:
flowchart LR
REQ(["Inbound request"])
PII["PII detection<br/>regex plus NER"]
POL{"Policy engine<br/>OPA or rules"}
REDACT["Redact or mask"]
LLM["LLM call"]
OUT["Response"]
AUDIT[("Append only<br/>audit log")]
BLOCK(["Block plus<br/>notify DPO"])
REQ --> PII --> POL
POL -->|Allow| REDACT --> LLM --> OUT --> AUDIT
POL -->|Deny| BLOCK
style POL fill:#4f46e5,stroke:#4338ca,color:#fff
style AUDIT fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style BLOCK fill:#dc2626,stroke:#b91c1c,color:#fff
style OUT fill:#059669,stroke:#047857,color:#fff
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64
import os
class PHIEncryptor:
"""Encrypts and decrypts protected health information at rest."""
def __init__(self, master_key: bytes):
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=b"hipaa-compliant-salt", # In production, use unique salt per record
iterations=480000,
)
key = base64.urlsafe_b64encode(kdf.derive(master_key))
self._cipher = Fernet(key)
def encrypt(self, plaintext: str) -> str:
return self._cipher.encrypt(plaintext.encode()).decode()
def decrypt(self, ciphertext: str) -> str:
return self._cipher.decrypt(ciphertext.encode()).decode()
class FieldLevelEncryption:
"""Encrypts specific PHI fields while leaving non-PHI fields accessible."""
PHI_FIELDS = {"name", "dob", "ssn", "phone", "email", "address", "mrn", "diagnosis"}
def __init__(self, encryptor: PHIEncryptor):
self._encryptor = encryptor
def encrypt_record(self, record: dict) -> dict:
encrypted = {}
for key, value in record.items():
if key in self.PHI_FIELDS and isinstance(value, str):
encrypted[key] = self._encryptor.encrypt(value)
encrypted[f"{key}_encrypted"] = True
else:
encrypted[key] = value
return encrypted
def decrypt_record(self, record: dict) -> dict:
decrypted = {}
for key, value in record.items():
if key.endswith("_encrypted"):
continue
if record.get(f"{key}_encrypted") and isinstance(value, str):
decrypted[key] = self._encryptor.decrypt(value)
else:
decrypted[key] = value
return decrypted
HIPAA's minimum necessary standard requires that users and systems only access the PHI they need for their specific function:
Hear it before you finish reading
Talk to a live CallSphere AI voice agent for healthcare in your browser — 60 seconds, no signup.
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
from datetime import datetime
class Role(Enum):
SCHEDULING_AGENT = "scheduling_agent"
TRIAGE_AGENT = "triage_agent"
BILLING_AGENT = "billing_agent"
DOCUMENTATION_AGENT = "documentation_agent"
ADMIN = "admin"
@dataclass
class AccessPolicy:
role: Role
allowed_fields: set[str]
allowed_operations: set[str] # read, write, delete
requires_break_glass: set[str] = field(default_factory=set)
class PHIAccessController:
POLICIES = {
Role.SCHEDULING_AGENT: AccessPolicy(
role=Role.SCHEDULING_AGENT,
allowed_fields={"name", "dob", "phone", "insurance_id", "appointment_history"},
allowed_operations={"read"},
),
Role.TRIAGE_AGENT: AccessPolicy(
role=Role.TRIAGE_AGENT,
allowed_fields={"name", "dob", "allergies", "current_medications", "symptoms"},
allowed_operations={"read"},
requires_break_glass={"full_medical_history"},
),
Role.BILLING_AGENT: AccessPolicy(
role=Role.BILLING_AGENT,
allowed_fields={"name", "dob", "insurance_id", "charges", "payments", "balance"},
allowed_operations={"read", "write"},
),
}
def __init__(self, audit_logger: "AuditLogger"):
self._audit = audit_logger
def check_access(
self, role: Role, field_name: str, operation: str, reason: str
) -> bool:
policy = self.POLICIES.get(role)
if not policy:
self._audit.log_denied(role.value, field_name, operation, "no policy defined")
return False
if operation not in policy.allowed_operations:
self._audit.log_denied(role.value, field_name, operation, "operation not allowed")
return False
if field_name in policy.requires_break_glass:
self._audit.log_break_glass(role.value, field_name, reason)
return True
if field_name not in policy.allowed_fields:
self._audit.log_denied(role.value, field_name, operation, "field not in allowed list")
return False
self._audit.log_access(role.value, field_name, operation, reason)
return True
HIPAA requires a complete audit trail of who accessed what PHI, when, and why. This is the most commonly overlooked requirement in AI agent systems:
import json
from datetime import datetime
from typing import Optional
@dataclass
class AuditEntry:
timestamp: datetime
actor: str
action: str
resource: str
field_accessed: Optional[str]
patient_id: Optional[str]
reason: str
outcome: str # success, denied, error
ip_address: Optional[str] = None
session_id: Optional[str] = None
class AuditLogger:
def __init__(self, log_store):
self._store = log_store
def log_access(self, actor: str, field: str, operation: str, reason: str) -> None:
entry = AuditEntry(
timestamp=datetime.utcnow(),
actor=actor,
action=operation,
resource="phi",
field_accessed=field,
patient_id=None, # Set by caller
reason=reason,
outcome="success",
)
self._store.write(entry)
def log_denied(self, actor: str, field: str, operation: str, reason: str) -> None:
entry = AuditEntry(
timestamp=datetime.utcnow(),
actor=actor,
action=operation,
resource="phi",
field_accessed=field,
patient_id=None,
reason=reason,
outcome="denied",
)
self._store.write(entry)
def log_break_glass(self, actor: str, field: str, reason: str) -> None:
entry = AuditEntry(
timestamp=datetime.utcnow(),
actor=actor,
action="break_glass_access",
resource="phi",
field_accessed=field,
patient_id=None,
reason=reason,
outcome="success",
)
self._store.write(entry)
def query_by_patient(self, patient_id: str, start: datetime, end: datetime) -> list[AuditEntry]:
return self._store.query(patient_id=patient_id, start=start, end=end)
def generate_access_report(self, patient_id: str) -> dict:
entries = self._store.query(patient_id=patient_id)
return {
"patient_id": patient_id,
"total_accesses": len(entries),
"unique_actors": list({e.actor for e in entries}),
"denied_attempts": sum(1 for e in entries if e.outcome == "denied"),
"break_glass_events": sum(1 for e in entries if e.action == "break_glass_access"),
"date_range": {
"earliest": min(e.timestamp for e in entries).isoformat() if entries else None,
"latest": max(e.timestamp for e in entries).isoformat() if entries else None,
},
}
Any AI vendor that processes PHI on behalf of a covered entity must sign a Business Associate Agreement (BAA). This applies to LLM providers, cloud hosting, and any third-party API the agent calls. Key BAA requirements include defining permitted uses of PHI, requiring the vendor to implement HIPAA-compliant safeguards, mandating breach notification within a specified timeframe, requiring return or destruction of PHI upon contract termination, and allowing the covered entity to audit compliance.
HIPAA does not specify exact retention periods, but state laws typically require keeping medical records for 6 to 10 years. AI agent conversation logs that contain PHI are subject to the same retention rules:
from datetime import datetime, timedelta
class DataRetentionManager:
DEFAULT_RETENTION_YEARS = 7
def __init__(self, encryptor: PHIEncryptor, audit_logger: AuditLogger):
self._encryptor = encryptor
self._audit = audit_logger
def check_retention_status(self, record_date: datetime) -> dict:
retention_end = record_date + timedelta(days=365 * self.DEFAULT_RETENTION_YEARS)
now = datetime.utcnow()
return {
"record_date": record_date.isoformat(),
"retention_expires": retention_end.isoformat(),
"eligible_for_disposal": now > retention_end,
"days_remaining": max(0, (retention_end - now).days),
}
def secure_dispose(self, record_id: str, record_data: dict) -> dict:
retention = self.check_retention_status(
datetime.fromisoformat(record_data.get("created_at", datetime.utcnow().isoformat()))
)
if not retention["eligible_for_disposal"]:
return {
"disposed": False,
"reason": f"Record must be retained until {retention['retention_expires']}",
}
self._audit.log_access(
actor="retention_system",
field="full_record",
operation="delete",
reason=f"Retention period expired for record {record_id}",
)
# In production: overwrite with random data, then delete
return {"disposed": True, "record_id": record_id, "method": "cryptographic_erasure"}
Cryptographic erasure — destroying the encryption keys rather than the data — is an accepted disposal method under HIPAA. When the key is gone, the encrypted data is unrecoverable.
Still reading? Stop comparing — try CallSphere live.
See the healthcare AI agent handle a real call — complete, industry-specific, and live in your browser. No signup.
Yes. If your AI agent uses a third-party LLM API (such as OpenAI or Anthropic) and sends PHI in prompts, you need a BAA with that LLM provider specifically. The cloud hosting BAA covers infrastructure; the LLM BAA covers the model provider. Some LLM providers offer HIPAA-eligible tiers with BAAs, while others explicitly exclude PHI from their terms of service. Always verify before sending any PHI to an external model.
Conversation logs between patients and AI agents are considered part of the designated record set if they contain PHI and are used to make decisions about the patient's care. This means they must be encrypted, access-controlled, retained according to state requirements, and available to patients who request their records. Logs should be stored separately from application logs, with PHI fields encrypted at the field level.
A breach occurs when PHI is accessed, used, or disclosed in a way not permitted by HIPAA. For AI agents, common breach scenarios include conversation logs with PHI being exposed through an unsecured API endpoint, PHI appearing in application error logs or monitoring dashboards, an LLM provider retaining PHI in training data without authorization, or an unauthorized user accessing the agent's administrative interface. If a breach affects 500 or more individuals, HHS must be notified within 60 days. All breaches require individual notification to affected patients.
#HealthcareAI #HIPAACompliance #Security #AuditLogging #Encryption #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
How to build a safety eval pipeline that runs known jailbreak corpora, prompt-injection attacks, and tool-misuse scenarios on every release — and gates merges on it.
Inside NVIDIA OpenShell — the open-source secure runtime for autonomous desktop agents. Sandboxing, policy enforcement, and why it matters in 2026.
Stop the agent BEFORE it does the wrong thing. How to wire input and output guardrails in the OpenAI Agents SDK with cheap classifiers and an eval suite that proves they work.
AWS HealthScribe became the open scribe layer EHR vendors built on top of in 2026. Here's the API surface, the per-encounter pricing, the BAA terms.
NeMo Guardrails and LlamaGuard solve overlapping problems with different architectures. The trade-offs once you push them past 100 RPS in production agent stacks.
Apollo, Manipal, and Narayana scaled AI agents across Bangalore in 2026. Here's the deployments across radiology, intake, and follow-up, the costs.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI