Skip to content

Implement Sinch Conversations OCE Module #1

@kojiromike

Description

@kojiromike

Template-Based Implementation Plan

Overview

This document outlines the implementation plan based on Sinch-approved templates for the Conversations API. OpenCoreEMR has a BAA (Business Associate Agreement) with Sinch and has received compliance approval for 12 message templates.

Important: These templates are approved for OpenCoreEMR's use with Sinch. Any other organization using this open-source module must:

  1. Establish their own relationship with Sinch
  2. Execute their own BAA
  3. Submit templates for compliance review
  4. Obtain approval before sending patient messages

Approved Templates Summary

Template Categories

Category Template Type Use Case
Consent Initial Opt-In Confirmation Individual Required after patient opts in
Appointments Appointment Reminder (High-Compliance) Individual Upcoming appointment notification
Appointments Missed Appointment / No-Show Follow-Up Individual After patient misses appointment
Appointments Telehealth Appointment Link Individual Before telehealth visit
Appointments Pre-Visit Instructions Individual Before scheduled visit
Portal Check Your Portal (New Message) Individual New secure message available
Portal Check Your Portal (Test Results) Individual Test results available
Billing Billing Reminder (Statement Ready) Individual/Batch Monthly statement cycle
Billing Billing Reminder (Balance Due) Individual/Batch Outstanding balance
Wellness Preventive Care / Wellness Reminder Individual/Batch Annual wellness/screening due
Operations Office Closure / Emergency Update Batch Clinic closure notification
Wellness Public Health Announcement Batch Health campaigns (flu shots, etc.)
Feedback Post-Visit Feedback Survey Individual After visit completion

Individual vs Batch Communication

Strictly Individual (1-to-1)

These require patient-specific context and should only be sent individually:

  1. Initial Opt-In Confirmation - Sent immediately after opt-in
  2. Appointment Reminder - Patient has specific appointment
  3. Missed Appointment Follow-Up - Patient missed specific appointment
  4. Telehealth Link - Patient has specific telehealth appointment
  5. Pre-Visit Instructions - Patient has upcoming visit
  6. Check Your Portal (New Message) - Patient has new message
  7. Check Your Portal (Test Results) - Patient has new results
  8. Post-Visit Feedback Survey - Patient just completed visit

Strictly Batch (1-to-many)

These are generic announcements sent to groups:

  1. Office Closure / Emergency Update - Broadcast to all active patients
  2. Public Health Announcement - Campaign to eligible cohort (e.g., all patients, patients 65+)

Flexible (Both Individual and Batch)

These can be sent in either context:

  1. Billing Reminder (Statement Ready) - Batch for monthly statement run, Individual for specific patient
  2. Billing Reminder (Balance Due) - Batch for collections cycle, Individual for specific account
  3. Preventive Care / Wellness Reminder - Batch for wellness campaign, Individual when patient is due

Template Variables

Standard Variables (All Templates)

// Required in all messages
{{ clinic_name }}  // Organization name
{{ opt_out }}      // "Reply STOP to opt-out" (REQUIRED)
{{ phone }}        // Clinic phone number

Template-Specific Variables

// Appointment templates
{{ appt_time }}           // "Saturday, Nov 15, 2025 at 2:30 PM EST"
{{ provider_name }}       // Provider name (optional, PHI risk)
{{ appt_date }}           // Appointment date (optional, PHI risk)
{{ portal_link }}         // Secure portal URL
{{ telehealth_link }}     // Telehealth video call URL

// Office closure
{{ reason_for_closure }}  // "severe weather"
{{ date_closed }}         // "Saturday, Nov 22, 2025"
{{ website_link }}        // Clinic website URL

// Portal notifications
{{ portal_link }}         // Secure portal login URL

// Billing
{{ billing_phone }}       // Billing department phone
{{ portal_link }}         // Portal for payment

// Wellness/preventive
{{ scheduling_link }}     // Online scheduling URL

// Survey
{{ survey_link }}         // Survey URL (must be HIPAA-compliant)

Features to Implement

Phase 1: Core Messaging Infrastructure

1.1 Template Management System

Database Schema:

CREATE TABLE oce_sinch_message_templates (
  id INT PRIMARY KEY AUTO_INCREMENT,
  template_key VARCHAR(100) UNIQUE NOT NULL,       -- 'appointment_reminder', 'opt_in_confirmation', etc.
  template_name VARCHAR(255) NOT NULL,
  category VARCHAR(50) NOT NULL,                   -- 'consent', 'appointments', 'portal', 'billing', etc.
  communication_type ENUM('individual', 'batch', 'both') NOT NULL,
  body TEXT NOT NULL,                              -- Template with {{ variables }}
  required_variables JSON NOT NULL,                -- ['clinic_name', 'portal_link', 'phone', 'opt_out']
  compliance_confidence INT NOT NULL,              -- 90-100%
  sinch_approved BOOLEAN DEFAULT TRUE,
  active BOOLEAN DEFAULT TRUE,
  created_at DATETIME,
  updated_at DATETIME,
  INDEX idx_category (category),
  INDEX idx_type (communication_type),
  INDEX idx_active (active)
);

Template Service:

class TemplateService
{
    /**
     * Render a template with variables
     */
    public function render(string $templateKey, array $variables): string;

    /**
     * Validate required variables are present
     */
    public function validateVariables(string $templateKey, array $variables): bool;

    /**
     * Get template by key
     */
    public function getTemplate(string $templateKey): array;

    /**
     * List templates by category
     */
    public function getTemplatesByCategory(string $category): array;

    /**
     * Check if template is approved for batch sending
     */
    public function isBatchApproved(string $templateKey): bool;
}

1.2 Consent Management

Database Schema:

CREATE TABLE oce_sinch_patient_consent (
  id INT PRIMARY KEY AUTO_INCREMENT,
  patient_id BIGINT NOT NULL,
  phone_number VARCHAR(20) NOT NULL,               -- E.164 format
  opted_in BOOLEAN DEFAULT FALSE,
  opt_in_method VARCHAR(50),                       -- 'web_form', 'in_person', 'portal'
  opt_in_date DATETIME,
  opt_in_ip_address VARCHAR(45),
  opted_out BOOLEAN DEFAULT FALSE,
  opt_out_date DATETIME,
  opt_out_method VARCHAR(50),                      -- 'sms_stop', 'web_form', 'in_person'
  consent_text TEXT,                               -- What they agreed to
  created_at DATETIME,
  updated_at DATETIME,
  INDEX idx_patient (patient_id),
  INDEX idx_phone (phone_number),
  INDEX idx_opted_in (opted_in),
  UNIQUE KEY unique_patient_phone (patient_id, phone_number)
);

Consent Service:

class ConsentService
{
    /**
     * Check if patient has active consent
     */
    public function hasConsent(int $patientId, string $phoneNumber): bool;

    /**
     * Record opt-in and send confirmation message
     */
    public function optIn(
        int $patientId,
        string $phoneNumber,
        string $method,
        ?string $ipAddress = null
    ): void;

    /**
     * Record opt-out (STOP keyword)
     */
    public function optOut(
        int $patientId,
        string $phoneNumber,
        string $method
    ): void;

    /**
     * Send initial opt-in confirmation message
     */
    private function sendOptInConfirmation(int $patientId, string $phoneNumber): void;
}

1.3 Keyword Handler (HELP, STOP)

Database Schema:

CREATE TABLE oce_sinch_keyword_responses (
  id INT PRIMARY KEY AUTO_INCREMENT,
  keyword VARCHAR(20) NOT NULL,                    -- 'STOP', 'START', 'HELP', 'UNSTOP'
  response_template TEXT NOT NULL,
  active BOOLEAN DEFAULT TRUE,
  created_at DATETIME,
  updated_at DATETIME,
  INDEX idx_keyword (keyword)
);

-- Default responses
INSERT INTO oce_sinch_keyword_responses (keyword, response_template, active) VALUES
('STOP', '{{ clinic_name }}: You have been unsubscribed from our text notifications. You will not receive further messages. Reply START to re-subscribe or call {{ phone }} for assistance.', TRUE),
('START', '{{ clinic_name }}: You have been re-subscribed to text notifications. Reply HELP for help. Reply STOP to unsubscribe.', TRUE),
('UNSTOP', '{{ clinic_name }}: You have been re-subscribed to text notifications. Reply HELP for help. Reply STOP to unsubscribe.', TRUE),
('HELP', '{{ clinic_name }}: Text notifications from {{ clinic_name }}. For assistance, call {{ phone }}. Msg&Data rates may apply. Reply STOP to unsubscribe.', TRUE);

