
WhatsApp Flows 优化并简化了企业收集客户数据的方式。你的企业可以从与客户的互动中轻松获取结构化信息,而客户则可在 WhatsApp 中获得良好的用户体验。WhatsApp Flows 非常适合用来收集潜在客户数据、开展调查、帮助客户预约、提交客户问题和疑虑等等。
最重要的是,你无需构建复杂的应用程序和后端,即可为客户提供所有这些选项:只需将 WhatsApp 用作前端,然后使用 Webhook 捕获 JSON 消息形式的响应、处理信息和检索所需数据即可。
本教程以一家虚构的公司为例,探讨如何使用 Webhooks 在 WhatsApp 上创建客户调查问卷。调查问卷将收集客户的反馈信息,例如客户是如何发现公司的,以及他们偏爱哪种类型的旅行,这样公司便可更好地为新老顾客提供服务。
要继续操作,请确保你拥有以下内容:
如果你想预览项目,可以查看完整代码。
创建 Flow 有两种方法:使用 Flow Builder UI 或 Flows API。本教程使用 Flows API 以编程方式创建调查问卷。
要构建使用服务器中动态数据的 Flow,你可以创建一个端点,该端点将调查问卷连接到你自己的服务器。你可以使用端点控制多个 Flow 屏幕之间的导航逻辑,从你的服务器填充 Flow 数据,或根据用户互动情况在屏幕上显示/隐藏组件。
将要讨论的调查问卷 Flow 示例不使用任何端点,因为该调查问卷 Flow 与服务器之间没有动态数据交换。你将使用聊天 Webhook 来捕获调查问卷中的信息。此外,你可以通过 WhatsApp 管理工具将 Flows 附加到消息模板。
首先,创建一个 Flask 应用以便与 Flows API 互动。在你的终端运行以下命令来创建虚拟环境。
python -m venv venv
然后,使用以下命令激活环境。
source venv/bin/activate
接下来,使用以下命令安装所需项目包。
pip install requests flask python-dotenv
你将使用 Flask 创建路由并与 Flows API 互动,使用 requests 发送 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 的文件,以包含用于创建 Flows 和 Webhook 的 Python 逻辑。
要创建 Flow,首先需要将以下项目包添加到 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}",
}
然后,添加以下路由来处理 Flow 创建操作。
@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)该函数将调用 Flows 端点 (flow_base_url),同时传入包含名称和 Flow 类别的有效负载 (flow_creation_payload)。此类别的可用值包括:SIGN_UP、SIGN_IN、APPOINTMENT_BOOKING、LEAD_GENERATION、CONTACT_US、CUSTOMER_SUPPORT、SURVEY 或 OTHER。
将 <FLOW-NAME> 替换为你所需的名称 — 例如,survey_flow。
代码创建 Flow 后,它会提取 created_flow_id 以上传其 JSON 正文。
使用这些内容创建一个 survey.json 文件。JSON 包含 Flow 的结构。
然后,将以下代码粘贴到 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 上传到 Flow 资产端点。
在下面的代码片段中,当用户触发点击操作时,该代码片段会启动 on-click-action,捕获有效负载中的数据。"name": "complete" 字段表示 Flow 已完成。它将关闭,有效负载将被发送到你的 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}"
}
}
...
有效负载对象内的值可能对应于 Flow 组件(类似于 HTML 表单中的元素名称)或数据对象。与这些有效负载值关联的键称为名称,类似于在编程语言中分配变量的方式。
data-source 元素还包含充当值键的编号。代码将发送这些编号以供选择。例如,如果用户为下面的 data-source 选择 Likely,则代码将发送 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"
}
]
}
...
该 Flow 有一个包含九道单选题的屏幕,如下所示。

你可以阅读开发者文档,详细了解 JSON 元素。
接下来,将以下函数粘贴到 main.py 文件中以添加发布 Flow 的逻辑。已发布的 Flow 处于可投入使用状态,因此你将无法进行后续更改。
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)
该函数在传入 Flow 编号时将调用发布端点。
将以下函数粘贴到 main.py 文件中,以便将 Flow 发送给 WhatsApp 用户。该函数在传入 Flow 有效负载时将调用云端 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")
Flow 有效负载包含 Flow 详细信息。你可以借助 action.parameters.flow_token 字段传递 Flow 消息的唯一标识符,该消息将在 Flow 完成后从客户端传输到你的 Webhook。在本教程中,你将使用随机编号 (uuid)。该代码将 action.parameters.flow_action_payload.screen 设置为 SURVEY_SCREEN,这是你希望用户点击 action.parameters.flow_cta 后显示的屏幕的编号。
Webhook 逻辑非常简单。它有两个函数:webhook_get 和 webhook_post,分别用来处理 GET 和 POST 请求。将 Webhook 添加到你的 Meta 应用时,代码会使用 GET 请求。成功后,它将返回请求的 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 正文。检查 messages JSON 正文是否存在后,仅当 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 的辅助函数从 Flow 中提取响应并将其发回给用户。由于从 RadioButtonsGroups 捕获数据时 Flow 响应包含所选选项编号,因此该函数会将此编号与相应的字符串值进行匹配。
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")
该函数使用云端 API 短信端点发送响应。
在 Meta 开发者控制台中配置 Webhook 之前,需要运行应用程序。因此,请在终端中使用命令 flask --app main run --port 5000 来运行代码。如果一切设置正确,你应在终端中看到 * Running on http://127.0.0.1:5000 消息。
接下来,在终端中运行命令 ngrokhttp 5000 以获取映射到你应用程序的网址。复制该链接。
在 Meta 开发者控制台左侧导航窗口中的 WhatsApp 下方,点击配置。

在 Webhook 图卡中,点击编辑。然后,在回调网址字段的对话框中,添加复制的网址并附加 /webhook。在验证口令字段中,添加 .env 文件的 TOKEN 变量中的口令。
完成后,点击验证并保存。对话框将关闭。点击管理并检查消息字段。此信息应与下图所示内容类似,其中包含回调网址、验证口令下方的隐藏信息,以及 Webhook 字段下方列出的消息。

Webhook 现已准备就绪。
在新的终端实例中,运行下方的 cURL 命令来创建 Flow。
curl --location --request POST 'http://127.0.0.1:5000/create-flow'
在显示 Webhook 输出的终端实例中,你应该会看到一条类似于下图所示的消息。

当用户给你的 WhatsApp 电话号码发消息时,他们会收到 Flow 作为回复,如下方截图所示。

完成调查问卷后,他们会收到包含答案的回复。

用户给你电话号码发送的任何消息都将触发 Flow。对于你的用例,可自定义代码以仅在特定情况下发送调查问卷 Flow,例如在用户与你聊天之后。
WhatsApp Flows 可通过互动界面收集结构化数据(例如假设的旅游公司的调查问卷回复),以此来增强用户体验。这家公司只需创建一个 Flask 应用,使用 Python 代码来生成,并通过 WhatsApp Flows API 部署 Flows,设置 Webhook 以监听传入消息,并运行应用程序以启用调查问卷回复收集功能。
WhatsApp Flows 可让你的组织快速、轻松地收集数据,这有助于提高客户互动的完成率。你可以使用类似流程创建自己的调查问卷、提供客户支持、帮助他们进行预约等等。
继续探索 WhatsApp Flows 的潜力。马上试试。
订阅以接收 Meta 开发者每月动态。