Healthcare Voice Agents
Build intelligent, HIPAA-compliant voice agents for healthcare organizations that handle appointment scheduling, prescription refills, lab results notifications, and patient follow-ups with seamless EHR integration.
Use Cases
| Use Case | Description | Automation Rate |
|---|---|---|
| Appointment Scheduling | Book, reschedule, cancel appointments | 70-85% |
| Appointment Reminders | Outbound confirmation calls | 90%+ |
| Prescription Refills | Process refill requests | 60-75% |
| Lab Results Notification | Notify patients when results are ready | 85%+ |
| Patient Follow-ups | Post-visit check-ins and care instructions | 75-85% |
| Insurance Verification | Verify coverage before appointments | 50-65% |
| Bill Payment | Payment reminders and processing | 65-80% |
Architecture Overview
Patient Call → Telephony (Twilio/Exotel)
↓
┌──────────────────────┐
│ Voice Pipeline │
│ │
│ STT → LLM → TTS │
│ ↓ │
│ Tool Executor │
└──────────┬───────────┘
↓
┌─────────────────┼─────────────────┐
↓ ↓ ↓
┌────────┐ ┌────────────┐ ┌────────────┐
│ EHR │ │ Scheduling │ │ Patient │
│ System │ │ System │ │ Portal │
│(Epic, │ │(Calendly, │ │(MyChart, │
│ Cerner)│ │ Nexmo) │ │ Athena) │
└────────┘ └────────────┘ └────────────┘
Agent Configuration
HIPAA-Compliant Configuration
{
"agent": {
"name": "MedAssist",
"language": "en-US",
"llmProvider": "gemini-2.5",
"llmModel": "gemini-2.5-flash-lite",
"llmTemperature": 0.3,
"sttProvider": "deepgram",
"sttModel": "nova-3-medical",
"sttConfig": {
"model": "nova-3-medical",
"endpointing": 400,
"interimResults": true,
"smartFormat": true,
"keywords": [
"prescription:2",
"refill:2",
"appointment:2",
"doctor:2",
"medication:2"
]
},
"ttsProvider": "azure",
"ttsVoice": "en-US-JennyNeural",
"ttsConfig": {
"speakingRate": 0.95,
"pitch": "default"
},
"greetingMessage": "Hello, thank you for calling City Medical Center. This is an automated assistant. How may I help you today?",
"allowInterruptions": true,
"prompt": "...",
"tools": [],
"webhooks": {
"url": "https://your-hipaa-server.com/webhooks/voice",
"events": ["call.ended", "appointment.booked", "refill.requested"],
"headers": {
"X-HIPAA-Audit": "enabled"
}
},
"security": {
"piiRedaction": true,
"transcriptEncryption": true,
"recordingEnabled": false,
"auditLogging": true,
"dataRetentionDays": 90
}
}
}
Multi-Language Healthcare Configuration (Spanish)
{
"agent": {
"name": "MedAssist Espanol",
"language": "es-US",
"llmProvider": "gemini-2.5",
"llmModel": "gemini-2.5-flash-lite",
"sttProvider": "deepgram",
"sttModel": "nova-3",
"sttConfig": {
"language": "es",
"model": "nova-3"
},
"ttsProvider": "azure",
"ttsVoice": "es-US-AlonsoNeural",
"greetingMessage": "Hola, gracias por llamar al Centro Medico de la Ciudad. Soy un asistente automatizado. Como puedo ayudarle hoy?"
}
}
System Prompt
You are a medical receptionist AI for City Medical Center. You handle patient calls professionally, empathetically, and efficiently while maintaining strict HIPAA compliance.
## Your Role
- Schedule, reschedule, and cancel patient appointments
- Process prescription refill requests
- Provide general clinic information
- Notify patients about lab results availability
- Conduct post-visit follow-up calls
- Transfer to human staff for clinical questions
## HIPAA Compliance Requirements
### Identity Verification (REQUIRED before disclosing ANY patient information)
Before providing appointment details, lab results, or any PHI:
1. Ask for patient's full name
2. Ask for date of birth
3. Verify phone number matches records
Script: "For your privacy and security, I need to verify your identity. Could you please provide your full name and date of birth?"
### Information You CAN Share (after verification)
- Appointment dates and times
- Prescription refill status (ready/not ready)
- Lab results availability (NOT the actual results)
- General clinic hours and location
- Provider names and specialties
### Information You CANNOT Share
- Actual lab results or test values
- Diagnoses or medical conditions
- Treatment details
- Information about other patients
- Information to unverified callers
### Safe Responses for Sensitive Topics
- Lab results content: "Your lab results are ready. Please log into MyChart or speak with your provider to review them."
- Medical advice: "I'm not able to provide medical advice. Would you like me to schedule an appointment or transfer you to a nurse?"
- Prescriptions issues: "I'll send a message to the pharmacy team. They'll call you back within 2 hours."
## Guidelines
### Appointment Scheduling Flow
1. Verify patient identity (name + DOB)
2. Ask what type of appointment they need
3. Ask for preferred provider (if any)
4. Check availability using the tool
5. Offer 2-3 available time slots
6. Confirm selection and provide instructions
7. Offer to add to calendar or send SMS confirmation
### Prescription Refill Flow
1. Verify patient identity
2. Ask for medication name and pharmacy
3. Check refill eligibility
4. Submit request or explain why it cannot be processed
5. Provide expected timeline
### Speaking Style
- Warm but professional
- Use simple, clear language
- Spell out medical terms if needed
- Speak at a measured pace
- Show empathy for health concerns
### Date/Time Handling
- Always confirm: "That's Monday, January 15th at 2:30 PM, is that correct?"
- Use 12-hour format with AM/PM
- Mention day of week AND date
### Escalation Triggers - Transfer to Human Staff
- Medical symptoms or emergencies
- Billing disputes
- Insurance questions
- Complaints
- Patient requests human
- Prescription questions requiring clinical judgment
- Mental health crises
## Clinic Information
- Name: City Medical Center
- Hours: Monday-Friday 8 AM - 6 PM, Saturday 9 AM - 1 PM
- Address: 100 Healthcare Drive, Suite 200
- Phone: (555) 123-4567
- Patient Portal: mychart.citymedical.com
- Pharmacy: On-site, open same hours
## Appointment Types
| Type | Duration | Providers |
|------|----------|-----------|
| New Patient | 45 min | All providers |
| Follow-up | 15 min | All providers |
| Annual Physical | 60 min | Dr. Smith, Dr. Johnson |
| Sick Visit | 20 min | All providers |
| Telehealth | 15 min | All providers |
## Example Conversations
**Appointment Scheduling:**
Patient: "I need to schedule a checkup"
You: "I'd be happy to help you schedule a checkup. For your security, could you please provide your full name and date of birth?"
Patient: "John Smith, March 15, 1980"
You: "Thank you, Mr. Smith. I've verified your information. Do you have a preferred provider, or would you like the next available appointment?"
**Prescription Refill:**
Patient: "I need to refill my blood pressure medication"
You: "I can help with that. First, for your security, may I have your full name and date of birth?"
Patient: "Mary Johnson, July 22, 1965"
You: "Thank you, Mrs. Johnson. What's the name of the medication you need refilled, and which pharmacy should we send it to?"
**Lab Results (Proper Handling):**
Patient: "Are my lab results back?"
You: "Let me check for you. For your security, could you please provide your full name and date of birth?"
Patient: "Robert Williams, November 3, 1975"
You: "Thank you, Mr. Williams. Yes, your lab results are now available. For detailed results, please log into MyChart at mychart.citymedical.com, or your provider will discuss them at your next appointment. Would you like to schedule a follow-up?"
Tools
Patient Identity Verification
{
"type": "function",
"function": {
"name": "verify_patient_identity",
"description": "Verify patient identity using name and date of birth. MUST be called before accessing any patient information.",
"parameters": {
"type": "object",
"properties": {
"full_name": {
"type": "string",
"description": "Patient's full name"
},
"date_of_birth": {
"type": "string",
"description": "Patient's date of birth (YYYY-MM-DD)"
},
"phone_number": {
"type": "string",
"description": "Caller's phone number for verification"
}
},
"required": ["full_name", "date_of_birth"]
}
}
}
Check Appointment Availability
{
"type": "function",
"function": {
"name": "check_availability",
"description": "Check available appointment slots for a specific provider or appointment type",
"parameters": {
"type": "object",
"properties": {
"appointment_type": {
"type": "string",
"enum": ["new_patient", "follow_up", "annual_physical", "sick_visit", "telehealth"],
"description": "Type of appointment"
},
"provider_id": {
"type": "string",
"description": "Specific provider ID (optional)"
},
"preferred_date": {
"type": "string",
"description": "Preferred date (YYYY-MM-DD)"
},
"time_preference": {
"type": "string",
"enum": ["morning", "afternoon", "any"],
"description": "Time of day preference"
}
},
"required": ["appointment_type"]
}
}
}
Book Appointment
{
"type": "function",
"function": {
"name": "book_appointment",
"description": "Book an appointment for a verified patient",
"parameters": {
"type": "object",
"properties": {
"patient_id": {
"type": "string",
"description": "Verified patient ID from identity check"
},
"provider_id": {
"type": "string",
"description": "Provider ID"
},
"appointment_type": {
"type": "string",
"description": "Type of appointment"
},
"datetime": {
"type": "string",
"description": "Appointment datetime (ISO 8601)"
},
"reason": {
"type": "string",
"description": "Reason for visit"
},
"send_confirmation": {
"type": "boolean",
"description": "Send SMS/email confirmation",
"default": true
}
},
"required": ["patient_id", "provider_id", "appointment_type", "datetime"]
}
}
}
Request Prescription Refill
{
"type": "function",
"function": {
"name": "request_prescription_refill",
"description": "Submit a prescription refill request",
"parameters": {
"type": "object",
"properties": {
"patient_id": {
"type": "string",
"description": "Verified patient ID"
},
"medication_name": {
"type": "string",
"description": "Name of medication"
},
"pharmacy_name": {
"type": "string",
"description": "Preferred pharmacy"
},
"pharmacy_phone": {
"type": "string",
"description": "Pharmacy phone number"
},
"urgency": {
"type": "string",
"enum": ["routine", "urgent"],
"description": "Urgency level"
}
},
"required": ["patient_id", "medication_name"]
}
}
}
Check Lab Results Status
{
"type": "function",
"function": {
"name": "check_lab_results_status",
"description": "Check if lab results are available (does NOT return actual results)",
"parameters": {
"type": "object",
"properties": {
"patient_id": {
"type": "string",
"description": "Verified patient ID"
}
},
"required": ["patient_id"]
}
}
}
Transfer to Clinical Staff
{
"type": "function",
"function": {
"name": "transfer_to_staff",
"description": "Transfer call to appropriate clinical or administrative staff",
"parameters": {
"type": "object",
"properties": {
"department": {
"type": "string",
"enum": ["nursing", "billing", "records", "pharmacy", "front_desk", "urgent"],
"description": "Department to transfer to"
},
"reason": {
"type": "string",
"description": "Reason for transfer"
},
"patient_id": {
"type": "string",
"description": "Patient ID for context"
},
"priority": {
"type": "string",
"enum": ["normal", "high", "urgent"],
"description": "Call priority"
}
},
"required": ["department", "reason"]
}
}
}
Tool Handlers
Patient Verification Handler
func handleVerifyPatientIdentity(params map[string]any) (string, error) {
fullName := params["full_name"].(string)
dob := params["date_of_birth"].(string)
callerPhone := params["phone_number"].(string)
// Query EHR system
patient, err := ehrClient.FindPatient(fullName, dob)
if err != nil {
logSecurityEvent("verification_failed", fullName, callerPhone)
return "I couldn't verify that information. Please check and try again, or I can transfer you to our front desk.", nil
}
// Verify phone matches (optional additional security)
if callerPhone != "" && !matchesPatientPhone(patient, callerPhone) {
logSecurityEvent("phone_mismatch", fullName, callerPhone)
// Continue but flag for review
}
// Store verified patient ID in session
session.Set("verified_patient_id", patient.ID)
session.Set("patient_name", patient.FirstName)
logAudit("patient_verified", patient.ID, callerPhone)
return fmt.Sprintf("Thank you, %s. I've verified your information. How can I help you today?",
patient.FirstName), nil
}
Appointment Booking Handler
func handleBookAppointment(params map[string]any) (string, error) {
patientID := params["patient_id"].(string)
providerID := params["provider_id"].(string)
aptType := params["appointment_type"].(string)
datetime := params["datetime"].(string)
// Parse datetime
aptTime, err := time.Parse(time.RFC3339, datetime)
if err != nil {
return "I'm having trouble with that date and time. Could you please confirm?", nil
}
// Book in scheduling system
appointment, err := schedulingClient.BookAppointment(BookingRequest{
PatientID: patientID,
ProviderID: providerID,
Type: aptType,
DateTime: aptTime,
Duration: getAppointmentDuration(aptType),
ConfirmationSMS: true,
})
if err != nil {
if errors.Is(err, ErrSlotTaken) {
return "I'm sorry, that slot was just taken. Let me find another option.", nil
}
return "I'm having trouble booking that appointment. Let me transfer you to our scheduling team.", nil
}
// Log to EHR
ehrClient.LogAppointment(appointment)
// Audit log
logAudit("appointment_booked", patientID, appointment.ID)
return fmt.Sprintf(
"I've scheduled your %s appointment with %s for %s at %s. "+
"You'll receive a confirmation text shortly. "+
"Please arrive 15 minutes early with your insurance card. Is there anything else I can help with?",
aptType,
appointment.ProviderName,
aptTime.Format("Monday, January 2nd"),
aptTime.Format("3:04 PM"),
), nil
}
Prescription Refill Handler
func handlePrescriptionRefill(params map[string]any) (string, error) {
patientID := params["patient_id"].(string)
medicationName := params["medication_name"].(string)
pharmacyName := params["pharmacy_name"].(string)
urgency := params["urgency"].(string)
// Check refill eligibility
eligibility, err := ehrClient.CheckRefillEligibility(patientID, medicationName)
if err != nil {
return "I'm having trouble checking that prescription. Let me transfer you to our pharmacy team.", nil
}
if !eligibility.Eligible {
switch eligibility.Reason {
case "no_refills":
return "That prescription has no refills remaining. I'll send a message to your provider to request a new prescription. They'll review it within 24-48 hours.", nil
case "too_early":
return fmt.Sprintf("That prescription isn't due for a refill until %s. Would you like me to set a reminder?",
eligibility.NextRefillDate.Format("January 2")), nil
case "controlled_substance":
return "For this medication, please speak with your provider directly. Would you like to schedule an appointment?", nil
}
}
// Submit refill request
refillRequest, err := pharmacyClient.SubmitRefillRequest(RefillRequest{
PatientID: patientID,
Medication: medicationName,
Pharmacy: pharmacyName,
Urgency: urgency,
})
if err != nil {
return "I couldn't submit that refill request. Let me transfer you to our pharmacy team.", nil
}
logAudit("refill_requested", patientID, refillRequest.ID)
timeline := "24-48 hours"
if urgency == "urgent" {
timeline = "4-6 hours"
}
return fmt.Sprintf(
"I've submitted your refill request for %s to %s. "+
"It should be ready within %s. "+
"The pharmacy will text you when it's ready. Is there anything else?",
medicationName, pharmacyName, timeline,
), nil
}
EHR Integration
Epic Integration
type EpicClient struct {
baseURL string
clientID string
privateKey *rsa.PrivateKey
accessToken string
httpClient *http.Client
}
func (c *EpicClient) GetPatientAppointments(patientID string) ([]Appointment, error) {
// FHIR R4 Appointment search
url := fmt.Sprintf("%s/api/FHIR/R4/Appointment?patient=%s&status=booked",
c.baseURL, patientID)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer "+c.accessToken)
req.Header.Set("Accept", "application/fhir+json")
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var bundle FHIRBundle
json.NewDecoder(resp.Body).Decode(&bundle)
return parseAppointments(bundle), nil
}
func (c *EpicClient) CreateAppointment(apt Appointment) (*Appointment, error) {
fhirApt := toFHIRAppointment(apt)
body, _ := json.Marshal(fhirApt)
req, _ := http.NewRequest("POST", c.baseURL+"/api/FHIR/R4/Appointment", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+c.accessToken)
req.Header.Set("Content-Type", "application/fhir+json")
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 201 {
return nil, fmt.Errorf("failed to create appointment: %s", resp.Status)
}
var created FHIRAppointment
json.NewDecoder(resp.Body).Decode(&created)
return fromFHIRAppointment(created), nil
}
Cerner Integration
type CernerClient struct {
baseURL string
credentials OAuthCredentials
}
func (c *CernerClient) FindPatient(name, dob string) (*Patient, error) {
// FHIR R4 Patient search
url := fmt.Sprintf("%s/Patient?name=%s&birthdate=%s",
c.baseURL,
url.QueryEscape(name),
dob,
)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer "+c.credentials.AccessToken)
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
var bundle FHIRBundle
json.NewDecoder(resp.Body).Decode(&bundle)
if len(bundle.Entry) == 0 {
return nil, ErrPatientNotFound
}
return parsePatient(bundle.Entry[0]), nil
}
HL7 FHIR Webhook Handler
type FHIRWebhookHandler struct {
voiceClient *VoiceAgentClient
}
func (h *FHIRWebhookHandler) HandleLabResult(w http.ResponseWriter, r *http.Request) {
var observation FHIRObservation
json.NewDecoder(r.Body).Decode(&observation)
patientID := extractPatientID(observation.Subject)
patient, _ := h.ehrClient.GetPatient(patientID)
if patient.NotificationPreference == "phone" {
// Trigger outbound call
h.voiceClient.InitiateCall(CallRequest{
AgentID: "lab_notification",
To: patient.Phone,
Variables: map[string]string{
"patientName": patient.FirstName,
"testType": observation.Code.Display,
},
})
}
w.WriteHeader(http.StatusOK)
}
HIPAA Compliance
PHI Handling
type HIPAACompliantSession struct {
callID string
verifiedPatient *Patient
accessLog []AccessLogEntry
piiRedactor *PIIRedactor
encryptionKey []byte
}
func (s *HIPAACompliantSession) LogPHIAccess(dataType, action string) {
entry := AccessLogEntry{
Timestamp: time.Now(),
CallID: s.callID,
PatientID: s.verifiedPatient.ID,
DataType: dataType,
Action: action,
AccessedBy: "voice_agent",
}
s.accessLog = append(s.accessLog, entry)
// Write to HIPAA audit log
hipaaAuditLog.Write(entry)
}
func (s *HIPAACompliantSession) GetTranscript() string {
rawTranscript := s.conversation.GetTranscript()
// Redact PHI before storage
return s.piiRedactor.RedactPHI(rawTranscript)
}
PHI Redaction
type HealthcarePIIRedactor struct {
patterns map[string]*regexp.Regexp
}
func NewHealthcarePIIRedactor() *HealthcarePIIRedactor {
return &HealthcarePIIRedactor{
patterns: map[string]*regexp.Regexp{
// Standard PII
"ssn": regexp.MustCompile(`\b\d{3}-\d{2}-\d{4}\b`),
"phone": regexp.MustCompile(`\b\d{3}[-.]?\d{3}[-.]?\d{4}\b`),
"email": regexp.MustCompile(`\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b`),
// Healthcare-specific
"mrn": regexp.MustCompile(`\b[A-Z]?\d{6,10}\b`), // Medical Record Number
"dob": regexp.MustCompile(`\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b`),
"insurance_id": regexp.MustCompile(`\b[A-Z]{3}\d{9}\b`),
"npi": regexp.MustCompile(`\b\d{10}\b`), // National Provider ID
// Drug/Prescription info (context-dependent)
"dosage": regexp.MustCompile(`\b\d+\s*(mg|mcg|ml|units?)\b`),
},
}
}
func (r *HealthcarePIIRedactor) RedactPHI(text string) string {
redacted := text
replacements := map[string]string{
"ssn": "[SSN_REDACTED]",
"phone": "[PHONE_REDACTED]",
"mrn": "[MRN_REDACTED]",
"dob": "[DOB_REDACTED]",
"insurance_id": "[INSURANCE_REDACTED]",
}
for name, pattern := range r.patterns {
if replacement, ok := replacements[name]; ok {
redacted = pattern.ReplaceAllString(redacted, replacement)
}
}
return redacted
}
Audit Logging
type HIPAAAuditLogger struct {
storage AuditStorage
encryption *EncryptionService
}
type AuditEntry struct {
ID string `json:"id"`
Timestamp time.Time `json:"timestamp"`
EventType string `json:"event_type"`
UserType string `json:"user_type"` // patient, staff, system
UserID string `json:"user_id"`
PatientID string `json:"patient_id"`
Resource string `json:"resource"` // appointment, prescription, lab_result
Action string `json:"action"` // create, read, update, delete
Outcome string `json:"outcome"` // success, failure
IPAddress string `json:"ip_address"`
CallID string `json:"call_id,omitempty"`
Details string `json:"details,omitempty"`
}
func (l *HIPAAAuditLogger) LogAccess(entry AuditEntry) error {
entry.ID = uuid.New().String()
entry.Timestamp = time.Now().UTC()
// Encrypt sensitive fields
if entry.Details != "" {
encrypted, _ := l.encryption.Encrypt([]byte(entry.Details))
entry.Details = base64.StdEncoding.EncodeToString(encrypted)
}
// Store with retention policy (6 years for HIPAA)
return l.storage.Store(entry, 6*365*24*time.Hour)
}
func (l *HIPAAAuditLogger) LogVoiceAccess(callID, patientID, dataAccessed string) {
l.LogAccess(AuditEntry{
EventType: "phi_access",
UserType: "system",
UserID: "voice_agent",
PatientID: patientID,
Resource: dataAccessed,
Action: "read",
Outcome: "success",
CallID: callID,
})
}
Data Encryption
type HIPAAEncryption struct {
kms KeyManagementService
dataKey []byte
keyVersion string
}
func (e *HIPAAEncryption) EncryptPHI(data []byte) ([]byte, error) {
block, _ := aes.NewCipher(e.dataKey)
gcm, _ := cipher.NewGCM(block)
nonce := make([]byte, gcm.NonceSize())
io.ReadFull(rand.Reader, nonce)
ciphertext := gcm.Seal(nonce, nonce, data, nil)
// Prepend key version for key rotation support
return append([]byte(e.keyVersion+":"), ciphertext...), nil
}
func (e *HIPAAEncryption) EncryptTranscript(transcript string) (string, error) {
encrypted, err := e.EncryptPHI([]byte(transcript))
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(encrypted), nil
}
Outbound Campaigns
Appointment Reminders
type AppointmentReminderCampaign struct {
voiceClient *VoiceAgentClient
ehrClient *EHRClient
agentID string
}
func (c *AppointmentReminderCampaign) SendReminders() {
// Get tomorrow's appointments
tomorrow := time.Now().Add(24 * time.Hour)
appointments, _ := c.ehrClient.GetAppointmentsForDate(tomorrow)
for _, apt := range appointments {
// Check patient notification preferences
patient, _ := c.ehrClient.GetPatient(apt.PatientID)
if patient.PreferredContact == "phone" {
c.initiateReminderCall(patient, apt)
}
}
}
func (c *AppointmentReminderCampaign) initiateReminderCall(patient *Patient, apt Appointment) {
c.voiceClient.InitiateCall(CallRequest{
AgentID: c.agentID,
To: patient.Phone,
Variables: map[string]string{
"patientName": patient.FirstName,
"appointmentTime": apt.DateTime.Format("3:04 PM"),
"appointmentDate": apt.DateTime.Format("January 2"),
"providerName": apt.ProviderName,
"appointmentType": apt.Type,
"location": apt.Location,
},
Webhook: "https://api.clinic.com/webhooks/reminder-outcome",
})
}
Reminder Agent Prompt:
You are calling to remind a patient about their upcoming appointment.
Greeting: "Hello, this is City Medical Center calling for {{patientName}}. I'm calling to remind you about your {{appointmentType}} appointment tomorrow, {{appointmentDate}} at {{appointmentTime}} with {{providerName}}."
## If Patient Confirms
"Wonderful! Please remember to arrive 15 minutes early and bring your insurance card and photo ID. We look forward to seeing you!"
## If Patient Needs to Reschedule
Use the reschedule_appointment tool to find a new time.
"I'd be happy to help you reschedule. Do you have a preference for morning or afternoon?"
## If Patient Cancels
"I understand. I've canceled your appointment. Would you like to schedule a new appointment for a later date?"
Document the cancellation reason.
## If Voicemail
"Hello {{patientName}}, this is City Medical Center reminding you of your appointment tomorrow, {{appointmentDate}} at {{appointmentTime}}. Please call us at 555-123-4567 if you need to reschedule. Thank you."
## Important
- Speak clearly and at a moderate pace
- Do not leave PHI in voicemails beyond appointment time
- If patient has questions, offer to transfer to staff
Post-Visit Follow-up
func (c *FollowUpCampaign) SendFollowUps() {
// Get visits from 24-48 hours ago
visits, _ := c.ehrClient.GetRecentVisits(24*time.Hour, 48*time.Hour)
for _, visit := range visits {
// Skip if patient opted out
if visit.Patient.OptedOutFollowUp {
continue
}
c.initiateFollowUpCall(visit)
}
}
Follow-up Agent Prompt:
You are calling to check on a patient after their recent visit.
Greeting: "Hello, this is City Medical Center calling for {{patientName}}. I'm calling to check in after your visit on {{visitDate}}. Do you have a few minutes?"
## If Yes
"How are you feeling since your visit? Any questions or concerns about your care plan?"
Listen for:
- Medication side effects → Offer to connect to pharmacy/nursing
- Worsening symptoms → Recommend appropriate action or transfer to clinical staff
- Questions about instructions → Answer if within scope, otherwise transfer
- Positive feedback → Document and thank them
## If Concerns Raised
"I'm sorry to hear that. Let me connect you with our nursing staff who can help."
Use transfer_to_staff with department="nursing".
## If All is Well
"That's great to hear! Remember, you can always reach us at 555-123-4567 or through MyChart if anything comes up. Take care!"
## Key Points
- Be empathetic and caring
- Don't provide medical advice
- Document any concerns raised
- Escalate clinical concerns immediately
Metrics and Analytics
Healthcare-Specific KPIs
type HealthcareMetrics struct {
// Efficiency
TotalCalls int
AutomatedResolutionRate float64 // Target: 70%+
AverageHandleTime time.Duration
TransferRate float64
// Appointment Management
AppointmentsBooked int
AppointmentsRescheduled int
AppointmentsCanceled int
NoShowRate float64 // Target: <10%
// Patient Satisfaction
CSAT float64 // Target: 4.5+/5
FCR float64 // First Call Resolution
// Clinical
RefillRequestsProcessed int
LabNotificationsSent int
UrgentTransfers int
// Compliance
VerificationSuccessRate float64
PHIAccessAttempts int
FailedVerifications int
}
func trackHealthcareCall(call *Call) {
metrics.Increment("healthcare.calls.total")
metrics.Gauge("healthcare.handle_time", call.Duration.Seconds())
switch call.Outcome {
case "appointment_booked":
metrics.Increment("healthcare.appointments.booked")
case "refill_requested":
metrics.Increment("healthcare.refills.requested")
case "transferred":
metrics.Increment("healthcare.calls.transferred")
if call.TransferReason == "clinical" {
metrics.Increment("healthcare.transfers.clinical")
}
}
if call.VerificationAttempted {
if call.VerificationSucceeded {
metrics.Increment("healthcare.verification.success")
} else {
metrics.Increment("healthcare.verification.failed")
}
}
}
No-Show Reduction Tracking
type NoShowAnalytics struct {
db *Database
}
func (a *NoShowAnalytics) CalculateNoShowReduction() NoShowReport {
// Get no-show rates before and after voice agent
beforeRate := a.getNoShowRate(time.Now().AddDate(0, -6, 0), time.Now().AddDate(0, -3, 0))
afterRate := a.getNoShowRate(time.Now().AddDate(0, -3, 0), time.Now())
reduction := ((beforeRate - afterRate) / beforeRate) * 100
// Calculate cost savings
avgAppointmentValue := 150.0 // USD
appointmentsPerMonth := 1000
savedAppointments := int(float64(appointmentsPerMonth) * (reduction / 100))
monthlySavings := float64(savedAppointments) * avgAppointmentValue
return NoShowReport{
PreviousNoShowRate: beforeRate,
CurrentNoShowRate: afterRate,
ReductionPercent: reduction,
MonthlySavings: monthlySavings,
}
}
Dashboard Metrics
type HealthcareDashboard struct {
metrics MetricsClient
}
func (d *HealthcareDashboard) GetDailyStats() DailyStats {
today := time.Now().Format("2006-01-02")
return DailyStats{
TotalCalls: d.metrics.Count("healthcare.calls.total", today),
AppointmentsBooked: d.metrics.Count("healthcare.appointments.booked", today),
RefillsProcessed: d.metrics.Count("healthcare.refills.requested", today),
AutomationRate: d.metrics.Ratio("healthcare.calls.resolved", "healthcare.calls.total", today),
AvgHandleTime: d.metrics.Average("healthcare.handle_time", today),
PatientSatisfaction: d.metrics.Average("healthcare.csat", today),
}
}
Best Practices
1. Always Verify Identity First
func (p *Pipeline) processHealthcareRequest(ctx context.Context, request string) {
session := GetSession(ctx)
if !session.IsPatientVerified() {
// Force verification before any PHI access
response := "For your privacy and security, I need to verify your identity first. " +
"Could you please provide your full name and date of birth?"
p.speak(response)
return
}
// Proceed with request
p.handleVerifiedRequest(ctx, request)
}
2. Never Provide Clinical Advice
var clinicalKeywords = []string{
"should I take", "is it safe", "what dose", "side effect",
"symptoms", "diagnosis", "treatment", "medicine for",
}
func containsClinicalQuestion(text string) bool {
lower := strings.ToLower(text)
for _, keyword := range clinicalKeywords {
if strings.Contains(lower, keyword) {
return true
}
}
return false
}
func (p *Pipeline) handleMessage(text string) {
if containsClinicalQuestion(text) {
p.speak("I'm not able to provide medical advice. Would you like me to " +
"schedule an appointment with your provider, or transfer you to our nursing staff?")
return
}
// Continue normal processing
}
3. Handle Emergencies Appropriately
var emergencyKeywords = []string{
"chest pain", "can't breathe", "heart attack", "stroke",
"unconscious", "bleeding heavily", "severe pain", "suicide",
"overdose", "emergency",
}
func (p *Pipeline) checkForEmergency(text string) bool {
lower := strings.ToLower(text)
for _, keyword := range emergencyKeywords {
if strings.Contains(lower, keyword) {
p.handleEmergency()
return true
}
}
return false
}
func (p *Pipeline) handleEmergency() {
p.speak("This sounds like a medical emergency. Please hang up and call 911 immediately. " +
"If you're unable to call, stay on the line and I'll transfer you to emergency services.")
// Log and alert
alertOps("Emergency keyword detected", p.session.CallID)
// Transfer to emergency queue
p.transferCall("emergency", "Medical emergency detected")
}
4. Maintain Empathetic Tone
var empathyPhrases = map[string][]string{
"appointment_canceled": {
"I understand things come up. Let's find a time that works better for you.",
"No problem at all. When would be a better time for you?",
},
"long_wait": {
"I appreciate your patience. Let me help you right away.",
"Thank you for waiting. How can I assist you today?",
},
"prescription_issue": {
"I understand how important your medication is. Let me look into this.",
"I'm sorry for the inconvenience. Let me help resolve this.",
},
"test_anxiety": {
"I understand waiting for results can be stressful. Let me check on that for you.",
},
}
func getEmpathyPhrase(situation string) string {
phrases, ok := empathyPhrases[situation]
if !ok {
return "I understand. Let me help you with that."
}
return phrases[rand.Intn(len(phrases))]
}
5. Secure Call Recording
type HIPAARecorder struct {
encryption *HIPAAEncryption
storage SecureStorage
}
func (r *HIPAARecorder) RecordCall(callID string, audio []byte, transcript string) error {
// Encrypt audio
encryptedAudio, err := r.encryption.EncryptPHI(audio)
if err != nil {
return err
}
// Redact and encrypt transcript
redactedTranscript := redactPHI(transcript)
encryptedTranscript, err := r.encryption.EncryptTranscript(redactedTranscript)
if err != nil {
return err
}
// Store with access controls
return r.storage.Store(Recording{
CallID: callID,
EncryptedAudio: encryptedAudio,
EncryptedTranscript: encryptedTranscript,
CreatedAt: time.Now(),
RetentionUntil: time.Now().AddDate(6, 0, 0), // 6 year HIPAA retention
AccessLevel: "phi_authorized",
})
}
Security Checklist
HIPAA Technical Safeguards
- All PHI encrypted at rest (AES-256)
- All data in transit uses TLS 1.2+
- Unique user identification for audit trails
- Automatic logoff after inactivity
- Audit controls and logging enabled
- PHI access logged with timestamp, user, action
- Data integrity controls in place
- Emergency access procedures documented
Voice Agent Specific
- Patient identity verification before PHI access
- PII/PHI redaction in transcripts and logs
- Clinical questions routed to staff
- Emergency detection and routing
- Call recordings encrypted and access-controlled
- Audit logging for all PHI access
- BAA in place with all voice/cloud providers
- Regular security assessments
Vendor Compliance
- STT provider is HIPAA compliant (BAA signed)
- TTS provider is HIPAA compliant (BAA signed)
- LLM provider is HIPAA compliant (BAA signed)
- Telephony provider is HIPAA compliant (BAA signed)
- Cloud hosting is HIPAA compliant
- All subprocessors documented
Next Steps
- Appointment Booking Example - Generic appointment scheduling
- Function Calling - Tool integration
- Security - General security practices
- Webhooks - EHR integration webhooks