Keyword Handler Service:

class KeywordHandlerService
{
    /**
     * Process inbound message for keywords
     */
    public function handleInboundMessage(string $fromNumber, string $messageBody): ?string;

    /**
     * Check if message contains a keyword
     */
    private function detectKeyword(string $messageBody): ?string;

    /**
     * Handle STOP keyword
     */
    private function handleStop(string $phoneNumber): string;

    /**
     * Handle START/UNSTOP keyword
     */
    private function handleStart(string $phoneNumber): string;

    /**
     * Handle HELP keyword
     */
    private function handleHelp(): string;
}

Implementation:

public function handleInboundMessage(string $fromNumber, string $messageBody): ?string
{
    $keyword = $this->detectKeyword($messageBody);

    if (!$keyword) {
        return null; // Not a keyword, route to provider
    }

    // Find patient by phone number
    $patient = $this->findPatientByPhone($fromNumber);

    switch (strtoupper($keyword)) {
        case 'STOP':
        case 'STOPALL':
        case 'UNSUBSCRIBE':
        case 'CANCEL':
        case 'END':
        case 'QUIT':
            return $this->handleStop($fromNumber, $patient);

        case 'START':
        case 'UNSTOP':
        case 'SUBSCRIBE':
            return $this->handleStart($fromNumber, $patient);

        case 'HELP':
        case 'INFO':
            return $this->handleHelp();

        default:
            return null;
    }
}

private function handleStop(string $phoneNumber, ?array $patient): string
{
    if ($patient) {
        $this->consentService->optOut(
            $patient['pid'],
            $phoneNumber,
            'sms_stop'
        );
    }

    return $this->templateService->render('keyword_stop', [
        'clinic_name' => $this->config->getClinicName(),
        'phone' => $this->config->getClinicPhone()
    ]);
}

Phase 2: Appointment Features

2.1 Appointment Reminders (Individual)

Features:

  • Send high-compliance reminder (no appointment details in SMS)
  • Link to patient portal for full details
  • Configurable reminder timing (24h, 2h before, etc.)
  • Track delivery status

Service Implementation:

class AppointmentReminderService
{
    /**
     * Send appointment reminder
     */
    public function sendReminder(int $appointmentId, string $timing = '24h'): void
    {
        $appointment = $this->getAppointment($appointmentId);
        $patient = $this->getPatient($appointment['pc_pid']);

        // Check consent
        if (!$this->consentService->hasConsent($patient['pid'], $patient['phone_cell'])) {
            throw new SinchValidationException("Patient has not opted in to SMS");
        }

        // Render template
        $message = $this->templateService->render('appointment_reminder', [
            'clinic_name' => $this->config->getClinicName(),
            'portal_link' => $this->generatePortalLink($patient['pid']),
            'phone' => $this->config->getClinicPhone(),
            'opt_out' => 'Reply STOP to opt-out'
        ]);

        // Send via Conversations API
        $this->messageService->sendIndividual(
            $patient['pid'],
            $patient['phone_cell'],
            $message,
            ['appointment_id' => $appointmentId]
        );
    }
}

2.2 Telehealth Links (Individual)

Features:

  • Send secure telehealth link before appointment
  • Include appointment time in message
  • Send 15-30 minutes before scheduled time

Template:

{{ clinic_name }}: Your telehealth visit is starting soon (at {{ appt_time }}).
Please log in to your secure video call here: {{ telehealth_link }}.
{{ opt_out }}.

2.3 Missed Appointment Follow-Up (Individual)

Features:

  • Automatically send after no-show
  • Encourage rescheduling
  • Link to scheduling portal

Trigger: When appointment status changes to "no-show" or patient doesn't check in

2.4 Pre-Visit Instructions (Individual)

Features:

  • Send 24-48h before appointment
  • Link to portal for pre-check-in forms
  • Include any special instructions

Phase 3: Portal Notifications

3.1 New Message Notification (Individual)

Trigger: When provider sends secure message to patient via portal

Template:

{{ clinic_name }}: You have a new secure message from your provider.
Please log in to your patient portal to view it: {{ portal_link }}.
Call {{ phone }} if you have trouble.
{{ opt_out }}.

3.2 Test Results Available (Individual)

Trigger: When test results are finalized and made available in portal

Template:

{{ clinic_name }}: Your recent test results are now available to view in your
secure patient portal. Please log in at: {{ portal_link }}.
Call {{ phone }} with any questions.
{{ opt_out }}.

CRITICAL: Never include actual results in SMS. Only notify that results are available.

Phase 4: Billing Features

4.1 Statement Ready (Individual/Batch)

Individual: When specific patient statement is generated
Batch: Monthly statement run for all patients with balances

Template:

{{ clinic_name }}: Your latest account statement is now available.
To view your statement and payment options, please log in to your secure portal:
{{ portal_link }}.
{{ opt_out }}.

4.2 Balance Due Reminder (Individual/Batch)

Individual: Specific patient with overdue balance
Batch: Collections cycle targeting patients with balances

Template:

{{ clinic_name }}: This is a reminder from {{ clinic_name }} that you have a
balance on your account. To view details or make a secure payment, please visit:
{{ portal_link }} or call {{ billing_phone }}.
{{ opt_out }}.

Note: Template does NOT include amount (which would be PHI)

Phase 5: Wellness & Prevention

5.1 Preventive Care Reminder (Individual/Batch)

Individual: Patient is due for annual wellness visit
Batch: Campaign for all patients due for wellness visits

Template:

{{ clinic_name }}: It's time to schedule your annual wellness visit!
Staying up-to-date on preventive care is key to your health.
Call {{ phone }} or book online: {{ scheduling_link }}.
{{ opt_out }}.

5.2 Public Health Announcements (Batch)

Use Cases:

  • Flu shot campaigns
  • COVID vaccine availability
  • Health screenings
  • Wellness programs

Template:

{{ clinic_name }}: Flu season is here! We now have flu shots available.
Protect yourself and your community. Schedule your shot today by calling
{{ phone }} or online: {{ scheduling_link }}.
{{ opt_out }}.

Phase 6: Operational Messages

6.1 Office Closure Alerts (Batch)

Triggers:

  • Emergency closures (weather, power outage)
  • Planned closures (holidays)
  • Service disruptions

Template:

{{ clinic_name }} Alert: Due to {{ reason_for_closure }}, our office will be
closed on {{ date_closed }}. We will contact you to reschedule any appointments.
See details: {{ website_link }}.
{{ opt_out }}.

Target Audience: All active patients, or subset with appointments on closure date

6.2 Post-Visit Survey (Individual)

Trigger: After visit completion (same day or next day)

Template:

{{ clinic_name }}: Thank you for visiting {{ clinic_name }} today. We value your
feedback. Would you take 60 seconds to complete a brief survey about your
experience? {{ survey_link }}.
{{ opt_out }}.

Implementation Priority

Phase 1: Foundation (Weeks 1-2)

  • ✅ Template management system
  • ✅ Consent tracking database
  • ✅ Initial opt-in confirmation message
  • ✅ HELP/STOP keyword handler
  • ✅ Basic message sending (individual)

Phase 2: Appointments (Weeks 3-4)

  • ✅ Appointment reminders (high-compliance version)
  • ✅ Missed appointment follow-up
  • ✅ Telehealth links
  • ✅ Pre-visit instructions

Phase 3: Portal Notifications (Week 5)

  • ✅ New message notifications
  • ✅ Test results available notifications

Phase 4: Batch Communications (Week 6)

  • ✅ Batch sending infrastructure
  • ✅ Office closure alerts
  • ✅ Public health announcements
  • ✅ Wellness campaigns

Phase 5: Billing & Surveys (Week 7)

  • ✅ Statement ready notifications
  • ✅ Balance due reminders
  • ✅ Post-visit surveys

Phase 6: Advanced Features (Week 8+)

  • ✅ Scheduled message sending
  • ✅ Analytics dashboard
  • ✅ A/B testing for templates
  • ✅ Delivery optimization

Message Sending Guidelines

Individual Messages (1-to-1)

// Example: Send appointment reminder
$messageService->sendIndividual(
    patientId: $patientId,
    phoneNumber: $phoneNumber,
    templateKey: 'appointment_reminder',
    variables: [
        'clinic_name' => 'Example Clinic',
        'portal_link' => 'https://example.com/portal',
        'phone' => '650-123-4567',
        'opt_out' => 'Reply STOP to opt-out'
    ],
    metadata: ['appointment_id' => 123]
);

