Revenir aux actualités des développeurs

Creating Surveys with WhatsApp Flows

6 mars 2024DeGafi G & Iryna Wagner

WhatsApp Flows permet d’optimiser et de simplifier la collecte des données clientèle de votre entreprise. Votre organisation peut recueillir des informations structurées en toute simplicité à partir des échanges avec les client·es qui, en retour, bénéficient d’une expérience utilisateur positive sur WhatsApp. WhatsApp Flows est idéal pour collecter des données de génération de prospects, réaliser des enquêtes, permettre aux client·es de prendre des rendez-vous ainsi que de transmettre leurs questions et préoccupations, et bien plus encore.

Mieux encore, vous pouvez offrir à votre clientèle toutes ces options sans avoir à créer une application et un back-end complexes. Utilisez simplement WhatsApp comme interface et servez-vous d’un webhook pour récupérer les réponses sous forme de messages JSON, traiter les informations et extraire les données dont vous avez besoin.

En prenant une entreprise fictive comme exemple, ce tutoriel présente la configuration d’une enquête auprès de la clientèle sur WhatsApp à l’aide de webhooks. L’enquête recueillera des informations sur la manière dont la clientèle a découvert l’entreprise et sur les types de circuits qu’elle préfère, afin que l’entreprise puisse améliorer ses services pour ses client·es actuel·les et futur·es.

Mener une enquête avec WhatsApp Flows

Conditions requises

Pour continuer, assurez-vous que vous disposez des éléments suivants :

Processus

  1. Créez une application Flask.
  2. Écrivez le code Python pour créer et publier les flux à l’aide de l’API WhatsApp Flows. Le code Python enverra également le flux publié à l’aide de l’API Cloud.
  3. Créez un webhook pour prendre en compte les messages d’une discussion.
  4. Exécutez l’application.

Si vous souhaitez avoir un aperçu du projet, vous pouvez voir le code complet.

Créer des enquêtes avec l’API WhatsApp Flows

Il existe deux façons de créer un flux : à l’aide de l’UI de l’Éditeur de flux ou de l’API Flows. Ce tutoriel met en place l’enquête par programmation au moyen de l’API Flows.

Pour créer un flux qui utilise des données dynamiques provenant de votre serveur, vous pouvez créer un point de terminaison connectant l’enquête à votre propre serveur. À l’aide d’un point de terminaison, vous pouvez contrôler la logique de navigation entre les écrans du flux, renseigner les données du flux à partir de votre serveur ou encore afficher/masquer des composants sur l’écran en fonction de l’interaction de l’utilisateur·ice.

L’exemple de flux d’enquête présenté n’utilise aucun point de terminaison puisqu’il n’y a pas d’échange de données dynamique entre celui-ci et un serveur. Vous utiliserez le webhook de discussion pour collecter les informations de l’enquête. De plus, vous pouvez joindre des flux à un modèle de message dans le Gestionnaire WhatsApp.

Créer une application Flask

Commencez par créer une application Flask pour interagir avec l’API Flows. Exécutez la commande suivante dans votre terminal pour créer un environnement virtuel.

python -m venv venv
        

Utilisez ensuite la commande ci-dessous pour activer l’environnement.

source venv/bin/activate
        

Ensuite, installez les packages nécessaires à l’aide de la commande suivante.

pip install requests flask python-dotenv
        

Vous utiliserez Flask pour créer des routes et interagir avec l’API Flows, demandes pour envoyer des requêtes HTTP et utiliser python-dotenv pour charger les variables d’environnement.

Créez maintenant un fichier d’environnement nommé .env et collez-y les informations suivantes.

VERIFY_TOKEN =
ACCESS_TOKEN =
WHATSAPP_BUSINESS_ACCOUNT_ID =
PHONE_NUMBER_ID =
        

Attribuez les valeurs en fonction des informations de votre compte de développeur. Vous pouvez utiliser n’importe quelle chaîne pour VERIFY_TOKEN. Les variables WHATSAPP_BUSINESS_ACCOUNT_ID et PHONE_NUMBER_ID sont les identifiants uniques de votre compte générés automatiquement par Meta. The ACCESS_TOKEN sert à authentifier et à autoriser les requêtes d’API.

Pour accéder à ces informations depuis le tableau de bord de votre application Meta, cliquez sur WhatsApp > Configuration de l’API dans le panneau de navigation de gauche, comme dans la capture d’écran ci-dessous.

Aperçu de la configuration de l’API

