Home
Blog
Sending Messages with WhatsApp in Your Python Applications

October 24, 2022

Sending Messages with WhatsApp in Your Python Applications

By Dmitry Vinnik

WhatsApp Business Platform enables businesses to communicate more closely with their audiences, offering tools that allow you to automate the sending, receiving, and handling of incoming messages. For example, automated messaging allows you to welcome new customers or notify them when they contact you outside business hours.

This article describes how you can integrate the Cloud API, hosted by Meta, Meta’s integration of the WhatsApp Business Platform, into a Python application to enable sending and managing WhatsApp messages.

Let’s dive in and explore how to create a Python web app powered with WhatsApp messaging from scratch. If you’d like a preview of where we’ll end up, you can download the complete application code.

Requirements

To send and receive messages using a test phone number, follow the Set up Developer Assets and Platform Access tutorial, ensuring that you complete the steps below:

Register for a free developer account at Meta for Developers.

Enable two-factor authentication for your account:

Create a Meta App. The App Id and the App Secret will be used later in this article.

Connect your Meta App with the WhatsApp product.

Associate your app with a Business Manager.

On the App Dashboard, open the WhatsApp > Get Started menu and configure a recipient phone number. Your app will need it as a recipient for the WhatsApp messages. This number will be used later in this article.

Create a system user for your Business Account.

On the System Users page, generate a new token for your new system user, assigning your WhatsApp app and all the available permissions. This token will be used later in this article.

On the System Users page, configure the assets to your System User, assigning your WhatsApp app with full control. Don’t forget to click the Save Changes button.

Last but not least, download and install Python if you haven’t already.

The App We’re Building

Our small sample application will work as an online flight reservation service. The application will use the API to provide the user with an engaging and more personalized experience than what email communication offers. When the users log in, they’re greeted by a WhatsApp message. Then, when they buy a flight ticket, they receive a message confirming the purchase.

Creating a Minimal App with Python and Flask

This section will help you get a new Python project up and running. We’ll use Jinja, a lightweight template engine, and Flask, a micro web framework.

First, open a terminal and create a folder for your project. Then execute the following command:

python3 -m venv venv 

This command will create a virtual environment for your Python project.

Then, execute the following:

$ mkdir myproject
$ cd myproject
$ python3 -m venv venv

Next, activate the virtual environment.

$ . venv/bin/activate

Now, install Flask:

pip install flask[async]

Create an app.py file at the project root with this content:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

Run the app:

$ flask run

Then, you’ll see the app running locally at port 5000:

 * Serving Flask app 'app.py' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)

Now, visit http://127.0.0.1:5000/ and you will see the homepage of your Python + Flask starter application:

Creating the Sample Login Page

To start your Flight Ticket application, you’ll create a sample login form that will work as your homepage. You’ll need to call the render_template function to render a view from a separate HTML file. Open the app.py file and modify it to import the render_template function:

from flask import Flask, render_template

Then replace the hello_world with the index function as follows:

def index():
    return render_template('index.html', name=__name__)

Create a new folder named templates and create a new file named index.html:

\templates
	|--- index.html

Next, open the index.html file and add the HTML content below. Here, you’re creating an example login that comes with a placeholder login and password. This way, you don’t need to provide those to use the application.

For our web app front-end, we’re using Bootstrap. This popular library will help build a consistent, lightweight UI that comes with responsive styling, allowing us to easily run our app across devices without worrying about CSS rules:

<!DOCTYPE html>
<html>
  <head>
    <title>Flight Confirmation Demo for Python</title>
    <h1 class="text-center">Flight Confirmation Demo for Python</h1>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
      crossorigin="anonymous"
    />
  </head>
  <body>
    <div class="d-flex flex-row justify-content-center align-items-center">
      <div class="border px-3">
        <div class="row">
          <div class="col-sm-6 d-none d-sm-block">
            <img
              src="https://images.unsplash.com/photo-1530521954074-e64f6810b32d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NHx8YWlyJTIwdHJhdmVsfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60"
              alt="Login image"
              class="w-100 vh-50 pt-3 pb-3"
              style="object-fit: cover; object-position: left"
            />
          </div>
          <div class="col-sm-6 text-black">
            <div class="px-5 ms-xl-4">
              <i
                class="fas fa-crow fa-2x me-3 pt-5 mt-xl-4"
                style="color: #709085"
              ></i>
            </div>

            <div class="d-flex align-items-center h-custom-2">
              <form class="w-100" method="post" action="/welcome">
                <div class="form-outline mb-4">
                  <input
                    type="text"
                    value="this_is_a_demo@email.com"
                    disabled
                    class="form-control form-control-md text-muted"
                  />
                </div>

                <div class="form-outline mb-4">
                  <input
                    type="text"
                    value="••••••••••••••••"
                    disabled
                    id="form2Example28"
                    class="form-control form-control-md text-muted"
                  />
                </div>

                <div class="pt-1 mb-4">
                  <input type="submit" class="btn btn-info btn-lg btn-block" value="Login"/>
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
     
    </div>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  </body>
</html>

Then, run the app again to see the new login page:

> flask run

Sending Text Messages with Python and WhatsApp Business

Your Python application will need to use specific data from your Meta developer account created in the beginning of this article. For the convenience of having all of your configuration in one place, and not scattered throughout code during development, place it in a file.

Create a config.json file at the project root with the following settings, replacing any placeholders with details from your WhatsApp Business account dashboard:

{
  "APP_ID": "<<YOUR-WHATSAPP-BUSINESS-APP_ID>>",
  "APP_SECRET": "<<YOUR-WHATSAPP-BUSINESS-APP_SECRET>>",
  "RECIPIENT_WAID": "<<YOUR-RECIPIENT-TEST-PHONE-NUMBER>>",
  "VERSION": "v13.0",
  "PHONE_NUMBER_ID": "<<YOUR-WHATSAPP-BUSINESS-PHONE-NUMBER-ID>>",
  "ACCESS_TOKEN": "<<YOUR-SYSTEM-USER-ACCESS-TOKEN>>"
}

Your login form action tells the app to POST to the /welcome route. So, you’ll need a new router to:

  • Handle the “welcome” HTTP POST request.
  • Obtain the configuration needed for the welcome message.
  • Send a welcome message via the API.
  • Redirect the app to the homepage once the message is sent. request.

Now, open the app.py file and replace its contents with the following code to include the /welcome endpoint:

import json
from flask import Flask, render_template
import flask
from message_helper import get_text_message_input, send_message

app = Flask(__name__)

with open('config.json') as f:
    config = json.load(f)

app.config.update(config)

@app.route("/")
def index():
    return render_template('index.html', name=__name__)

@app.route('/welcome', methods=['POST'])
async def welcome():
  data = get_text_message_input(app.config['RECIPIENT_WAID']
                                , 'Welcome to the Flight Confirmation Demo App for Python!');
  await send_message(data)
  return flask.redirect(flask.url_for('index'))

Now, install aiohttp to enable your app to perform asynchronous HTTP requests:

pip install aiohttp[speedups]

Next, you’ll need the function to encapsulate the code that sends text-based messages via the API. Create a new message_helper.py file at the project root with the following code:

import aiohttp
import json
from flask import current_app

async def send_message(data):
  headers = {
    "Content-type": "application/json",
    "Authorization": f"Bearer {current_app.config['ACCESS_TOKEN']}",
    }
  
  async with aiohttp.ClientSession() as session:
    url = 'https://graph.facebook.com' + f"/{current_app.config['VERSION']}/{current_app.config['PHONE_NUMBER_ID']}/messages"
    try:
      async with session.post(url, data=data, headers=headers) as response:
        if response.status == 200:
          print("Status:", response.status)
          print("Content-type:", response.headers['content-type'])

          html = await response.text()
          print("Body:", html)
        else:
          print(response.status)        
          print(response)        
    except aiohttp.ClientConnectorError as e:
      print('Connection Error', str(e))

