Twilio Verify is an easy-to-use service for user verification through numeric codes. This service now includes support for WhatsApp (in public beta, as of June 2022). In this tutorial, you’ll learn how to implement a WhatsApp user verification flow in a Python and Flask application.
To work on this tutorial you will need the following items:
- Python 3.6 or newer. If your operating system does not provide a Python interpreter, you can go to python.org to download an installer.
- A Twilio account. If you are new to Twilio, click here to create a free account now and receive $10 credit when you upgrade to a paid account. You can review the features and limitations of a free Twilio account.
- An active WhatsApp account, to test the project.
Create a Twilio Verify service
To use Twilio Verify in an application, a Verify service must be created first. Open the Twilio Console in your web browser, type verify in the navigation bar’s search box, and select “Verify services” from the search results.
In the Verify services page, click the “Create Service Now” button.
If you already have one or more Verify services in your account, then this button will be displayed as a plus sign at the top of the service list.
Type the name of your application in the “Friendly Name” field. For this tutorial, I’m going to use the name “Awesome App”. The name that you type in this field will appear in the WhatsApp verification messages that are sent to your users. Once you have entered the name, press “Create”.
Once the service is created, you will be redirected to its settings page. Leave this page open while you continue with the tutorial, as you will later need the “Service SID” that was assigned to your service.
For this tutorial, the default settings for the service are appropriate, but note how you can configure, among other things, how many digits to use in the verification codes that are sent to users.
In this section, you are going to set up a brand-new Flask project. To keep things nicely organized, open a terminal or command prompt and find a suitable parent directory where the project you are about to create will live. Type the following commands to create a directory for this project:
mkdir flask-whatsapp-verify cd flask-whatsapp-verify
This application is going to need a few HTML templates. Create a directory for them inside the project:
Create a virtual environment
Following Python best practices, you are now going to create a virtual environment, where you are going to install the Python dependencies needed for this project.
If you are using a UNIX or macOS system, open a terminal and enter the following commands:
python3 -m venv venv source venv/bin/activate
If you are following the tutorial on Windows, enter the following commands in a command prompt window:
python -m venv venv venv\Scripts\activate
With the virtual environment activated, you are ready to install the Python dependencies required for this project:
pip install flask python-dotenv twilio
The Python packages that this project uses are:
- The Flask framework, to create the web application.
- python-dotenv, to import the application configuration from a .env file.
- Twilio Python Helper library, to work with the Twilio APIs.
Define application settings
To send verification messages with Twilio Verify, the Flask application will need to authenticate with your Twilio account credentials. The application will also need to know the “Service SID” assigned to the Verify service you created above.
The most secure way to define these configuration values is to set environment variables for them, and the most convenient way to manage your environment variables in a Flask application is to use a .env file.
Create a new file named .env (note the leading dot) in the root directory of your project and enter the following contents in it:
TWILIO_ACCOUNT_SID=xxxxxxxxx TWILIO_AUTH_TOKEN=xxxxxxxxx TWILIO_VERIFY_SERVICE=xxxxxxxxx
You will need to replace all the
xxxxxxxxx placeholders with the correct values that apply to you. The first two variables are your Twilio “Account SID” and your “Auth Token”. You can find them in the “Account Info” section of the Twilio Console main dashboard:
The value of the third configuration variable comes from the “Service SID” field in the Verify service page. This is a long string of characters that starts with the letters
Send a verification code
You are now ready to start coding the Python application. In your text editor or IDE, create a file named app.py in the root directory of the project. Copy the following contents to it:
import os from flask import Flask, render_template, request, session, redirect, url_for from twilio.rest import Client app = Flask(__name__) app.config['SECRET_KEY'] = 'top-secret!' client = Client() @app.route('/', methods=['GET', 'POST']) def index(): if request.method == 'GET': return render_template('index.html') phone = request.form.get('phone') if not phone: return redirect(url_for('index')) verification = client.verify.services( os.environ['TWILIO_VERIFY_SERVICE']).verifications.create( to=phone, channel='whatsapp') if verification.status != 'pending': return redirect(url_for('index')) session['phone'] = phone session['verified'] = False return redirect(url_for('verify'))
The application creates two global objects: the Flask application and the Twilio client instance. The Flask application instance is configured with a secret key, as this is required to have access to session storage.
The Twilio client is implicitly initialized with the
TWILIO_AUTH_TOKEN variables defined in the environment. If these variables are not defined or are incorrect, an authentication error message will appear when using the application.
index() function is the handler for the root URL of the application. This route supports
POST methods. The
GET request is going to render a web form where the user can enter their WhatsApp phone number. The browser will then send a
POST request to the same URL when the user submits the form.
When the request method is
GET, the application renders a template named index.html. Create the index.html file in the templates subdirectory, and copy the following HTML into it:
<!doctype html> <html> <head> <title>Twilio WhatsApp Verification Example</title> </head> <body> <h1>Twilio WhatsApp Verification Example</h1> <form method="post"> <label for="phone">Your WhatsApp number:</label> <input name="phone" id="phone" placeholder="+12345678900" autofocus> <input type="submit" value="Submit"> </form> </body> </html>
This HTML page renders a form that prompts the user to enter their WhatsApp phone number.
When the user submits the form, the browser will send a
POST request to the same URL. At this point the application will retrieve the phone number from the
request.form object, provided by the Flask framework. If the phone number is not present, then a redirect to the index page is issued, to give the user another chance to enter correct information.
The Twilio client is then used to create a
verification instance, initialized with the user’s phone number and a channel set to
The application then checks the reported status on the verification instance. If the status is anything other than
pending, it is assumed that an error has occurred and the user is then redirected to the main page.
With a pending verification, the phone number is stored in the user’s session, and then the user is redirected to the /verify URL, which is where the verification code will be requested.
Verify a code
Once the user provides a phone number and a verification code is issued, the /verify page of the application allows the user to enter the code they received via WhatsApp.
Below you can see the verification route of the application. Add this code at the bottom of app.py:
@app.route('/verify', methods=['GET', 'POST']) def verify(): if request.method == 'GET': return render_template('verify.html') phone = session.get('phone') code = request.form.get('code') if not phone: return redirect(url_for('index')) if not code: return redirect(url_for('verify')) verification_check = client.verify.services( os.environ['TWILIO_VERIFY_SERVICE']).verification_checks.create( to=phone, code=code) if verification_check.status == 'approved': del session['phone'] session['verified'] = True return redirect(url_for('success')) return redirect(url_for('verify'))
This route also supports
POST requests. In the
GET case, it renders a template named verify.html. In your editor, open a new file named verify.html in the templates subdirectory, and copy the following HTML page to it:
<!doctype html> <html> <head> <title>Twilio WhatsApp Verification Example</title> </head> <body> <h1>Twilio WhatsApp Verification Example</h1> <form method="post"> <label for="code">Verification code:</label> <input name="code" id="code" placeholder="123456" autofocus> <input type="submit" value="Submit"> </form> </body> </html>
The form defined in verify.html requests a code from the user. When the form is submitted, a
POST request is sent to the same URL.
The application retrieves the
phone argument from the session storage and the
code argument submitted with the form, and sends them to Twilio Verify to create a verification check instance. If the code is correct for the given number, then the status of the verification check is
approved. In this case, the phone number is removed from the session, the
verified session variable is set to
True, and then the user is redirected to the third and last route of the application on the /success URL. If the status is any other value, then the verification has failed and the user is redirected back to the /verify route to try another code.
Below, you can see the definition of the /success route. Copy this code at the bottom of app.py.
@app.route('/success') def success(): if not session.get('verified'): return redirect(url_for('index')) return render_template('success.html')
This route renders the success.html template when the user has been verified, or else redirects to the initial page. This HTML template is shown below. Remember that template files go in the templates subdirectory.
<!doctype html> <html> <head> <title>Twilio WhatsApp Verification Example</title> </head> <body> <h1>Twilio WhatsApp Verification Example</h1> <p>You have been verified!</p> <p><a href="/">Verify another number?</a></p> </body> </html>
Run the application
The application is now complete. Make sure that your app.py file is in the root directory of the project, and that your templates subdirectory has the index.html, verify.html and success.html files.
Start the application by entering the following command in your terminal:
A few seconds later, you should have an incoming message on WhatsApp with your code. Type this code in the verification page of the application to receive a success message. Also, feel free to try a wrong code to see how it is rejected and you’re offered another chance.
Congratulations on learning how to verify users on WhatsApp with Twilio Verify! Did you know that Twilio Verify can also send verification codes to users by SMS, voice call and email? It can also verify standard TOTP codes and push notifications to your iOS or Android app!
I’d love to see what you build with Twilio Verify!