Batch Messages (1-to-many)

// Example: Send office closure alert
$patientIds = $this->getActivePatients();

$messageService->sendBatch(
    patientIds: $patientIds,
    templateKey: 'office_closure',
    variables: [
        'clinic_name' => 'Example Clinic',
        'reason_for_closure' => 'severe weather',
        'date_closed' => 'Saturday, Nov 22, 2025',
        'website_link' => 'https://example.com/closure',
        'opt_out' => 'Reply STOP to opt-out'
    ],
    metadata: ['campaign' => 'weather_closure_2025_11_22']
);

Compliance Checklist

Before sending any messages:

  • Consent verified - Patient has opted in
  • Template approved - Using Sinch-approved template
  • Variables validated - All required variables present
  • Opt-out included - Every message has {{ opt_out }}
  • No PHI in SMS - Only portal links, no sensitive details
  • BAA in place - OpenCoreEMR has BAA with Sinch
  • Proper timing - Messages sent during reasonable hours
  • Rate limits respected - Not exceeding carrier limits

Legal Disclaimer for README

## Legal Requirements & Disclaimers

### Business Associate Agreement (BAA) Required

**IMPORTANT:** This module uses the Sinch Conversations API to send text messages
to patients. **You MUST have a Business Associate Agreement (BAA) with Sinch**
before using this module in production with patient data.

OpenCoreEMR has a BAA with Sinch and has received compliance approval for the
message templates included in this module. **If you are not using this module
through OpenCoreEMR**, you must:

1. **Execute your own BAA with Sinch** before sending any patient messages
2. **Submit your message templates** to Sinch for compliance review
3. **Obtain approval** before using templates in production
4. **Maintain proper consent records** for all patients

### TCPA & HIPAA Compliance

Before sending any SMS messages, you **MUST**:

1. **Obtain Prior Express Written Consent** from patients to receive text messages
2. **Document consent** including:
   - Patient signature or electronic agreement
   - Date of consent
   - Phone number provided
   - Types of messages they agree to receive
3. **Honor opt-out requests immediately** (STOP keywords)
4. **Never include PHI in SMS** - Use portal links instead
5. **Identify yourself** in every message (clinic name)
6. **Include opt-out instructions** in every message

### Standard SMS Disclaimers

The following disclaimers are REQUIRED by carriers (CTIA) and TCPA:

**Initial Opt-In Confirmation:**

{{ clinic_name }}: You have successfully opted-in to receive alerts from
{{ clinic_name }}. Msg&Data rates may apply. Msg freq varies. Reply HELP for help.
Reply STOP to unsubscribe at any time.


**This message MUST be sent immediately after a patient opts in.**

### Message Template Approval

All message templates included in this module have been reviewed and approved
by Sinch for use by OpenCoreEMR under our BAA. These templates are:

- HIPAA-compliant (when used correctly with proper consent)
- TCPA-compliant
- Carrier-compliant (CTIA guidelines)

**If you create custom templates**, you MUST submit them to Sinch for compliance
review before using them in production.

### No Warranty / Use at Your Own Risk

This open-source software is provided "as is" without warranty of any kind.
You are solely responsible for ensuring your use of this module complies with:

- HIPAA Privacy and Security Rules
- TCPA (Telephone Consumer Protection Act)
- State-specific privacy laws
- Carrier regulations (CTIA)

**Consult with legal counsel** before deploying this module in a production
healthcare environment.

### Support

For compliance questions or to establish a BAA with Sinch:
- **OpenCoreEMR Customers:** Contact [email protected]
- **Other Users:** Contact Sinch directly at https://www.sinch.com/contact/

For technical issues with this module:
- GitHub Issues: https://github.com/opencoreemr/oce-module-sinch-conversations/issues

Summary

This implementation plan provides:

  1. 12 Sinch-approved templates with clear categorization
  2. Individual vs Batch communication types defined
  3. HELP/STOP keyword handling for compliance
  4. Consent management system
  5. Template variable system for personalization
  6. Phased implementation over 8 weeks
  7. Legal disclaimers for README
  8. Compliance checklist for every message

Next Steps:

  1. Implement template management database and service
  2. Build consent tracking system
  3. Implement keyword handler for HELP/STOP
  4. Create message sending service (individual + batch)
  5. Integrate with OpenEMR appointment system
  6. Update README with legal disclaimers

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions