
Платформа WhatsApp Flows позволяет компаниям оптимизировать и упростить сбор данных о клиентах. Вы сможете легко собирать структурированную информацию о клиентах, а они будут получать положительные впечатления от взаимодействия с вашей организацией в WhatsApp. Используйте платформу WhatsApp Flows, чтобы собирать данные для генерации лидов, проводить опросы, помогать клиентам оформлять бронирования, реагировать на их вопросы и жалобы и т. д.
Чтобы предложить все эти услуги клиентам, не нужно создавать отдельное приложение и заниматься бэкенд-разработкой. Просто используйте WhatsApp в качестве фронтенд-платформы и задействуйте Webhook для сбора ответов в качестве JSON-сообщений, обработки информации и извлечения необходимых данных.
Давайте рассмотрим на примере выдуманной компании, как создать опрос для клиентов в WhatsApp с помощью Webhook. В ходе опроса, кроме прочего, можно выяснить, как клиенты узнали о компании и какие типы туров они предпочитают. Так компания сможет оптимизировать обслуживание текущих и будущих клиентов.
Чтобы приступить к работе, убедитесь, что:
Вы также можете просмотреть полный код проекта.
Есть два способа создать сценарий: с помощью Flow Builder UI или Flows API. В этой статье описано, как создавать опрос с помощью программирования и Flows API.
Если вы хотите, чтобы сценарий использовал динамические данные с вашего сервера, создайте конечную точку, которая свяжет опрос с вашим сервером. С помощью конечной точки вы сможете контролировать логику навигации между экранами сценария, извлекать данные сценария со своего сервера, а также показывать и скрывать компоненты на экранах в зависимости от действий пользователя.
В рассматриваемом примере не используется конечная точка, поскольку сценария для опроса и сервер не обмениваются динамическими данными. Для сбора информации из опроса вы будете использовать Webhook чата. Вы также можете прикрепить сценарий к шаблону сообщения в WhatsApp Manager.
Сначала создайте приложение Flask для взаимодействия с Flows API. Выполните в терминале указанную ниже команду, чтобы создать виртуальную среду.
python -m venv venv
Выполните команду ниже, чтобы активировать среду.
source venv/bin/activate
Выполните команду ниже, чтобы установить необходимые пакеты.
pip install requests flask python-dotenv
Вы будете использовать Flask для создания маршрутов и взаимодействия с Flows API, запросы для отправки HTTP-запросов и пакет python-dotenv для загрузки переменных среды.
Теперь создайте файл среды с названием .env и вставьте указанную ниже информацию.
VERIFY_TOKEN =
ACCESS_TOKEN =
WHATSAPP_BUSINESS_ACCOUNT_ID =
PHONE_NUMBER_ID =
Укажите значения на основе информации своего аккаунта разработчика. Для VERIFY_TOKEN можно использовать любую строку. Переменные WHATSAPP_BUSINESS_ACCOUNT_ID и PHONE_NUMBER_ID — это уникальные идентификаторы вашего аккаунта, автоматически сгенерированные Meta. The ACCESS_TOKEN служит для аутентификации и авторизации запросов API.
Чтобы получить доступ к информации с панели вашего приложения Meta, нажмите WhatsApp > Настройка API на панели навигации в левой части экрана, как показано на скриншоте ниже.

Наконец, в том же каталоге создайте файл main.py. Он будет содержать логику Python для создания сценариев и Webhook.
Чтобы создать сценарий, сначала добавьте в файл main.py указанные ниже пакеты.
import os
import uuid
import requests
from dotenv import load_dotenv
from flask import Flask, request, make_response, json
Далее добавьте указанный ниже фрагмент кода в файл main.py, чтобы инициализировать переменные. Этот фрагмент также инициализирует Flask и вызывает пакет load_dotenv(), чтобы загрузить переменные.
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}",
}
Добавьте указанный ниже маршрут в обработчик, который создаст сценарий.
@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)Функция вызывает конечную точку сценария (flow_base_url) в то время, как передает полезные данные (flow_creation_payload) с названием и категорией сценария. Возможные значения категории: SIGN_UP, SIGN_IN, APPOINTMENT_BOOKING, LEAD_GENERATION, CONTACT_US, CUSTOMER_SUPPORT, SURVEY или OTHER.
Замените <FLOW-NAME> нужным названием, например survey_flow.
После того как код создаст сценарий, он извлечет идентификатор сценария created_flow_id для загрузки его основного текста JSON.
Создайте файл survey.json с этим содержанием. JSON содержит структуру сценария.
Далее вставьте следующий код в файл 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())
Эта функция загрузит данные JSON из файла survey.json в конечные точки объектов сценария.
Во фрагменте кода ниже действие нажатия вызывает функцию on-click-action. Выполняется сбор полезных данных. Поле "name": "complete" указывает на то, что сценарий готов. Он закроется, а полезные данные будут отправлены на сервер вашего 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}"
}
}
...
Значения в объектах полезных данных могут соответствовать компонентам сценария (напоминая названия элементов в HTML-формах) или объектам данных. Ключи, связанные с этими значениями полезных данных, называются именами, как при присвоении переменных в языках программирования.
Элементы data-source также содержат ID, которые выступают в роли ключей к значениям. Код отправляет эти ID в зависимости от выбора пользователя. Например, если пользователь выбирает Likely для элемента data-source ниже, то код отправляет значение 1. Вы можете сопоставить полученную информацию с источниками данных.
...
{
"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"
}
]
}
...
В сценарии есть один экран с девятью вопросами и несколькими вариантами ответа (как показано ниже).

Вы можете узнать больше об элементах JSON в документации для разработчиков.
Далее вставьте функцию ниже в файл main.py, чтобы добавить логику публикации в сценарий. Опубликованный сценарий готов к использованию, поэтому вам не требуется вносить изменения.
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)
Эта функция вызовет конечную точку публикации, передавая при этом ID сценария.
Вставьте указанную ниже функцию в файл main.py, чтобы отправить сценарий пользователю WhatsApp. Эта функция вызовет конечную точку сообщений Cloud API, передавая при этом полезные данные сценария:
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")
В полезных данных сценария содержатся сведения о нем. Поле action.parameters.flow_token позволяет передать уникальный идентификатор для сообщения сценария, которое отправится от клиента в ваш Webhook, как только клиент пройдет сценарий. В рассматриваемом примере мы будем использовать случайный ID (uuid). Код устанавливает экран action.parameters.flow_action_payload.screen в качестве экрана SURVEY_SCREEN. Это ID экрана, который будет появляться, когда пользователь нажмет action.parameters.flow_cta.
Логика Webhook довольно проста. В ней есть две функции: webhook_get и webhook_post, которые управляют запросами GET и POST соответственно. Код использует запрос GET, когда Webhook добавляется в ваше приложение Meta. Если запрос успешный, он возвращает hub.challenge запроса. Запрос POST записывает полезные данные сообщения в терминал.
@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)
Запрос POST извлекает и обрабатывает полезные данные сообщения. Поскольку код работает только с полезными данными сообщения, он возвращает ошибку, если получает другие полезные данные. В связи с этим нужно использовать утверждение if, чтобы проверить наличие основного текста сообщения (messages). После проверки наличия основного текста JSON в сообщении (messages) выполняется другая проверка для извлечения номера телефона отправителя. Это происходит, только если в полезных данных сообщения (messages) есть основной текст (text).
@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)
Также следует использовать вспомогательную функцию flow_reply_processor, чтобы извлечь ответ из сценария и отправить его назад пользователю. Поскольку ответ на вопрос сценария содержит ID выбранного варианта при сборе данных из RadioButtonsGroups, функция сопоставляет ID с соответствующими значениями строк.
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")
Функция отправляет ответ с помощью конечной точки сообщения Cloud API.
Прежде чем настраивать Webhook на консоли Meta for Developers, запустите приложение. Используйте в терминале команду flask --app main run --port 5000, чтобы запустить код. Если всё настроено правильно, в терминале появится сообщение * Running on http://127.0.0.1:5000.
Выполните в терминале команду ngrokhttp 5000, чтобы получить URL, связанный с вашим приложением. Скопируйте эту ссылку.
На консоли Meta for Developers под пунктом WhatsApp на панели навигации в левой части экрана нажмите Конфигурация.

На карточке Webhook нажмите Редактировать. В диалоговом окне в поле URL обратного вызова добавьте скопированный URL и добавьте /webhook. В поле Маркер подтверждения добавьте маркер из переменной TOKEN файла .env.
Когда всё будет готово, нажмите Подтвердить и сохранить. Диалоговое окно закроется. Нажмите Управлять и проверьте поле messages. Информация должна быть похожей на данные на изображении ниже: поле URL обратного вызова, скрытая информация под пунктом Маркер подтверждения и значение messages под пунктом Поля Webhook.

Настройка Webhook завершена.
В новом экземпляре терминала выполните команду cURL (как указано ниже), чтобы создать сценарий.
curl --location --request POST 'http://127.0.0.1:5000/create-flow'
В экземпляре терминала, который показывает вывод данных Webhook, появится сообщение, похожее на приведенное ниже.

Когда пользователь отправляет сообщение на ваш номер WhatsApp, он получает сценарий "Опрос" в качестве ответа, как показано на скриншоте ниже.

После прохождения опроса пользователь получит сообщение со своими ответами.

Любое сообщение, которое отправит пользователь на ваш номер, вызовет сценарий опроса. В вашем случае нужно кастомизировать код так, чтобы опрос отправлялся только в определенных ситуациях, например после завершения беседы между клиентом и компанией.
WhatsApp Flows дает возможность использовать интерактивные интерфейсы для сбора информации пользователей — например, через опрос вымышленной туристической компании. Этот процесс улучшает впечатление клиентов от взаимодействия с вашей компанией. Компании просто нужно создать приложение Flask, внедрить код Python для генерации и развертывания сценария опроса через WhatsApp Flows API, настроить Webhook для прослушивания входящих сообщений и запустить приложение для сбора ответов на опрос.
WhatsApp Flows позволяет организации быстро и просто собирать данные, что может повысить показатель завершения взаимодействий с клиентами. Подобным образом можно создать собственный опрос, предлагать поддержку клиентам, помогать им совершать бронирования и многое другое.
Продолжайте исследовать возможности WhatsApp Flows. Скорее попробуйте этот инструмент.
Подпишитесь на ежемесячную рассылку Meta for Developers.