Kembali ke Beranda untuk Developer

Creating Surveys with WhatsApp Flows

6 Maret 2024OlehGafi G & Iryna Wagner

WhatsApp Flows mengoptimalkan dan menyederhanakan cara bisnis Anda mengumpulkan data pelanggan. Organisasi Anda bisa dengan mudah mendapatkan informasi terstruktur dari interaksi dengan pelanggan, yang nantinya, akan merasakan pengalaman pengguna yang positif di WhatsApp. WhatsApp Flows sangat cocok digunakan untuk mengumpulkan data penciptaan prospek, melakukan survei, membantu klien memesan janji temu, mengumpulkan pertanyaan dan masalah pelanggan, serta banyak lagi lainnya.

Istimewanya lagi, Anda bisa menawarkan semua opsi ini kepada pelanggan Anda tanpa harus membuat aplikasi yang rumit dan back end: Cukup gunakan WhatsApp sebagai front end dan gunakan webhook untuk mendapatkan tanggapan sebagai pesan JSON, memproses informasi, dan mengambil data yang Anda butuhkan.

Dengan menggunakan perusahaan fiktif sebagai contoh, tutorial ini akan menjelaskan cara menyiapkan survei pelanggan di WhatsApp menggunakan webhook. Survei ini akan mengumpulkan masukan seperti cara pelanggan menemukan perusahaan dan jenis tur pilihan mereka agar perusahaan bisa melayani klien saat ini dan mendatang dengan lebih baik.

Melakukan Survei dengan WhatsApp Flows

Prasyarat

Untuk melakukannya, pastikan Anda memiliki beberapa hal berikut:

Prosesnya

  1. Buat aplikasi Flask.
  2. Tulis kode Python untuk membuat dan menerbitkan Flows menggunakan WhatsApp Flows API. Kode Python juga akan mengirim Flow yang diterbitkan menggunakan Cloud API.
  3. Buat webhook untuk mendengarkan pesan obrolan.
  4. Jalankan aplikasi.

Jika ingin melihat pratinjau proyek, Anda bisa melihat kode lengkapnya.

Membuat Survei dengan WhatsApp Flows API

Ada dua cara untuk membuat Flow: menggunakan Flow Builder UI atau Flows API. Tutorial kali ini akan menggunakan Flows API untuk menyiapkan survei secara terprogram.

Untuk membuat Flow yang menggunakan data dinamis dari server Anda, Anda bisa membuat endpoint yang menghubungkan survei ke server milik Anda. Dengan menggunakan endpoint, Anda bisa mengontrol logika navigasi antara layar Flow, mengisi data Flow dari server Anda, atau menampilkan/menyembunyikan komponen di layar berdasarkan interaksi pengguna.

Contoh Flow survei yang akan dibahas tidak menggunakan endpoint apa pun karena tidak ada pertukaran data dinamis antara survei ini dan server. Anda akan menggunakan webhook obrolan untuk mendapatkan informasi dari survei. Selain itu, Anda bisa melampirkan Flows ke template pesan di Pengelola WhatsApp.

Membuat Aplikasi Flask

Pertama, buat aplikasi Flask untuk berinteraksi dengan Flows API. Jalankan perintah berikut di terminal Anda untuk membuat lingkungan virtual.

python -m venv venv
        

Lalu, gunakan perintah di bawah ini untuk mengaktifkan lingkungan tersebut.

source venv/bin/activate
        

Selanjutnya, gunakan perintah berikut untuk menginstal paket yang diperlukan.

pip install requests flask python-dotenv
        

Anda akan menggunakan Flask untuk membuat rute dan berinteraksi dengan Flows API, meminta untuk mengirimkan permintaan HTTP, dan python-dotenv untuk memuat variabel lingkungan.

Sekarang, buat file lingkungan bernama .env dan tempel informasi berikut.

VERIFY_TOKEN =
ACCESS_TOKEN =
WHATSAPP_BUSINESS_ACCOUNT_ID =
PHONE_NUMBER_ID =
        

Tetapkan nilai berdasarkan informasi akun developer Anda. Anda bisa menggunakan string apa pun untuk VERIFY_TOKEN. Variabel WHATSAPP_BUSINESS_ACCOUNT_ID dan PHONE_NUMBER_ID adalah pengidentifikasi unik akun Anda yang dibuat otomatis oleh Meta. The ACCESS_TOKEN diperlukan untuk mengautentikasi dan mengotorisasi permintaan API.

Untuk mengakses informasi ini dari dasbor aplikasi Meta Anda, klik WhatsApp > Penyiapan API di panel navigasi kiri seperti pada cuplikan layar di bawah ini.

Preview API setup

Terakhir, di direktori yang sama, buat file bernama main.py untuk menampung logika Python dalam membuat Flows dan webhook.

Membuat Flow

Untuk membuat Flow, pertama-tama, tambahkan paket ke main.py.

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

Selanjutnya, tambahkan snippet kode berikut ke main.py untuk menginisialisasi variabel. Snippet juga akan memulai Flask dan memanggil metode load_dotenv() untuk membantu memuat variabel.

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}",
}
        

Kemudian, tambahkan rute berikut untuk menangani pembuatan 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)

Fungsi tersebut akan memanggil endpoint Flows (flow_base_url) sekaligus meneruskan payload (flow_creation_payload) yang berisi nama dan kategori Flow. Nilai yang bisa digunakan untuk kategori ini adalah: SIGN_UP, SIGN_IN, APPOINTMENT_BOOKING, LEAD_GENERATION, CONTACT_US, CUSTOMER_SUPPORT, SURVEY, atau OTHER.

Ganti <FLOW-NAME> dengan nama yang diinginkan —misalnya, survey_flow.

Setelah membuat Flow, kode akan mengekstrak created_flow_id untuk mengunggah isi JSON.

Mengunggah Komponen JSON Flow

Buat file survey.json dengan konten ini. JSON berisi struktur Flow.

Lalu, tempel kode berikut ke file main.py tersebut.

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())

Fungsi itu akan mengunggah data JSON dari survey.json ke endpoint aset Flow.

Dalam snippet kode di bawah ini, saat pengguna memicu tindakan klik, ini akan memulai on-click-action yang mengambil data dalam payload. Kolom "name": "complete" menunjukkan bahwa Flow selesai. Flow akan ditutup dan payload akan dikirim ke server webhook Anda.

...
"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}"
    }
}

...
        

Nilai dalam objek payload bisa berhubungan dengan komponen Flow (menyerupai nama elemen dalam formulir HTML) atau objek data. Kunci yang terkait dengan nilai payload ini disebut nama, mirip dengan cara Anda menetapkan variabel dalam bahasa pemrograman.

Elemen data-source juga memuat ID yang bertindak sebagai kunci untuk nilai tersebut. Kode mengirimkan ID ini untuk pilihan. Misalnya, jika pengguna memilih Likely untuk data-source di bawah, kode itu akan mengirimkan 1. Setelah menerima data, Anda bisa mencocokkan sumber data.

...
{
    "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 memilih satu layar yang memuat sembilan pertanyaan dengan pilihan ganda, seperti di bawah ini.

Survey flow

Anda bisa mempelajari detail elemen JSON dalam dokumentasi developer.

Menerbitkan Flow

Selanjutnya, tempel fungsi di bawah ini ke file main.py untuk menambahkan logika untuk menerbitkan Flow. Flow yang diterbitkan sudah siap diproduksi sehingga Anda tidak akan bisa melakukan perubahan lebih lanjut.

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)
        

Fungsi ini memanggil endpoint penerbitan saat meneruskan ID Flow.

Mengirimkan Flow

Tempel fungsi berikut ke dalam file main.py untuk mengirimkan Flow ke pengguna WhatsApp. Fungsi ini akan memanggil endpoint pesan Cloud API saat meneruskan payload Flow.

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")
        

Payload Flow berisi detail Flow. Kolom action.parameters.flow_token memungkinkan Anda meneruskan pengidentifikasi unik untuk pesan Flow yang akan dikirimkan dari klien ke webhook Anda begitu Flow selesai. Untuk tutorial ini, Anda akan menggunakan ID acak (uuid). Kode ini menetapkan action.parameters.flow_action_payload.screen sebagai SURVEY_SCREEN yang merupakan ID layar yang ingin Anda tampilkan ketika pengguna mengklik action.parameters.flow_cta.

Mengatur Webhook

Logika webhook cukup sederhana. Logika ini memiliki dua fungsi, webhook_get dan webhook_post, yang masing-masing menangani permintaan GET dan POST. Kode ini menggunakan permintaan GET saat menambahkan webhook ke aplikasi Meta Anda. Kode ini mengembalikan hub.challenge permintaan jika berhasil. Permintaan POST mencetak payload pesan ke 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)
        

Permintaan POST mengekstrak dan memproses payload pesan. Karena kode ini hanya menangani payload pesan, kode ini akan memunculkan kesalahan saat menangkap payload lainnya. Untuk alasan ini, gunakan pernyataan if untuk memeriksa apakah terdapat isi messages. Setelah memeriksa bahwa terdapat isi JSON messages, pemeriksaan lain dilakukan untuk mengekstrak nomor telepon pengirim saja jika terdapat isi text dalam payload 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)
        

Selain itu, Anda harus menggunakan fungsi bantuan berikut yang disebut flow_reply_processor untuk mengekstrak tanggapan dari Flow dan mengirimkannya kembali kepada pengguna. Karena tanggapan Flow berisi ID opsi yang dipilih saat mengambil data dari RadioButtonsGroups, fungsi itu akan mencocokkan ID dengan nilai string yang sesuai.

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")
        

Fungsi ini mengirimkan tanggapan menggunakan endpoint pesan teks Cloud API.

Aplikasi harus dijalankan sebelum Anda mengonfigurasi webhook di konsol Meta untuk Developer. Oleh karena itu, gunakan perintah flask --app main run --port 5000 di terminal Anda untuk menjalankan kode. Anda akan melihat pesan * Running on http://127.0.0.1:5000 di terminal Anda jika semua penyiapan sudah benar.

Selanjutnya, jalankan perintah ngrokhttp 5000 di terminal Anda untuk mendapatkan URL yang mengarah ke aplikasi Anda. Salin tautan tersebut.

Di konsol Meta untuk Developer, di bawah WhatsApp di panel navigasi kiri, klik Konfigurasi.

WhatsApp Configuration

Di kartu Webhook, klik Edit. Lalu, di kotak dialog pada kolom URL Callback, tambahkan URL yang telah disalin dan tambahkan /webhook. Di kolom Verifikasi token, tambahkan token dari variabel TOKEN file .env.

Setelah selesai, klik Verifikasi dan simpan. Kotak dialog akan tertutup. Klik Kelola dan centang kolom pesan. Informasinya harus mirip dengan gambar di bawah ini, yaitu URL Callback, informasi yang tersembunyi di bawah Verifikasi token, dan pesan yang tercantum di bawah kolom Webhook.

WhatsApp Configuration

Webhook kini sudah siap.

Menjalankan Aplikasi

Di instance terminal baru, jalankan perintah cURL berikut untuk membuat Flow.

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

Di instance terminal yang menampilkan output webhook, Anda akan melihat pesan seperti yang ditampilkan di bawah ini.

Running application

Saat pengguna mengirimkan pesan ke nomor WhatsApp Anda, mereka menerima Flow sebagai balasan, seperti pada cuplikan layar di bawah.

Flow survey prompt example

Mereka menerima tanggapan dengan jawaban mereka setelah menyelesaikan survei.

Flow survey response example

Setiap pesan yang dikirim pengguna ke nomor Anda akan memicu Flow. Untuk kasus penggunaan Anda, sesuaikan kode hanya untuk mengirim Flow survei dalam situasi tertentu, misalnya, setelah pengguna mengobrol dengan bisnis Anda.

Kesimpulan

WhatsApp Flows meningkatkan pengalaman pengguna melalui antarmuka interaktif untuk mengumpulkan data terstruktur, seperti tanggapan survei untuk perusahaan tur hipotesis. Bisnis ini hanya membuat aplikasi Flask, menerapkan kode Python untuk membuat, dan menyebarkan Flows melalui WhatsApp Flows API, menyiapkan webhook untuk mendengarkan pesan masuk, serta menjalankan aplikasi untuk mengaktifkan pengumpulan tanggapan survei.

Dengan WhatsApp Flows, organisasi Anda bisa mengumpulkan data dengan cepat dan mudah, sehingga bisa menaikkan tingkat penyelesaian interaksi pelanggan. Gunakan proses serupa untuk menyiapkan survei Anda sendiri, menawarkan dukungan pelanggan, membantu pelanggan memesan janji temu, dan lainnya.

Terus pelajari potensi WhatsApp Flows. Coba sekarang.