Build an Emergency Notification System with Flask, HTMX, and Twilio Programmable Voice

March 10, 2025
Written by
Oyedele Tioluwani
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

Build an Emergency Notification System with Flask, HTMX, and Twilio Programmable Voice

Introduction

An effective emergency notification system can be important in maintaining your safety and well-being in various circumstances.

This article will walk you through the process of creating an emergency notification system with Flask, HTMX, and Twilio Programmable Voice. Combining these technologies enables you to construct a responsive and interactive system for providing emergency notifications via voice calls.

An Emergency Notification System is developed for this project to distribute important information during an emergency promptly. The solution will allow you to input emergency messages using a web form, which will subsequently be routed as phone calls to predetermined recipient numbers. You'll use Flask, a lightweight and adaptable Python web framework, together with HTMX for smooth client-server connection, and Twilio Programmable Voice for voice call integration.

Prerequisites

  • A Twilio Account. If you do not have a Twilio account you can create a free trial account here.
  • A code editor such as Pycharm or Visual Studio Code
  • A verified Twilio Number
  • Basic Python Knowledge

Understand the Components

Flask

Flask is a Python web framework specifically designed for web application development. It is well-known for its simplicity and flexibility, making it ideal for projects of all kinds, from small scripts to huge web apps.

Role in the project

Flask serves as the backend of our application, handling HTTP requests and responses. It's also responsible for rendering the user interface (UI) and processing form submissions to send notifications.

HTMX

HTMX (HTML Extended with AJAX and Multimedia) is a modern tool that allows you to access AJAX, CSS Transitions, WebSockets, and Server-Sent Events directly in HTML using attributes. Thus, you can build modern user interfaces with the simplicity and power of hypertext.

Role in the Project

HTMX improves the user experience by handling form submissions asynchronously, avoiding a full-page reload. This is accomplished using the hx-post and hx-target elements in the HTML form.

Twilio Programmable Voice

Twilio Programmable Voice is a service that enables developers to integrate voice calling capability into their applications. It offers APIs for making and receiving phone calls, as well as managing voice answers.

Role in the Project

Twilio Programmable Voice is used to deliver voice notifications to specific phone numbers. It enables us to make voice calls programmatically and send messages to the recipients.

Buy a Twilio Number

The first step is to have a Twilio phone number. Login to the Twilio Console. On the left side, click on manage, select buy a number, and choose a number that you prefer from the list of available numbers.

twilio console image

Create Your Project Directory

First, create a new directory for your project and navigate into it:

mkdir twilio-project
cd twilio-project

Get Your Twilio Credentials

Return to your Twilio Console and scroll to the bottom of the page to see your Account SID and Auth token. These are in the Account Info panel as seen in the image below. Copy these values and save them somewhere secure. They’ll be needed when deploying the application created in this project.

Image of Twilio Console

Create a .env file in the root directory of your project. To this file, add your Twilio account credentials and a Twilio phone number:

TWILIO_ACCOUNT_SID=your_twilio_account_sid
TWILIO_AUTH_TOKEN=your_twilio_auth_token
TWILIO_PHONE_NUMBER=your_twilio_phone_number

Set up the Development Environment

Since you'll be utilizing Python and the Flask web framework, ensure that Python is installed on your computer. It's recommended to use a virtual environment to manage dependencies. Go to your project folder in the terminal and create a virtual environment. Run the virtual environment depending on your operating system using the code below.

python -m venv .venv
# Unix or Mac
source .venv/bin/activate
# Windows
.venv\Scripts\activate

You will also need to install Flask and the Twilio Python module. Navigate to the project folder in your terminal and run this command:

pip install flask twilio

Another thing you'll need is the python-dotenv library to load environment variables from a .env file. Install it with this command:

pip install python-dotenv

Build the Application

Create the Flask Application

Inside your project folder, create a new file called main.py and add the following code:

from flask import Flask, render_template, request, jsonify
from twilio.twiml.voice_response import VoiceResponse, Gather
from twilio.rest import Client
import logging
import os
from dotenv import load_dotenv
app = Flask(__name__)

# Configure logging
logging.basicConfig(level=logging.INFO)

# Specify the path to the .env file
dotenv_path = os.path.join(os.path.dirname(__file__), '.env')

# Load environment variables from .env file
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)
else:
    logging.error(".env file not found at {}".format(dotenv_path))

# Access environment variables
account_sid = os.getenv('TWILIO_ACCOUNT_SID')
auth_token = os.getenv('TWILIO_AUTH_TOKEN')
twilio_number = os.getenv('TWILIO_PHONE_NUMBER')
client = Client(account_sid, auth_token)

# Phone numbers
to_numbers = [' +1551234567   ']  # List of recipient phone numbers

In this code, you’re importing the necessary modules, configuring logging, and loading environment variables from the .env file using the python-dotenv library. You’ll store your Twilio account credentials and phone numbers in a .env file for security purposes.

Next, within the same file, define a Flask route for the homepage:

@app.route('/')
def index():
    return render_template('the_call.html')

This route will render an HTML template called the_call.html, which you'll create shortly.

Now, define the route that will handle the emergency notification:

@app.route('/send_notification', methods=['POST'])
def send_notification():
    if request.is_json:
        data = request.get_json()
        message = data['message']
    else:
        message = request.form.get('message', '')
    for to_number in to_numbers:
        try:
            response = VoiceResponse()
            response.say(message)
            twiml = str(response)
            # Log the TwiML response
            logging.info(f'TwiML to be sent: {twiml}')
            call = client.calls.create(
                twiml=twiml,
                to=to_number,
                from_=twilio_number
            )
            logging.info(f'Notification sent to {to_number} (Call SID: {call.sid})')
        except Exception as e:
            logging.error(f'Error sending notification to {to_number}: {e}')
    return jsonify({'Message': 'Notifications sent successfully'})

This route is responsible for sending the emergency notification as a voice call to the specified phone numbers. It accepts a POST request with an emergency message in the request body.

The send_notification function first checks if the request is JSON or form data and extracts the message accordingly. Then, it iterates over the list of recipient phone numbers (to_numbers) and uses the Twilio Python library to create a voice call for each number. The twiml parameter specifies the Twilio Markup Language (TwiML) instructions for the call, which in this case is to say the provided message using text-to-speech.

If the call is created successfully, a log message is printed with the recipient’s phone number and the call SID (a unique identifier for the call). If there's an error creating the call, an error log message is printed instead.

Finally, the function returns a JSON response indicating that the notifications were sent successfully.

At the end of the main.py file, add the following code to run the Flask application in debug mode:

if __name__ == '__main__':
    app.run(debug=True)

Create the HTML Template

The next step is to create the the_call.html template file that will be rendered by the Flask route. Create a new folder called templates in your project directory, and inside it, create a file called the_call.html with the following content:

<!DOCTYPE html>
<html>
<head>
    <title>Emergency Notification System</title>
    <script src="https://unpkg.com/htmx.org@1.8.4"></script>
    <style>
        /* CSS styles go here */
    </style>
</head>
<body>
    <h1>Emergency Notification System</h1>
    <form hx-post="/send_notification" hx-target="this">
        <label for="message">Emergency Message:</label>
        <input type="text" id="message" name="message" required>
        <button type="submit">Send Notification</button>
    </form>
    <div id="response"></div>
    <script>
        document.body.addEventListener('htmx:after-request', function(event) {
            const response = event.detail.response;
            const responseDiv = document.getElementById('response');
            responseDiv.textContent = response;
        });
    </script>
</body>
</html>

This HTML file contains a form with an input field for the emergency message and a submit button. When the button is clicked, the form data is sent to the /send_notification route using HTMX's hx-post attribute. The hx-target="this" attribute tells HTMX to update the form itself with the server response.The HTML also includes a <div> element with an ID of the response, which will be used to display the server's response after sending the notification.

At the bottom of the HTML file, you have a JavaScript event listener that listens for the htmx:after-request event, which is triggered after HTMX receives a response from the server. The event listener updates the response div with the server's response text.

In your HTML template, you'll include the HTMX library by loading it from a CDN:

<script src="https://unpkg.com/htmx.org@1.8.4"></script>

You then use HTMX attributes in our HTML code to define how the page should behave. In the form tag, you use the 1hx-post attribute to specify the URL to which the form data should be sent:

<form hx-post="/send_notification" hx-target="this">

The hx-target="this" attribute tells HTMX to update the form itself with the server's response.

After the form, you have a <div> element with an ID of response:

<div id="response"></div>

This <div> will be updated with the server's response after sending the notification.

Finally, you have a JavaScript event listener that listens for the htmx:after-request event, which is triggered after HTMX receives a response from the server:

document.body.addEventListener('htmx:after-request', function(event) {
    const response = event.detail.response;
    const responseDiv = document.getElementById('response');
    responseDiv.textContent = response;
});

This event listener updates the response div with the server's response-text

You can also modify your HTML file using CSS Styles. In between <style> tags in the header of your HTML file, input the following code:

body {
    font-family: Arial, sans-serif;
}
h1 {
    color: #333;
}
label {
    display: block;
    margin-top: 10px;
    font-weight: bold;
}
input[type="text"] {
    width: 50%;
    padding: 5px;
    margin-top: 5px;
}
button[type="submit"] {
    padding: 5px 10px;
    margin-top: 10px;
    background-color: #4CAF50;
    color: white;
    border: none;
    cursor: pointer;
}
button[type="submit"]:hover {
    background-color: #45a049;
}
#response {
    margin-top: 20px;
    font-style: italic;
    color: #666;
}

These CSS styles enhance the visual appearance of the HTML elements by applying consistent styles for fonts, colors, spacing, and layout. They also provide visual cues for interactive elements like the submit button by changing the cursor and background color on hover. These styles ensure a clean and organized layout for emergency notification form.

The full code for the HTML file should look like this:

<!DOCTYPE html>
<html>
<head>
    <title>Emergency Notification System</title>
    <script src="https://unpkg.com/htmx.org@1.8.4"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
        }
        h1 {
            color: #333;
        }
        label {
            display: block;
            margin-top: 10px;
            font-weight: bold;
        }
        input[type="text"] {
            width: 50%;
            padding: 5px;
            margin-top: 5px;
        }
        button[type="submit"] {
            padding: 5px 10px;
            margin-top: 10px;
            background-color: #4CAF50;
            color: white;
            border: none;
            cursor: pointer;
        }
        button[type="submit"]:hover {
            background-color: #45a049;
        }
        #response {
            margin-top: 20px;
            font-style: italic;
            color: #666;
        }
    </style>
</head>
<body>
    <h1>Emergency Notification System</h1>
    <form hx-post="/send_notification" hx-target="this">
        <label for="message">Emergency Message:</label>
        <input type="text" id="message" name="message" required>
        <button type="submit">Send Notification</button>
    </form>
    <div id="response"></div>
    <script>
        document.body.addEventListener('htmx:after-request', function(event) {
            const response = event.detail.response;
            const responseDiv = document.getElementById('response');
            responseDiv.textContent = response;
        });
    </script>
</body>
</html>

Test the Application

To test the application, follow these steps:

  1. When deploying your application, make sure to set the required environment variables (TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_PHONE_NUMBER) in the .env file with your actual Twilio credentials and phone number.
  2. Update the to_numbers list in main.py with the phone numbers you want to receive the emergency notifications.
  3. Run the Flask application by running:

python main.py
  1. Open your web browser and navigate to http://localhost:5000/.
  2. Enter an emergency message in the input field and click the "Send Notification" button.
  3. Check the console output for log messages indicating that the notifications were sent successfully. You should have the below output on your terminal.
Emergency notification system image

Verify that you received a voice call on the specified phone numbers with the emergency message.

If the messages were not sent correctly, you can add debug logging to verify the message being retrieved from the form. Add this line of code to the def self_notification() function.

logging.debug(f'Received message: {message}') # Debug log to check the message

You can add logging in to your Flask application to print out the TwiML being generated. This way, you can ensure that the correct TwiML is being created and sent to Twilio.

Under the def self_notification(), include the following code. This code needs to be included within the try block where the TwiML is defined.

# Log the TwiML response
logging.info(f'TwiML to be sent: {twiml}')

After making this change, rerun your Flask application, send a notification, and check the application logs. You should see the TwiML response containing your message on your terminal.

Conclusion

This article explored how to build an emergency notification system using Flask, HTMX, and Twilio Programmable Voice. It covered setting up the development environment, building the Flask application, integrating HTMX for interactive user interfaces, implementing the voice call functionality with Twilio Programmable Voice, and testing the application.

This emergency notification system can be useful in various scenarios where timely notifications are critical, such as emergency response systems, security monitoring, or alerting key personnel during critical events.

By leveraging the power of Flask, HTMX, and Twilio Programmable Voice, you can create a robust and interactive notification system that can quickly deliver urgent messages to multiple recipients via voice calls.

If you want to compare your version to a final version on Github, here is a link to the code repository.

Oyedele Tioluwani is a Technical Writer specializing in converting complex technical concepts into user-friendly documentation and creating effective software solutions with Python.