Automated Appointment Reminders with Twilio SendGrid and Java

November 19, 2024
Written by
Nyikwagh Moses
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

Automated Appointment Reminders with Twilio SendGrid and Java

In this tutorial, you'll develop an appointment scheduling application in Java. What's special about that is that you'll leverage Twilio SendGrid's email sending capabilities to deliver customizable and dynamic appointment reminder emails to users, ensuring they don't miss their appointments.

When you are done with this tutorial, you will be able to create a Java application that can interact with the SendGrid API, regardless of the build tool you use to create it.

Prerequisites

To follow along with this tutorial, you will need the following:

Create the Java application

Using IntelliJ IDEA, create a new Java project. Give it a name; you can call it whatever you want, but I recommend calling it "Scheduler". Then, choose "IntelliJ" for the build system. I will be using JDK 17, and advise you to do so as well. That way, you'll get the exact same results as me. But, I am sure newer versions will work just fine too.

Then, leave all the other settings as shown in the screenshot below, and click Create.

Create an environment file

The next thing you need to do is to initialize a dotenv file to store the credentials the application needs, keeping them outside of the code.

To do that, create a file named . env in the root of your project. Then, inside .env paste the code below.

API_KEY=<<YOUR_SENDGRID_API_KEY>>
IPINFO_API_TOKEN=<<YOUR-IPINFO-API-TOKEN>>

The first one, API_KEY, will store your SendGrid API key, allowing the application to make authenticated requests against SendGrid's API. The second is your IPinfo API token, allowing the application to do the same with IPinfo's API.

Add the required dependencies

Next you need to add three external packages which the code will make use of. The first is dotenv-java, which loads the variables defined in the .env file as environment variables within the application. The second is the official SendGrid Java API library, so that you can easily interact with SendGrid's API. The third is google/gson, a serialization/deserialization library for converting Java Objects to and from JSON.

A screenshot of a Java project in IntelliJ IDEA, showing the project structure and code editor.

To add them, in Intellij IDEA, open the Project tool window ( CMD + 1 on macOS or Ctrl + 1 on Linux or Microsoft Windows). Then, under External Libraries right-click on the JDK and click Open Library Settings, which opens the Project Structure dialog.

Interface for adding a new global library in project structure settings

From there, in the left-hand navigation column, under Platform Settings, click Global Libraries. Then, click on the + icon at the top of the middle column (alternatively, click CMD + N on macOS, or Ctrl + N on Linux or Microsoft Windows) to add a new global library. In the popup that appears, click From Maven….

Dialog box for downloading libraries from Maven Repository with options for transitive dependencies and sources.

Now, in the search bar of the "Download Library from Maven Repository" window, first, search for "dotenv". You will see quite a long list of search results. The one you need is io.github.cdimascio.dotenv-java:3.0.0. Click it and click OK in the Choose Modules window.

Next, search for "SendGrid". From the search results, choose com.sendgrid:sendgrid-java:4.10.1. After that, click OK in the Choose Modules window.

Then, search for "google.code.gson". From the search results, choose com.google.code.gson:gson:2.11.0. After that, click OK in the Choose Modules window.

With the dependencies installed, click OK in the Project Structure window and you're done.

Create a custom Java package

In the src directory, create a package and call it mosnyik. This is where you will add all your classes.

The first class you'll create is the SendMail class. This is the class that will do the work of sending out dynamic emails depending on trigger events like user registration, logins, or when appointments are due.

Create a new Java class in src/mosnyik called SendMail. Then, update the file to match the following code.