Enfin, dans le même répertoire, créez un fichier appelé main.py pour contenir la logique Python de création des flux et du webhook.

Créer le flux

Pour créer le flux, ajoutez tout d’abord les packages suivants au fichier main.py.

import os
import uuid
import requests
from dotenv import load_dotenv
from flask import Flask, request, make_response, json
        

Ajoutez ensuite l’extrait de code suivant à main.py pour initialiser les variables. L’extrait initialise également Flask et appelle la méthode load_dotenv() pour charger les variables.

app = Flask(__name__)

load_dotenv()
PHONE_NUMBER_ID = os.getenv('PHONE_NUMBER_ID')
VERIFY_TOKEN = os.getenv('VERIFY_TOKEN')
ACCESS_TOKEN = os.getenv('ACCESS_TOKEN')
WHATSAPP_BUSINESS_ACCOUNT_ID = os.getenv('WHATSAPP_BUSINESS_ACCOUNT_ID')
created_flow_id = ""
messaging_url = f"https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}/messages"

auth_header = {"Authorization": f"Bearer {ACCESS_TOKEN}"}

messaging_headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {ACCESS_TOKEN}",
}
        

Ensuite, ajoutez la route suivante pour gérer la création d’un flux.

@app.route("/create-flow", methods=["POST"])
def create_flow():
    flow_base_url = (
        f"https://graph.facebook.com/v18.0/{WHATSAPP_BUSINESS_ACCOUNT_ID}/flows"
    )
    flow_creation_payload = {"name": "<FLOW-NAME>", "categories": '["SURVEY"]'}
    flow_create_response = requests.request(
        "POST", flow_base_url, headers=auth_header, data=flow_creation_payload
    )

    try:
        global created_flow_id
        created_flow_id = flow_create_response.json()["id"]
        graph_assets_url = f"https://graph.facebook.com/v18.0/{created_flow_id}/assets"

        upload_flow_json(graph_assets_url)
        publish_flow(created_flow_id)

        print("FLOW CREATED!")
        return make_response("FLOW CREATED", 200)
    except:
        return make_response("ERROR", 500)

La fonction appelle le point de terminaison des flux (flow_base_url) tout en transmettant la charge utile (flow_creation_payload) contenant le nom et la catégorie du flux. Les valeurs possibles pour la catégorie sont les suivantes : SIGN_UP, SIGN_IN, APPOINTMENT_BOOKING, LEAD_GENERATION, CONTACT_US, CUSTOMER_SUPPORT, SURVEY ou OTHER.

Remplacez <FLOW-NAME> par le nom de votre choix (par exemple, flux_enquête).

Une fois que le code a créé le flux, il extrait created_flow_id pour importer son corps JSON.

Importer les composants JSON du flux

Créez un fichier survey.json avec ce contenu. Le fichier JSON contient la structure du flux.

Ensuite, collez le code ci-dessous dans le fichier main.py.

def upload_flow_json(graph_assets_url):
    flow_asset_payload = {"name": "flow.json", "asset_type": "FLOW_JSON"}
    files = [("file", ("survey.json", open("survey.json", "rb"), "application/json"))]

    res = requests.request(
        "POST",
        graph_assets_url,
        headers=auth_header,
        data=flow_asset_payload,
        files=files,
    )
    print(res.json())

Cette fonction importe les données JSON du fichier survey.json dans le point de terminaison des éléments du flux.

Dans l’extrait de code ci-dessous, lorsque l’utilisateur clique sur un élément, on-click-action est déclenchée, ce qui permet de capturer des données dans la charge utile. Le champ "name": "complete" indique que le flux est terminé. Il sera clôturé, et les données de charge utiles seront envoyées à votre serveur webhook.

...
"on-click-action": {
    "name": "complete",
    "payload": {
        "source": "${form.source}",
        "tour_type": "${form.tour_type}",
        "tour_quality": "${form.tour_quality}",
        "decision_influencer": "${form.decision_influencer}",
        "tour_guides": "${form.tour_guides}",
        "aspects_enjoyed": "${form.aspects_enjoyed}",
        "improvements": "${form.improvements}",
        "recommend": "${form.recommend}",
        "return_booking": "${form.return_booking}"
    }
}

...
        

Les valeurs contenues dans les objets de charge utile peuvent correspondre à des composants de flux (ressemblant à des noms d’éléments dans les formulaires HTML) ou à des objets de données. Les clés associées à ces valeurs de charge utile sont appelées des noms, de la même manière que l’on attribue des variables dans les langages de programmation.

