
WhatsApp Flows 可改善並簡化企業蒐集顧客資料的流程。您的機構可輕鬆地透過顧客互動擷取結構化資訊,從而讓顧客在 WhatsApp 享有更優良的用戶體驗。WhatsApp Flows 特別適合用來蒐集開發潛在顧客資料、執行問卷調查、協助客戶預約服務、提交顧客問題和疑慮等等。
最令人雀躍的是,您可以為顧客提供所有這些選項,而無需構建複雜的應用程式和後端:您只需使用 WhatsApp 作為前端,並採用 Webhook 以 JSON 訊息形式擷取回應、處理資訊並檢索所需資料。
此教學導覽會以一間虛構公司為例,探索如何使用 Webhooks 在 WhatsApp 設定顧客問卷調查。此問卷調查將蒐集各類意見,例如顧客如何發掘到這間公司,以及他們喜歡的巡迴活動類型,以便此公司為現有及未來客戶提供更出色的服務。
若要使用此功能,請確保您具備下列條件:
若您想預覽此專案,可查看完整程式碼。
您可透過兩種方式來建立流程:使用流程建造工具用戶介面 或 Flows API。此教學導覽使用 Flows API 以程式輔助的方式來設定問卷調查。
若要建立流程以使用來自您伺服器的動態資料,您可以建立一個端點,將問卷調查連結至自家伺服器。您可使用此端點來控制各個流程畫面之間的導覽邏輯、填入來自您伺服器的流程資料,或根據用戶互動顯示/隱藏畫面上的元件。
本文的問卷調查流程範例並未使用任何端點,這是因為其與伺服器之間沒有交換動態資料。我們將使用聊天室 Webhook 來擷取問卷調查中的資訊。此外,您可以透過 WhatsApp 管理工具將流程附加至訊息範本。
首先,建立 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 的檔案,以包含用於建立流程和 Webhook 的 Python 邏輯。
若要構建流程,請先將下列套件加入 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)此函數會呼叫 Flows 端點 (flow_base_url),同時傳遞含有名稱和流程類別的裝載 (flow_creation_payload)。可用於類別的值包括:SIGN_UP、SIGN_IN、APPOINTMENT_BOOKING、LEAD_GENERATION、CONTACT_US、CUSTOMER_SUPPORT、SURVEY 或 OTHER。
使用您所需的名稱(例如 survey_flow)來替換 <FLOW-NAME>。
程式碼建立流程後,便會擷取 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 表格中的元素名稱)或資料物件對應。與此等裝載值相關的鍵稱為「name」,其分配方式與您在程式語言中分配變數的方法類似。
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"
}
]
}
...
流程有一個包含 9 條選擇題的畫面,如下所示。

您可以查閱開發人員文件,探索各項 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)
此函數會調用發佈端點,同時傳遞流程編號。
將以下函數貼至 main.py 檔案,以將流程傳送至 WhatsApp 用戶。此函數會呼叫雲端 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。在此教學導覽中,我們會使用隨機編號(uuid)。程式碼將 action.parameters.flow_action_payload.screen 設為 SURVEY_SCREEN,後者是您希望在用戶點擊 action.parameters.flow_cta 時顯示的畫面之編號。
Webhook 邏輯簡明易懂。它有 2 個函數 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 要求會用於擷取並處理訊息裝載。由於此程式碼僅用於處理訊息裝載,因此擷取任何其他裝載時會出現錯誤。因此,您可使用 messages 陳述式來檢查是否存在 if 主體。如果發現存在 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 的協助工具函數,從流程中擷取回覆並將其傳回給用戶。由於從 RadioButtonsGroups 擷取資料時流程回覆會包含用戶所選的選項編號,因此此函數會將編號與相應字串值配對。
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 for Developers 主控台配置 Webhook。因此,請在您的終端使用指令 flask --app main run --port 5000 以執行程式碼。若一切設定正確,您應該會在終端看到 * Running on http://127.0.0.1:5000 訊息。
接下來,在您的終端執行指令 ngrokhttp 5000,以取得與您應用程式相應的網址。複製此連結。
在 Meta for Developers 主控台中,於左側導覽面板中的「WhatsApp」下方,點擊「配置」。

在「Webhook」小卡片中,點擊「編輯」。然後,在「回調網址」欄位中的對話框中,加入所複製的網址並附加 /webhook。在「驗證憑證」欄位中,加入 .env 檔案的 TOKEN 變數所提供的憑證。
完成後,點擊「驗證並儲存」。對話框會隨即關閉。點擊「管理」並檢查「訊息」欄位。此資訊應與下方圖像類似,當中包含「回調網址」、「驗證憑證」下方的隱藏資訊,以及列於「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 的每月最新消息。