Build an Automated Appointment Reminder Service with Go and Twilio

March 04, 2025
Written by
Popoola Temitope
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

Build an Automated Appointment Reminder Service with Go and Twilio

Building an automated appointment reminder system using Go and Twilio can streamline communication by sending reminders to clients via SMS. This helps reduce no-shows and improves the overall efficiency of appointment-based businesses.

In this tutorial, you will learn how to build an automated appointment reminder system in Go using the Twilio Programmable Messaging API.

Prerequisites

To follow along with this tutorial, ensure you have the following requirements.

  • Go 1.22 or above installed on your computer
  • A Twilio account (free or paid) with an active phone number. If you don't have an account, sign up today.
  • Access to a MySQL database

Initialize a new Go project

To get started, open your terminal and navigate to the directory where you want to create the new Go project. Then, run the command below to initialize the project.

mkdir appointment-reminder
cd appointment-reminder
go mod init appointment-reminder

After running the commands above, open the project in your preferred IDE or code editor, such as VS Code.

Install the required dependencies

Let’s first install the Twilio Go package to interact with the Twilio Programmable Messaging API. Run the command below to install the package.

go get github.com/twilio/twilio-go

In our project, we will also need to install gorilla/mux to handle the application's user interface, the Go MySQL driver to store patient appointment records, cron to send reminder SMS to patients with appointments scheduled for the next day, and GoDotEnv to manage environment variables.

To install these packages, run the command below.

go get github.com/gorilla/mux github.com/go-sql-driver/mysql github.com/robfig/cron/v3@v3.0.0 github.com/joho/godotenv

Create the dotenv file

In the project’s root folder, create a file named .env and add the following environment variables to it.

TWILIO_ACCOUNT_SID=<account_SID>
TWILIO_AUTH_TOKEN=<auth_token>
TWILIO_PHONE_NUMBER=<phone_number>

From the code above, replace the <account_SID>, <auth_token> and <phone_number> placeholders with your corresponding Twilio values.

Provision the database

Now, login to your MySQL server and create a new database named reminder. After creating the database, create a table named "appointments" with the following fields:

  • id:int(11) Primary Key Auto Increment
  • patient_name:varchar(99)
  • doctor_name:varchar(99)
  • patient_phone varchar(99)
  • appointment_date:date
Screenshot of phpMyAdmin showing the structure of the appointments table.

Retrieve your Twilio credentials

To connect your Go application to your Twilio account, log in to your Twilio dashboard to retrieve your credentials which will be used to authenticate the Twilio API calls. You'll find your Account SID and Auth Token under the Account Info section, as shown in the screenshot below.

Screenshot of Twilio account info showing Account SID, Auth Token, and Twilio phone number sections.

Create the application logic

Now, let’s create the application's core logic. To do this, create a file named main.go in your project folder.

package main

import (
	"database/sql"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os"

	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"github.com/joho/godotenv"
	"github.com/robfig/cron/v3"
	"github.com/twilio/twilio-go"
	openapi "github.com/twilio/twilio-go/rest/api/v2010"
)

type Appointment struct {
	PatientName     string
	DoctorName      string
	PatientPhone    string
	AppointmentDate string
}

var (
	templates = template.Must(template.ParseFiles("appointment.html"))
	db        *sql.DB
)

func InitializeTwilioClient() *twilio.RestClient {
	accountSID := os.Getenv("TWILIO_ACCOUNT_SID")
	authToken := os.Getenv("TWILIO_AUTH_TOKEN")
	client := twilio.NewRestClientWithParams(twilio.ClientParams{
		Username: accountSID,
		Password: authToken,
	})
	log.Printf("Initialized Twilio client with Account SID: %s", accountSID)
	return client
}

func initializeDatabase() error {
	var err error
	dsn := "<db_username>:<db_password>@tcp(127.0.0.1:3306)/appointments"
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return err
	}
	err = db.Ping()
	if err != nil {
		return err
	}
	log.Println("Connected to the MySQL database successfully!")
	return nil
}

From the code above, we:

  • Import all the necessary packages and define the path to the application's booking HTML template
  • Initialize the Twilio client, which connects to the Twilio endpoint using accountSID and authToken
  • Use the initializeDatabase() function to establish a connection to the database server
Inside the initializeDatabase() function above, replace the <db_username> and <db_password> placeholders with your corresponding MySQL database details.

Next, let’s add the application functions to the main.go file. To do this, add the following code at the bottom of the main.go file.

func SendSMSReminder(client *twilio.RestClient, to, body string) error {
	params := &openapi.CreateMessageParams{}
	params.SetTo(to)
	params.SetFrom(os.Getenv("TWILIO_PHONE_NUMBER"))
	params.SetBody(body)
	resp, err := client.Api.CreateMessage(params)
	if err != nil {
		log.Printf("Error occurred while sending message: %v", err)
		return err
	}
	log.Printf("Reminder sent to %s: SID: %s", to, *resp.Sid)
	return nil
}

func ServeAppointmentForm(w http.ResponseWriter, r *http.Request) {
	message := r.URL.Query().Get("message")
	data := struct {
		Message string
	}{
		Message: message,
	}
	err := templates.ExecuteTemplate(w, "appointment.html", data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func HandleAppointmentSubmission(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, "Error parsing form", http.StatusInternalServerError)
		return
	}
	appointment := Appointment{
		PatientName:     r.FormValue("patientName"),
		DoctorName:      r.FormValue("doctorName"),
		PatientPhone:    r.FormValue("patientPhone"),
		AppointmentDate: r.FormValue("appointmentDate"),
	}
	err = storeAppointment(appointment)
	if err != nil {
		log.Printf("Failed to store appointment: %v", err)
		http.Error(w, "Failed to save appointment", http.StatusInternalServerError)
		return
	}
	message := fmt.Sprintf("Hello %s, your appointment with Dr. %s is scheduled for %s.", appointment.PatientName, appointment.DoctorName, appointment.AppointmentDate)
	client := InitializeTwilioClient()
	err = SendSMSReminder(client, appointment.PatientPhone, message)
	if err != nil {
		log.Printf("Failed to send SMS: %v", err)
		http.Error(w, "Failed to send reminder", http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, fmt.Sprintf("/?message=%s", url.QueryEscape("Appointment  successfully booked! A confirmation SMS has been sent.")), http.StatusSeeOther)
}

func storeAppointment(appointment Appointment) error {
	query := `INSERT INTO appointments (patient_name, doctor_name, patient_phone, appointment_date) VALUES (?, ?, ?, ?)`
	_, err := db.Exec(query, appointment.PatientName, appointment.DoctorName, appointment.PatientPhone, appointment.AppointmentDate)
	return err
}

func getNextDayAppointments() ([]Appointment, error) {
	var appointments []Appointment
	query := `SELECT patient_name, doctor_name, patient_phone, appointment_date FROM appointments WHERE appointment_date = DATE_ADD(CURDATE(), INTERVAL 1 DAY)`
	rows, err := db.Query(query)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		var appointment Appointment
		if err := rows.Scan(&appointment.PatientName, &appointment.DoctorName, &appointment.PatientPhone, &appointment.AppointmentDate); err != nil {
			return nil, err
		}
		appointments = append(appointments, appointment)
	}
	return appointments, nil
}

func SendReminders() {
	appointments, err := getNextDayAppointments()
	if err != nil {
		log.Printf("Failed to fetch appointments: %v", err)
		return
	}
	client := InitializeTwilioClient()
	for _, appointment := range appointments {
		message := fmt.Sprintf("Hello %s, this is a reminder for your appointment with Dr. %s tomorrow on %s.",
			appointment.PatientName, appointment.DoctorName, appointment.AppointmentDate)
		err = SendSMSReminder(client, appointment.PatientPhone, message)
		if err != nil {
			log.Printf("Failed to send reminder to %s: %v", appointment.PatientPhone, err)
		} else {
			log.Printf("Reminder sent to %s for appointment on %s", appointment.PatientPhone, appointment.AppointmentDate)
		}
	}
}

func startCronJob() {
	c := cron.New()
	cron_job_time := "0 15 * * *"
	_, err := c.AddFunc(cron_job_time, func() {
		log.Println("Sending daily appointment reminders...")
		SendReminders()
	})
	if err != nil {
		log.Fatalf("Failed to schedule cron job: %v", err)
	}
	c.Start()
	select {}
}

From the code above:

  • The ServeAppointmentForm() function is responsible for displaying the HTML form used to book an appointment
  • The HandleAppointmentSubmission() function processes the form submission and stores the appointment in the database
  • The getNextDayAppointments() function retrieves appointments scheduled for the following day
  • Finally, the SendReminders() function sends reminder SMS messages to patients with appointments scheduled for the next day

Create the application routes

Now, let's define the application routes and set up the cron job that will send reminder SMS to users one day before their appointment. To do that, in the main.go file, add the following code.

func main() {
	err := godotenv.Load()
	if err != nil {
		log.Fatal("Error loading .env file")
	}
	err = initializeDatabase()
	if err != nil {
		log.Fatalf("Could not connect to the database: %v", err)
	}
	defer db.Close()

	go startCronJob()

	r := mux.NewRouter()
	r.HandleFunc("/", ServeAppointmentForm).Methods("GET")
	r.HandleFunc("/submit", HandleAppointmentSubmission).Methods("POST")
	http.Handle("/", r)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Create the application user interface

Let’s create a user interface where patients can fill in their appointment details. To do this, create an HTML file named appointment.html and add the following code to it.

<!DOCTYPE html>
<html>
<head>
    <title>Book Appointment</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f9;
            margin: 40px;
            color: #333;
        }
        h1 {
            color: #5a5a5a;
        }
        form {
            background-color: #ffffff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            max-width: 500px;
            margin: auto;
        }
        label {
            margin-bottom: 10px;
            display: block;
            font-weight: bold;
        }
        input[type="text"],
        input[type="tel"],
        input[type="date"],
        input[type="submit"] {
            width: calc(100% - 22px);
            padding: 10px;
            margin-top: 5px;
            margin-bottom: 20px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        input[type="submit"] {
            background-color: #4CAF50;
            color: white;
            border: none;
            cursor: pointer;
        }
        input[type="submit"]:hover {
            background-color: #45a049;
        }
        .container {
            padding: 20px;
        }
        h1 {
            text-align: center
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Book an Appointment</h1>
        <form action="/submit" method="post">
            {{if .Message}}
            <div style="color: green;">{{.Message}}</div> 
            {{else}}
            <label for="patientName">Patient Name:</label>
            <input type="text" id="patientName" name="patientName" required><br>
            <label for="doctorName">Doctor Name:</label>
            <input type="text" id="doctorName" name="doctorName" required><br>
            <label for="patientPhone">Patient Phone:</label>
            <input type="text" id="patientPhone" name="patientPhone" required ><br>
            <label for="appointmentDate">Appointment Date:</label>
            <input type="date" id="appointmentDate" name="appointmentDate" required><br>
            <input type="submit" value="Book Appointment">
            {{end}}
        </form>
    </div>
</body>
</html>

Test the application

To test the application, start the application development server by running the command below.

go run main.go

After starting the application, open http://localhost:8080 in your browser of choice and book an appointment with a doctor.

Screenshot of an online form for booking an appointment with fields for patient name, doctor name, phone, and date.

Once your appointment is successfully booked, you will receive a confirmation SMS, as shown in the image below.

SMS notification on a mobile screen reminding about an appointment with Dr. Matt scheduled for 2024-09-17.

One day before your appointment with the doctor, you will receive a reminder SMS message, as shown in the screenshot below.

Two SMS reminders from Twilio about an appointment with Dr. Matt on 2024-09-17.

That is how to build automated appointment reminders with Go and Twilio

In this article, we've built a Go application that automatically sends appointment reminder SMS messages using Twilio. We used cron jobs to schedule reminders and connected to a MySQL database to store and retrieve appointment information.

Popoola Temitope is a mobile developer and a technical writer who loves writing about frontend technologies. He can be reached on LinkedIn.

Schedule icon in the post's main image was created by Freepik on Flaticon.