package mosnyik;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.sendgrid.Method;
import com.sendgrid.Request;
import com.sendgrid.Response;
import com.sendgrid.SendGrid;
import com.sendgrid.helpers.mail.Mail;
import com.sendgrid.helpers.mail.objects.Content;
import com.sendgrid.helpers.mail.objects.Email;
import io.github.cdimascio.dotenv.Dotenv;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class SendMail {
    private final Dotenv dotenv = Dotenv.configure().load();
    private final String apiKey = dotenv.get("API_KEY");
    private final String ipinfoApiToken = dotenv.get("IPINFO_API_TOKEN");
    private final LocalDateTime currentDateTime = LocalDateTime.now();
    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy, HH:mm:ss");
    private final String dateAndTime = currentDateTime.format(formatter);
    private final String osName = System.getProperty("os.name");
    private final String osVersion = System.getProperty("os.version");
    private  String ip = "";
    private  String city = "";
    private  String country = "";
    private final String masterEmail = "<ENTER-YOUR-SENDER-EMAIL-HERE>";

    public void getUserLocation() {
        try {
            URL url = new URL("https://ipinfo.io/json?token=" + ipinfoApiToken);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(connection.getInputStream())
            );
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            Gson gson = new Gson();
            JsonObject locationData = gson.fromJson(response.toString(), JsonObject.class);
            ip = locationData.get("ip").getAsString();
            city = locationData.get("city").getAsString();
            country = locationData.get("country").getAsString();
            connection.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void sendRegMail(String userName, String email) {
        Email from = new Email(masterEmail);
        String subject = "Welcome to Scheduler!";
        Email to = new Email(email);
        Content content = new Content("text/plain", "Dear " + userName + ","+" \n" +
                "\n" +
                "Welcome to Scheduler! We are thrilled to have you join our community. Thank you for choosing our platform for scheduling your appointments conveniently.\n"
                 +
                "At Scheduler, we strive to provide you with a seamless and hassle-free experience for managing your appointments. Whether you're booking appointments for medical consultations, salon services, or any other services, we've got you covered.\n" +
                "\n"+
                "  Here's a quick overview of what you can do with our app:\n" +
                "1. Schedule Appointments: Easily book appointments with your preferred service providers at your convenience.\n" +
                "2. Manage Your Calendar: Keep track of all your upcoming appointments and receive reminders so you never miss an appointment.\n" +
                "3. View Service Providers: Explore a variety of service providers available on our platform and choose the one that best fits your needs. \n" +
                "4. Rate and Review: Share your feedback by rating and reviewing service providers to help other users make informed decisions.\n" +
                "\n"+
                "To get started, simply log in to your account using the email and password you provided during registration. If you have any questions or need assistance, our support team is here to help. Feel free to reply to this mail. \n" +
                "Once again, welcome aboard! We're excited to help you manage your appointments effortlessly.\n" +
                "\n"+
                "Best regards,\n" +
                "Nyikwagh Moses\n" +
                "Scheduler");
        Mail mail = new Mail(from, subject, to, content);
        SendGrid sg = new SendGrid(apiKey);
        Request request = new Request();
        try {
            request.setMethod(Method.POST);
            request.setEndpoint("mail/send");
            request.setBody(mail.build());
            Response response = sg.api(request);
            System.out.println(response.getStatusCode());
            System.out.println(response.getBody());
            System.out.println(response.getHeaders());
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public void sendLoginMail(String userName, String email) {
        getUserLocation();
        Email from = new Email(masterEmail);
        String subject = "Schedule - Login Notification";
        Email to = new Email(email);
        Content content = new Content("text/plain", "Dear " + userName + ","+" \n" +
                "\n" +
                "We hope this email finds you well. We wanted to inform you that your Scheduler account was recently accessed.\n"
                +
                "\n" +
                "Below are the details of the login:\n" +
                "Date and Time: " + dateAndTime + "\n" +
                "Device: " + osName + " " + osVersion + "\n" +
                "Location: "+ city + " " + country +
                "\n" +
                "IP Address: "+ ip +
                "\n" +
                "\n" +
                "If this login was made by you, you can disregard this email. \n" +
                "However, if you did not authorize this login or suspect any unauthorized activity on your account, please take immediate action by changing your password and reviewing your account settings.\n"+
                "Thank you for choosing Scheduler for managing your appointments. We appreciate your trust in us.\n" +
                "\n" +
                "Best regards,\n" +
                "Nyikwagh Moses\n" +
                "Scheduler");
        Mail mail = new Mail(from, subject, to, content);
        SendGrid sg = new SendGrid(apiKey);
        Request request = new Request();
        try {
            request.setMethod(Method.POST);
            request.setEndpoint("mail/send");
            request.setBody(mail.build());
            Response response = sg.api(request);
            System.out.println(response.getStatusCode());
            System.out.println(response.getBody());
            System.out.println(response.getHeaders());
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public void sendBookingMail( String email, String appointmentDate, String appointmentTime, RegisterUser reg) {
        Email from = new Email(masterEmail);
        String subject = "You have booked an appointment";
        Email to = new Email(email);
        Content content = new Content("text/plain", "Welcome Back!! \n" +
            "Thank you for booking an appointment with us. We are excited to assist you with your " + reg.userAppointment + " appointment " + " scheduled for " + appointmentDate + " at " + appointmentTime+". Below are the details of your appointment:\n" +
            "\n" +
            "Appointment Details:\n" +
            "Date:  " + appointmentDate + "\n" +
            "Time: " + appointmentTime + "\n" +
            "Location: "+ city + "," + country +"\n" +
            "\n" +
            "If you need to make any changes to your appointment, please let us know as soon as possible. You can reply to this email.\n" +
            "\n" +
            "We look forward to meeting with you and providing our services. If you have any questions or concerns, feel free to reach out to us.\n" +
            "\n" +
            "Best regards,\n" +
            "Scheduler");
        Mail mail = new Mail(from, subject, to, content);
        SendGrid sg = new SendGrid(apiKey);
        Request request = new Request();
        try {
            request.setMethod(Method.POST);
            request.setEndpoint("mail/send");
            request.setBody(mail.build());
            Response response = sg.api(request);
            System.out.println(response.getStatusCode());
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public void sendBookingReminderMail( String email, String appointmentDate, String appointmentTime, RegisterUser reg) {
        Email from = new Email(masterEmail);
        String subject = "You have booked an appointment";
        Email to = new Email(email);
        Content content = new Content("text/plain", "Welcome Back!! \n" +
            "This is a reminder that you have " + reg.userAppointment + " appointment " + " scheduled for " + appointmentDate + " at " + appointmentTime+". Below are the details of your appointment:\n" +
            "\n" +
            "Appointment Details:\n" +
            "Date:  " + appointmentDate + "\n" +
            "Time: " + appointmentTime + "\n" +
            "Location: "+ city + "," + country +"\n" +
            "\n" +
            "If you need to make any changes to your appointment, please let us know as soon as possible. You can reply to this email.\n" +
            "\n" +
            "We look forward to meeting with you and providing our services. If you have any questions or concerns, feel free to reach out to us.\n" +
            "\n" +
            "Best regards,\n" +
            "Scheduler");
        Mail mail = new Mail(from, subject, to, content);
        SendGrid sg = new SendGrid(apiKey);
        Request request = new Request();
        try {
            request.setMethod(Method.POST);
            request.setEndpoint("mail/send");
            request.setBody(mail.build());
            Response response = sg.api(request);
            System.out.println(response.getStatusCode());
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

With that done, replace <ENTER-YOUR-SENDER-EMAIL-HERE> with the authenticated email from the Single Sender Verification email addressyou set up earlier.

Now, you have created a class to send mail to users when they register, when they log in, when they book an appointment, and when they need a reminder.

Next, you need to create the RegisterUser class. To do that, in src/mosnyik create a Java class and name it RegisterUser. Then, copy and paste the code below into the class.

package mosnyik;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Scanner;
import java.text.SimpleDateFormat;

public class RegisterUser {
    SendMail sendMail = new mosnyik.SendMail();
    final Scanner scanner = new Scanner(System.in);
    public ArrayList<String> email = new ArrayList<>();
    public ArrayList<String> password = new ArrayList<>();
    ArrayList<String> userName = new ArrayList<>();
    ArrayList<String> confirmPassword = new ArrayList<>();
    ArrayList<String> appointmentTitle = new ArrayList<>();
    ArrayList<String> appointmentContent = new ArrayList<>();
    ArrayList<String> appointmentDate = new ArrayList<>();
    ArrayList<String> appointmentTime = new ArrayList<>();
    String formattedDate = "";
    String formattedTime = "";
    String userEmail = "";
    String userAppointment = "";

    public void addUser() throws Exception {
        System.out.println("Please enter your userName");
        String userName = scanner.next();
        System.out.println("Please enter your email");
        String email = scanner.next();
        while (true) {
            System.out.println("Please enter your password");
            String password = scanner.next();
            System.out.println("Please confirm your password");
            String confirmPassword = scanner.next();
            if (password.equals(confirmPassword)) {
                this.userName.add(String.valueOf(userName));
                this.email.add(email);
                this.password.add(password);
                System.out.println("You have registered as " + userName);
                System.out.println("When you want to login, use " + email + " " + "and your password");
                sendMail.sendRegMail(userName, email);
                this.login();
                break;
            }
            System.out.println("Your password does not match!!");
        }
    }
}

Great, now you can now register a user and send them a welcome mail. So, up next, we'll work on the login() method. Add the code in the code block below after the addUser() user method.

public void login() throws Exception {
    if (System.getProperty("os.name").contains("windows")) {
        new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor();
    } else {
        System.out.println("\033[H\033[2J");
        System.out.flush();
    }
    System.out.println("\t\t");
    System.out.println("\t\t ____________________  ");
    System.out.println("\t\t |                                         |");
    System.out.println("\t\t |    Welcome to Login        | ");
    System.out.println("\t\t |____________________ |");
    System.out.println(" ");
    System.out.println("Enter your email: ");
    String email = scanner.next();
    System.out.println("Enter your password: ");
    String password = scanner.next();
    boolean flag = false;
    while (true) {
        for (int i = 0; i < this.email.size(); i++) {
            if (this.email.get(i).equals(email) && this.password.get(i).equals(password)) {
                flag = true;
                System.out.println(this.userName.get(i));
                userEmail = this.email.get(i);
                sendMail.sendLoginMail(this.userName.get(i), email);
                if (!this.appointmentDate.isEmpty()) {
                    userAppointment = this.appointmentContent.get(i);
                    System.out.println("You can check for your appointments");
                } else {
                    System.out.println("No appointments yet");
                }
                break;
            }
        }
        if (flag) {
            scheduleAppointment();
        } else {
            throw new RuntimeException("Invalid email or password. Please try again");
        }
    }
}

The login() method requests the user to provide their registered email address and password. Once provided, we enter into a while loop to check the stored registered users against the email and password pair. If there is a match, the user gets redirected to the "Schedule Appointment" page, otherwise they will see the message “Invalid email or password. Please try again”.

With that done, your user can now login and be redirected to the "Schedule Appointment" page. That is the next method you will create in src/mosnyik/RegisterUser.java, by pasting the function below at the end of the class.

public void scheduleAppointment() throws Exception {
    if (System.getProperty("os.name").contains("windows")) {
        new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor();
    } else {
        System.out.println("\033[H\033[2J");
        System.out.flush();
    }
    System.out.println("\t\t");
    System.out.println("\t\t ________________________________ ");
    System.out.println("\t\t |                                                                 |");
    System.out.println("\t\t |     SCHEDULE AN APPOINTMENT        | ");
    System.out.println("\t\t |________________________________ |");
    System.out.println(" ");
    System.out.println("1. Schedule New Appointment \t 2. See Appointment List \t 3. Exit\n");
    Byte selection = scanner.nextByte();
    switch (selection) {
        case 1 -> {
            System.out.println("You can schedule Appointment now");
            System.out.println("Enter your appointment title : ");
            scanner.nextLine();
            String title = scanner.nextLine();
            System.out.println("Enter your appointment description: ");
            String content = scanner.nextLine();
 while (true) {
                System.out.print("Enter appointment date (dd-MM-yyyy): ");
                String dateInput = scanner.nextLine();
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
                try {
                    LocalDate date = LocalDate.parse(dateInput, formatter);
                    formattedDate = date.format(formatter);
                    break;
                } catch (Exception e) {
                    System.out.println("Invalid date format. Please enter the date in dd-MM-yyyy format.");
                }
            }
            while (true) {
                System.out.print("Enter a time (eg 01:30 PM): ");
                String timeInput = scanner.nextLine().trim();
                DateTimeFormatter formatTime = DateTimeFormatter.ofPattern("hh:mm a", Locale.US);
                try {
                    new SimpleDateFormat("hh.mm aa", Locale.US);
                    LocalTime time = LocalTime.parse(timeInput, formatTime);
                    formattedTime = time.format(formatTime);
                    break;
                } catch (Exception e) {
                    System.out.println("The appointment time " + timeInput + "is wrong format" + e.getLocalizedMessage());
                    System.out.println("Invalid time format. Please enter the time in hh:mm:a format.");
                }
            }
            this.appointmentTitle.add(title);
            this.appointmentContent.add(content);
            this.appointmentDate.add(this.formattedDate);
            this.appointmentTime.add(this.formattedTime);
            userAppointment = content;
            sendMail.sendBookingMail(
                this.userEmail,this.formattedDate,this.formattedTime,this
            )
        }
        case 2 -> {
            listAppointments();
        }
        default -> System.exit(0);
    }
}

With the above code, you can now add a new appointment, and receive a confirmation email sent to the user by email.

Next, you will be building the listAppointments() method. This code block should be added after the scheduleAppointment() methodinside the RegisterUser class.

public void listAppointments() throws Exception {
    System.out.println("List of appointments ");
    for (int i = 0; i < this.appointmentTitle.size(); i++) {
        System.out.println(i + 1 + " " + this.appointmentTitle.get(i) + "-" + this.appointmentDate.get(i) + " " + this.appointmentTime.get(i));	
    }
}

Next, you need to create a ReminderService class that checks for reminders on a periodic basis. So, in src/mosnyik, create a new class named ReminderService. Then, update the class' code to match the following.

package mosnyik;

import java.time.format.DateTimeFormatter;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.time.LocalDateTime;
import java.util.Locale;
import java.time.format.DateTimeParseException;

public class ReminderService {
    SendMail sendEmail = new SendMail();
    private final ScheduledExecutorService scheduler;

    public ReminderService() {
        this.scheduler = Executors.newScheduledThreadPool(1);
    }

    public void startReminderService(RegisterUser reg) {
        scheduler.scheduleAtFixedRate(() -> checkForReminders(reg), 0, 1, TimeUnit.MINUTES);
    }

    private void checkForReminders(RegisterUser reg)  {
        if (!reg.appointmentDate.isEmpty()) {
            for (int i = 0; i < reg.appointmentDate.size(); i++) {
                String appointmentDateTimeString = reg.appointmentDate.get(i) + " " + reg.appointmentTime.get(i);
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm a", Locale.US);
                try {
                    LocalDateTime date = LocalDateTime.parse(appointmentDateTimeString, formatter);
                    LocalDateTime currentDate = LocalDateTime.now();
                    if ( date.isAfter(currentDate)) {
                        System.out.println("You should get ready for your upcoming " + " " + reg.appointmentTitle.get(i) + " appointment");
                    } else if (date.isBefore(currentDate)) {
                        System.out.println("You have missed your" + " " + reg.appointmentTitle.get(i) + " appointment" + " on " + reg.appointmentDate.get(i) + " " + reg.appointmentTime.get(i));
                    } else if (date.equals(currentDate)) {
                        System.out.println("You should be at your appointment already");	sendEmail.sendBookingReminderMail(reg.email.get(i), reg.appointmentDate.get(i), reg.appointmentTime.get(i), reg);
                    }
                } catch (DateTimeParseException e) {
                    System.err.println("Invalid date and time format: " + e.getMessage());
                }
            }
        }
    }
}

At this point, you have built almost the entire app, but in bits and pieces. So let's now tie those pieces together. In your Main class, add the following code.

import mosnyik.RegisterUser;
import mosnyik.ReminderService;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        ReminderService reminderService = new ReminderService();
        RegisterUser reg = new RegisterUser();
        final Scanner scanner = new Scanner(System.in);
        reminderService.startReminderService(reg);
        boolean flag = true;
        while (flag) {
            if (System.getProperty("os.name").contains("Windows")) {
                new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor();
            } else {
                System.out.print("\033[H\033[2J");
                System.out.flush();
            }
            System.out.println("\t\t\t\t---------------------------------------");
            System.out.println("\t\t\t\t            | SCHEDULE APP  |          ");
            System.out.println("\t\t\t\t---------------------------------------");
            System.out.println("1. Register \t 2. Login \t 3. Exit \n ");
            byte selection = scanner.nextByte();
            switch (selection) {
                case 1 -> {
                    flag = false;
                    System.out.println("You want Register");
                    reg.addUser();
                }
                case 2 -> {
                    flag = false;
                    System.out.println("You want Login");
                    reg.login();
                }
                case 3 -> {
                    flag = false;
                }
                default -> System.exit(0);
            }
        }
    }
}
Screenshot of settings page for sender verification and link branding with options to verify a single sender and brand links.

To send emails using the SendGrid API, you need to authenticate your sender email address. There are two ways to authenticate your mail: Domain Authentication and Single Sender Verification. We'll be using the latter in this tutorial.

To set it up for your account, login to your SendGrid dashboard, then open Sender Authentication. There, under Settings, click on the Single Sender Verification button. It will open a form for you to fill in the appropriate data for verifying your email address. When you are done, click on the create button.

You will get an email from SendGrid to verify the email address. Once you verify your email address, you are done. You can now send mails from that email address. You can read more in the Sender Authentication documentation.

Retrieve the required API keys

Generated a SendGrid API key

The first thing you need to do is create a SendGrid API key, so that the application can securely interact with SendGrid's API. To do that, in your SendGrid dashboard, click on Settings, then click on API keys.

There, click on Create API Key. Then, enter the project name at the top where you see the label name, e.g., "Tutorial Key", as seen in the image below. After that, click on Restricted Access, and then go down to Send Mail.

click on Restricted Access

Scroll down to Mail Send and click it, then check the Mail Send option.

click on 'Mail Send' and then toggle 'send mail' on

Now, go to the bottom of the screen and click on Create & View to generate the API key. Now, copy and paste it into .env in place of <<YOUR_SENDGRID_API_KEY>>.

Retrieve your IPinfo API key

Screenshot of IPinfo dashboard showing an access token and an option to copy it to the clipboard.

The app needs access to a geolocation API, so head over to ipinfo.io and login to your account. On the dashboard, click the Token tab, then copy your Access Token and paste it into .env in place of <YOUR-IPINFO-API-TOKEN>.

Test that the app works

Now, you can test out your application and register a user. Run the application and select "1" to register your user.

Command line interface showing menu options to register, login, or exit a scheduling application.

Enter the required details to create a user. When you have created a user successfully, you will be redirected to login with the details you just used to create the user in the screen like you see below.

ASCII art of a login prompt with text Welcome to Login and a prompt to enter your email.

When you enter the login details, you will be redirected to schedule an appointment as seen below.

Terminal window showing an error message about an invalid or expired authorization grant during scheduling.

From here you can schedule appointments. When you select "1" to schedule an appointment, you get the prompt to enter the appointment category and then the appointment name, once you are done, it returns you to this screen, and you can now check the list of appointments you have by selecting "2", check image below.

Screenshot showing options to schedule or view appointments with a list example.

This is the GitHub link of the complete app, should you want to compare your code and straighten any errors you may have along the way.

That's how to automate appointment reminders with Twilio SendGrid and Java

Twilio gives an easy to use way to interage mailing to your application ranging up to dynamic mailing for your marketing. It is even better now with the verification. The steps and very easy and the integration, very light weight.

Nyikwagh Moses is a Blockchain Full Stack Developer in Abuja, Nigeria. He is very enthusiastic about anything technology, especially Blockchain Technology. He loves to make like minded friends and would love to connect, you can find him on LinkedIn and X (formerly known as Twitter), if you have a question or you also want to connect.

Java logo in the tutorial's main image was created by Mark Anderson (work for hire for Sun Microsystems) - http://www.logoeps.com/java-eps-vector-logo/40925/, Fair use, https://en.wikipedia.org/w/index.php?curid=51298819 and the appointment icon was created by Vectors Tank on Flaticon.