Les éléments data-source contiennent également des ID qui servent de clés pour les valeurs. Le code envoie ces ID pour les choix. Par exemple, si l’utilisateur choisit Likely pour la data-source ci-dessous, le code envoie 1. Une fois que vous avez reçu les données, vous pouvez faire correspondre les sources de données.

...
{
    "type": "RadioButtonsGroup",
    "required": true,
    "name": "return_booking",
    "data-source": [
        {
            "id": "0",
            "title": "Very likely"
        },
        {
            "id": "1",
            "title": "Likely"
        },
        {
            "id": "2",
            "title": "Undecided"
        },
        {
            "id": "3",
            "title": "Unlikely"
        },
        {
            "id": "4",
            "title": "Very likely"
        }
    ]
}
...
        

Le flux se compose d’un écran contenant neuf questions à choix multiples, comme ci-dessous.

Flux d’enquête

Vous pouvez consulter les détails des éléments JSON dans la documentation de développement.

Publier le flux

Collez ensuite la fonction ci-dessous dans le fichier main.py pour ajouter la logique de publication du flux. Un flux publié est prêt à l’emploi, vous ne pourrez donc pas y apporter d’autres modifications.

def publish_flow(flow_id):
    flow_publish_url = f"https://graph.facebook.com/v18.0/{flow_id}/publish"
    requests.request("POST", flow_publish_url, headers=auth_header)
        

La fonction invoque le point de terminaison de publication tout en transmettant l’ID du Flow.

Envoyer le flux

Collez la fonction suivante dans le fichier main.py pour envoyer le flux à une personne utilisant WhatsApp. La fonction appelle le point de terminaison des messages de l’API Cloud tout en transmettant la charge utile du flux.

def send_flow(flow_id, recipient_phone_number):
    # Generate a random UUID for the flow token
    flow_token = str(uuid.uuid4())

    flow_payload = json.dumps(
        {
            "type": "flow",
            "header": {"type": "text", "text": "Survey"},
            "body": {
                "text": "Your insights are invaluable to us – please take a moment to share your feedback in our survey."
            },
            "footer": {"text": "Click the button below to proceed"},
            "action": {
                "name": "flow",
                "parameters": {
                    "flow_message_version": "3",
                    "flow_token": flow_token,
                    "flow_id": flow_id,
                    "flow_cta": "Proceed",
                    "flow_action": "navigate",
                    "flow_action_payload": {"screen": "SURVEY_SCREEN"},
                },
            },
        }
    )

    payload = json.dumps(
        {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": str(recipient_phone_number),
            "type": "interactive",
            "interactive": json.loads(flow_payload),
        }
    )

    requests.request("POST", messaging_url, headers=messaging_headers, data=payload)
    print("MESSAGE SENT")
        

La charge utile du flux contient les détails du flux. Le champ action.parameters.flow_token vous permet de transmettre un ID unique pour le message de flux qui sera transmis du client à votre webhook une fois le flux terminé. Pour ce tutoriel, vous utilisez un ID aléatoire (uuid). Le code définit le paramètre action.parameters.flow_action_payload.screen sur SURVEY_SCREEN qui est l’ID de l’écran que vous souhaitez afficher lorsque l’utilisateur clique sur action.parameters.flow_cta.

Configurer le webhook

La logique du webhook est assez simple. Elle comporte deux fonctions, webhook_get et webhook_post, qui gèrent respectivement les requêtes GET et POST. Le code utilise la requête GET lors de l’ajout du webhook à votre application Meta. Il renvoie l’élément hub.challenge de la requête en cas de succès. La requête POST affiche la charge utile du message dans le terminal.

@app.route("/webhook", methods=["GET"])
def webhook_get():
    if (
        request.args.get("hub.mode") == "subscribe"
        and request.args.get("hub.verify_token") == VERIFY_TOKEN
    ):
        return make_response(request.args.get("hub.challenge"), 200)
    else:
        return make_response("Success", 403)
        

La requête POST extrait et traite la charge utile du message. Comme le code ne s’occupe que de la charge utile du message, il émet des erreurs lors de la capture de toute autre charge utile. C’est pourquoi vous devez utiliser une instruction if pour vérifier s’il y a un corps messages. Une fois que la présence du corps JSON messages est confirmée, une autre vérification est effectuée pour extraire le numéro de téléphone de l’expéditeur uniquement s’il y a un corps text dans la charge utile messages.

@app.route("/webhook", methods=["POST"])
def webhook_post():
    # checking if there is a messages body in the payload
    if (
        json.loads(request.get_data())["entry"][0]["changes"][0]["value"].get(
            "messages"
        )
    ) is not None:
        """
        checking if there is a text body in the messages payload so that the sender's phone number can be extracted from the message
        """
        if (
            json.loads(request.get_data())["entry"][0]["changes"][0]["value"][
                "messages"
            ][0].get("text")
        ) is not None:
            user_phone_number = json.loads(request.get_data())["entry"][0]["changes"][
                0
            ]["value"]["contacts"][0]["wa_id"]
            send_flow(created_flow_id, user_phone_number)
        else:
            flow_reply_processor(request)

    return make_response("PROCESSED", 200)
        

En outre, vous devez utiliser la fonction d’aide suivante, appelée flow_reply_processor, pour extraire la réponse du flux et la renvoyer à l’utilisateur. Étant donné que la réponse du flux contient l’ID de l’option sélectionnée lors de la capture des données issues de RadioButtonsGroups, la fonction fait concorder les ID aux valeurs de chaîne correspondantes.

def flow_reply_processor(request):
    flow_response = json.loads(request.get_data())["entry"][0]["changes"][0]["value"][
        "messages"
    ][0]["interactive"]["nfm_reply"]["response_json"]

    flow_data = json.loads(flow_response)
    source_id = flow_data["source"]
    tour_type_id = flow_data["tour_type"]
    tour_quality_id = flow_data["tour_quality"]
    decision_influencer_id = flow_data["decision_influencer"]
    tour_guides_id = flow_data["tour_guides"]
    aspects_enjoyed_id = flow_data["aspects_enjoyed"]
    improvements_id = flow_data["improvements"]
    recommend_id = flow_data["recommend"]
    return_booking_id = flow_data["return_booking"]

    match source_id:
        case "0":
            source = "Online search"
        case "1":
            source = "Social media"
        case "2":
            source = "Referral from a friend/family"
        case "3":
            source = "Advertisement"
        case "4":
            source = "Others"

    match tour_type_id:
        case "0":
            tour_type = "Cultural tour"
        case "1":
            tour_type = "Adventure tour"
        case "2":
            tour_type = "Historical tour"
        case "3":
            tour_type = "Wildlife tour"

    match tour_quality_id:
        case "0":
            tour_quality = "1 - Poor"
        case "1":
            tour_quality = "2 - Below Average"
        case "2":
            tour_quality = "3 - Average"
        case "3":
            tour_quality = "4 - Good"
        case "4":
            tour_quality = "5 - Excellent"

    match decision_influencer_id:
        case "0":
            decision_influencer = "Positive reviews"
        case "1":
            decision_influencer = "Pricing"
        case "2":
            decision_influencer = "Tour destinations offered"
        case "3":
            decision_influencer = "Reputation"

    match tour_guides_id:
        case "0":
            tour_guides = "Knowledgeable and friendly"
        case "1":
            tour_guides = "Knowledgeable but not friendly"
        case "2":
            tour_guides = "Friendly but not knowledgeable"
        case "3":
            tour_guides = "Neither of the two"
        case "4":
            tour_guides = "I didn’t interact with them"

    match aspects_enjoyed_id:
        case "0":
            aspects_enjoyed = "Tourist attractions visited"
        case "1":
            aspects_enjoyed = "Tour guide's commentary"
        case "2":
            aspects_enjoyed = "Group dynamics/interaction"
        case "3":
            aspects_enjoyed = "Activities offered"

    match improvements_id:
        case "0":
            improvements = "Tour itinerary"
        case "1":
            improvements = "Communication before the tour"
        case "2":
            improvements = "Transportation arrangements"
        case "3":
            improvements = "Advertisement"
        case "4":
            improvements = "Accommodation quality"

    match recommend_id:
        case "0":
            recommend = "Yes, definitely"
        case "1":
            recommend = "Yes, but with reservations"
        case "2":
            recommend = "No, I would not"

    match return_booking_id:
        case "0":
            return_booking = "Very likely"
        case "1":
            return_booking = "Likely"
        case "2":
            return_booking = "Undecided"
        case "3":
            return_booking = "Unlikely"

    reply = (
        f"Thanks for taking the survey! Your response has been recorded. This is what we received:\n\n"
        f"*How did you hear about our tour company?*\n{source}\n\n"
        f"*Which type of tour did you recently experience with us?*\n{tour_type}\n\n"
        f"*On a scale of 1 to 5, how would you rate the overall quality of the tour?*\n{tour_quality}\n\n"
        f"*What influenced your decision to choose our tour company?*\n{decision_influencer}\n\n"
        f"*How knowledgeable and friendly were our tour guides?*\n{tour_guides}\n\n"
        f"*What aspects of the tour did you find most enjoyable?*\n{aspects_enjoyed}\n\n"
        f"*Were there any aspects of the tour that could be improved?*\n{improvements}\n\n"
        f"*Would you recommend our tour company to a friend or family member?*\n{recommend}\n\n"
        f"*How likely are you to book another tour with us in the future?*\n{return_booking}"
    )

    user_phone_number = json.loads(request.get_data())["entry"][0]["changes"][0][
        "value"
    ]["contacts"][0]["wa_id"]
    send_message(reply, user_phone_number)

After the extraction, the following send_message function sends the responses to the sender.

def send_message(message, phone_number):
    payload = json.dumps(
        {
            "messaging_product": "whatsapp",
            "to": str(phone_number),
            "type": "text",
            "text": {"preview_url": False, "body": message},
        }
    )

    requests.request("POST", messaging_url, headers=messaging_headers, data=payload)
    print("MESSAGE SENT")
        

La fonction envoie la réponse à l’aide du point de terminaison du message texte de l’API Cloud.

Avant de configurer le webhook dans la console Meta for Developers, assurez-vous que l’application est en cours d’exécution. Par conséquent, utilisez la commande flask --app main run --port 5000 dans votre terminal pour exécuter le code. Le message * Running on http://127.0.0.1:5000 doit s’afficher dans votre terminal si tout est configuré correctement.

Ensuite, exécutez la commande ngrokhttp 5000 dans votre terminal pour obtenir une URL qui correspond à votre application. Copiez ce lien.

Dans la console Meta for Developers, sous WhatsApp dans le panneau de navigation à gauche, cliquez sur Configuration.

Configuration dans WhatsApp

Dans la fiche Webhook, cliquez sur Modifier. Ensuite, dans le champ URL de rappel de la boîte de dialogue, ajoutez l’URL copiée et /webhook. Dans le champ Vérifier le jeton, ajoutez le token de la variable TOKEN de votre fichier .env.

Lorsque vous avez terminé, cliquez sur Vérifier et enregistrez. La boîte de dialogue se ferme. Cliquez sur Gérer et vérifiez le champ des messages. Les informations doivent ressembler à l’image ci-dessous, avec l’URL de rappel, les informations cachées sous Vérifier le jeton et les messages répertoriés sous Champs webhook.

Configuration dans WhatsApp

Le webhook est maintenant prêt.

Exécuter l’application

Dans la nouvelle instance du terminal, exécutez la commande cURL ci-dessous pour créer un flux.

curl --location --request POST 'http://127.0.0.1:5000/create-flow'
        

Dans l’instance de terminal qui affiche la sortie de votre webhook, un message semblable à celui présenté ci-dessous doit apparaître.

Exécuter l’application

Lorsqu’un·e utilisateur·ice envoie un message sur votre numéro WhatsApp, il ou elle reçoit le flux en tant que réponse, comme dans la capture d’écran ci-dessous.

Exemple d’invite pour une enquête avec flux

Il ou elle reçoit une réponse après avoir répondu à l’enquête.

Exemple de réponse à l’enquête avec flux

Tout message envoyé par un·e utilisateur·ice sur votre numéro déclenche le flux. Pour votre cas d’utilisation, personnalisez votre code pour n’envoyer le flux d’enquête que dans des situations spécifiques. Par exemple, après qu’un·e utilisateur·ice a discuté avec votre entreprise.

Conclusion

WhatsApp Flows améliore l’expérience d’utilisation grâce à des interfaces interactives permettant de collecter des données structurées, comme les réponses à une enquête pour une société de tourisme fictive. Il suffit à l’entreprise de créer une application Flask, d’implémenter un code Python à générer, de déployer des flux via l’API WhatsApp Flows, de configurer un webhook pour prendre en compte les messages entrants et d’exécuter l’application pour collecter les réponses à l’enquête.

WhatsApp Flows permet à votre organisation de collecter des données rapidement et simplement, ce qui peut contribuer à améliorer les taux de réussite des interactions avec la clientèle. Utilisez un processus similaire pour mettre en place vos propres enquêtes, offrir une assistance à votre clientèle, l’aider à prendre des rendez-vous, et bien plus encore.

Continuez à explorer le potentiel de WhatsApp Flows. Essayer dès maintenant.