def get_text_message_input(recipient, text):
  return json.dumps({
    "messaging_product": "whatsapp",
    "preview_url": False,
    "recipient_type": "individual",
    "to": recipient,
    "type": "text",
    "text": {
        "body": text
    }
  })

The above code makes an HTTP POST request to the /messages endpoint on the Meta Graph API at graph.facebook.com, passing:

  • The Cloud API version you’re working with
  • The test phone number that will receive the message (you have already configured this)
  • The access token you generated for your System User

Also, note that the get_text_message_input function returns a specific data structure required for sending basic text messages.

Finally, run the app again:

> flask run

Then click the Login button. You’ll see the WhatsApp notification popping up on your screen:

Click that notification to open the WhatsApp app and see the basic text message sent by your Python application:

So far, you’ve been able to send simple messages using WhatsApp. Next, you will use templates to send more complex messages.

Creating the Flight Catalog Page

First, you’ll create a catalog of available flights and their details so that online customers can buy tickets. This data will be stored in a separate file. Create a new \flights.py file with the following content:

def get_flights():
  return [{
    "flight_id": 1,
    "document": 'https://github.com/marcelooliveira/flight-confirmation-python/raw/main/FlightConfirmation.pdf',
    "thumbnail": 'https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Aerial_view_of_Bajra_Sandhi_Monument_Denpasar_Bali_Indonesia.jpg/250px-Aerial_view_of_Bajra_Sandhi_Monument_Denpasar_Bali_Indonesia.jpg',
    "origin": 'Singapore (SIN)',
    "destination": 'Denpasar (DPS)',
    "time": 'June 24, 2022 - 8:25 PM'
  },
  {
    "flight_id": 2,
    "document": 'https://github.com/marcelooliveira/flight-confirmation-python/raw/main/FlightConfirmation.pdf',
    "thumbnail": 'https://upload.wikimedia.org/wikipedia/commons/thumb/7/75/Parliament_at_Sunset.JPG/275px-Parliament_at_Sunset.JPG',
    "origin": 'New York (JFK)',
    "destination": 'London (LHR)',
    "time": 'June 25, 2022 - 9:15 PM'
  },
  {
    "flight_id": 3,
    "document": 'https://github.com/marcelooliveira/flight-confirmation-python/raw/main/FlightConfirmation.pdf',
    "thumbnail": 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/Sydney_Australia._%2821339175489%29.jpg/250px-Sydney_Australia._%2821339175489%29.jpg',
    "origin": 'Beijing (PEK)',
    "destination": 'Sydney (SYD)',
    "time": 'June 25, 2022 - 5:30 AM'
  },
  {
    "flight_id": 4,
    "document": 'https://github.com/marcelooliveira/flight-confirmation-python/raw/main/FlightConfirmation.pdf',
    "thumbnail": 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Mouth_of_Miami_River_20100211.jpg/220px-Mouth_of_Miami_River_20100211.jpg',
    "origin": 'São Paulo (GRU)',
    "destination": 'Miami (MIA)',
    "time": 'June 25, 2022 - 9:25 AM'
  }]

You now need a new route for users to access the flights catalog page. Open the app.py file and import the get_flight function:

from flights import get_flights

Then, add the catalog function to the app.py file:

@app.route("/catalog")
def catalog():
    return render_template('catalog.html', title='Flight Confirmation Demo for Python', flights=get_flights())

Modify the welcome function to redirect to the catalog page instead of the index page:

 return flask.redirect(flask.url_for('catalog'))

Finally, create a new file at templates\catalog.html with the following content:

<!DOCTYPE html>
<html>
  <head>
    <title>{{title}}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
  </head>
  <body>
    <h1 class="text-center">{{title}}</h1>
    <p class="text-center">Welcome to {{title}}</p>

    <div class="container"> 
      <div class="row">
        {% for flight in flights %}
          <div class="col col-3">
            <div class="card">
              <img src="{{ flight.thumbnail }}" class="card-img-top" alt="{{flight.origin}}"/>
              <div class="card-body">
                <p class="card-text"><b>origin:</b> {{ flight.origin }}</p>
                <p class="card-text"><b>destination:</b> {{ flight.destination }}</p>
                <p class="card-text"><b>time:</b> {{ flight.time }}</p>
                <p>
                  <form method="POST" action="/buy-ticket">
                    <div class="container">
                      <input type="hidden" name="id" value="{{flight.flight_id}}"/>
                      <div class="row gx-5">
                        <input type="submit" value="&#127915; Buy" class="col col-5"/>
                      </div>
                    </div>
                  </form>
                </p>
              </div>
            </div>
          </div>
        {% endfor %}
      </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
  </body>
</html>

Run the app again and click the Login button. This will send your WhatsApp number a welcome message. Plus, it will redirect you to the /catalog view:

> flask run

Note that there is a button to buy the ticket for each flight displayed on the screen above. Next, you will configure the application to process the ticket purchase.

Sending Templated Messages with Python and WhatsApp Business

A message template is required to start a business-initiated conversation. These conversations can be customer care messages or appointment reminders, payment or shipping updates, alerts, and more.

Open the app.py file and add an import of the get_templated_message_input and request functions:

from flask import Flask, render_template, request
from message_helper import get_templated_message_input, get_text_message_input, send_message

Then, add a new function for the /buy-ticket route with the following contents:

@app.route("/buy-ticket", methods=['POST'])
async def buy_ticket():
  flight_id = int(request.form.get("id"))
  flights = get_flights()
  flight = next(filter(lambda f: f['flight_id'] == flight_id, flights), None)
  data = get_templated_message_input(app.config['RECIPIENT_WAID'], flight)
  await send_message(data)
  return flask.redirect(flask.url_for('catalog'))

Next, open the message_helper.py file and include the get_templated_message_input function:

def get_templated_message_input(recipient, flight):
  return json.dumps({
    "messaging_product": "whatsapp",
    "to": recipient,
    "type": "template",
    "template": {
      "name": "sample_flight_confirmation",
      "language": {
        "code": "en_US"
      },
      "components": [
        {
          "type": "header",
          "parameters": [
            {
              "type": "document",
              "document": {
                "filename": "FlightConfirmation.pdf",
                "link": flight['document']
              }
            }
          ]
        },
        {
          "type": "body",
          "parameters": [
            {
              "type": "text",
              "text": flight['origin']
            },
            {
              "type": "text",
              "text": flight['destination']
            },
            {
              "type": "text",
              "text": flight['time']
            }
          ]
        }
      ]
    }
  })

Note that we are using the sample_flight_confirmation template above, where we provided the flight document PDF file, the flight origin, destination, and the date/time. You can experiment with other available templates or create new ones by visiting the Message Templates page.

Finally, run the app again and click one of the Buy buttons. This will cause your app to send a template message to your test phone number via WhatsApp:

> flask run

Now, open your WhatsApp app to see the template message.

That’s it!

As you can see, sending messages with Python code can be straightforward. However, note the following tips and best practices for integrating WhatsApp into applications:

  • Even if you’re automating your app messages, make sure that communication with customers doesn’t feel robotic. People expect a more personal experience, so ensure that you’re sending more personalized messages.
  • Explore a more relaxed and informal tone. However, avoid syntax or grammar mistakes.
  • Keep your text clear and to the point.
  • When using templates, provide rich context information by using links to documents, videos, or images like those used above to depict the flights related to the tickets.

Conclusion

In this article, you learned how to add messaging capability to a Python app by integrating it with a WhatsApp Business account.

After creating a simple Python application from scratch, you added a sample login page and configured the application to send basic welcome messages to users via the Cloud API. Finally, you added a catalog page and configured it to send template messages with flight confirmation details.

And this is only the tip of the iceberg. Want to learn how to configure WebHooks in your application and configure notifications about sending and receiving customer messages and business account information? Check out WhatsApp Business Platform documentation to discover this and much more.


Get our newsletter

Sign up for monthly updates from Meta for Developers.

Sign up