Healthcare Voice Agents

Build HIPAA-compliant AI voice agents for healthcare - appointment scheduling, prescription refills, lab results, patient follow-ups, and EHR integration.

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