Zurück zu den Neuigkeiten für Entwickler

Terminvereinbarung mit WhatsApp Flows: Ein Node.js-Backend erstellen

27. Februar 2024VonGafi G und Iryna Wagner

Mit WhatsApp Flows kannst du interaktive Nachrichten erstellen, über die Nutzer*innen bestimmte Handlungen direkt auf WhatsApp abschließen können. Mit Flows kannst du Bildschirme für die Nutzer*innen-Interaktion erstellen. Du kannst beispielsweise einfache Eingabeformulare erstellen, um Leads zu erfassen oder Bewertungen einzuholen. Daneben kannst du für die Terminvereinbarung komplexe Flows mit mehreren Bildschirmen erstellen.

In diesem Leitfaden lernst du, eine Node.js-App zu entwickeln, über die Nutzer*innen per WhatsApp Flows einen Termin vereinbaren können. Dazu erstellst du auf der WhatsApp Business Platform einen Flow und konfigurierst anschließend einen Webhook, um die Flow-Antwort zu empfangen und den Termin zu vereinbaren.

Voraussetzungen

Das benötigst du für dieses Tutorial:

Einen WhatsApp Flow erstellen

Es gibt zwei Möglichkeiten, einen WhatsApp Flow zu erstellen – mit dem Flows Builder im WhatsApp Manager oder der Flows API. Dieses Tutorial nutzt den Flows Builder.

Flow erstellen

Wähle links im Dashboard des WhatsApp Managers Kontofunktionen aus. Klicke dann auf Flows.

Grafik: WhatsApp Manager.

Klicke oben rechts auf Flow erstellen.

Grafik: Flow erstellen.

Es erscheint ein Dialog, in dem du folgende Details des Termin-Flows eingibst:

  • Name –Gib Terminvereinbarung ein oder einen anderen Namen, der dir gefällt.
  • Kategorien – Wähle Terminbuchung aus.
  • Vorlage – Wähle Termin vereinbaren aus. Wir verwenden diese Vorlage, da sie die erforderlichen Elemente für eine Terminvereinbarung enthält. Dazu zählen etwa Bildschirme für die Termindetails, für die Eingabe von Nutzer*innendetails, für eine Zusammenfassung des Termins und für die Nutzungsbedingungen des Unternehmens. Du kannst die Vorlage weiter an deine Bedürfnisse anpassen.
Grafik: Einen Termin buchen.

Klicke auf Senden, um den Flow zu erstellen.

Rechts in der Flow UI kannst du eine Vorschau des Flows anzeigen. Auf dem Buchungsbildschirm kann der*die Nutzer*in Termindetails wie Ort und Datum auswählen. Auf dem Detailbildschirm gibt er*sie seine*ihre Kontaktinformationen ein. Der Zusammenfassungsbildschirm liefert eine Übersicht über den Termin. Der letzte Bildschirm enthält die Nutzungsbedingungen des Unternehmens.

Während du den Flow bearbeitest, bleibt er im Entwurfsmodus. Du kannst ihn nur zu Testzwecken mit deinem Team teilen. Um ihn einer größeren Zielgruppe zugänglich zu machen, musst du ihn veröffentlichen. Nach der Veröffentlichung kannst du den Flow allerdings nicht mehr bearbeiten. Du musst für diesen Termin-Flow noch eine Endpunkt-URL hinzufügen. Fahre also vorerst im Entwurfsmodus fort und konfiguriere im nächsten Schritt den Endpunkt.

Flow-Endpunkt konfigurieren

WhatsApp Flows lassen sich mit einem externen Endpunkt verknüpfen. Dieser Endpunkt kann dynamische Daten für deinen Flow liefern und das Routing steuern. Außerdem empfängt er Nutzer*innen-Antworten aus dem Flow.

Zu Testzwecken dient in diesem Artikel Glitch als Endpunkt-Host. Die Verwendung von Glitch ist optional und nicht für Flows erforderlich. Du kannst den Endpunkt-Code von GitHub klonen und in deiner bevorzugten Umgebung ausführen.

Rufe den Endpunkt-Code in Glitch auf und remixe ihn, um deine individuelle Domain zu erzeugen. Klicke dazu oben auf der Seite auf Remix. Rechts auf der Glitch-Seite wird im Eingabe-Element als Platzhalter eine individuelle Domain angezeigt.

Sehen wir uns den Code an, bevor es weitergeht. Im src-Verzeichnis befinden sich vier JavaScript-Dateien: encryption.js, flow.js, keyGenerator.js und server.js. Die erste Datei ist server.js. Sehen wir sie uns also auch zuerst an.

server.js

Die Datei server.js konfiguriert zunächst die Express-Anwendung zur Verwendung der express.json-Middleware, um eingehende JSON-Anfragen zu parsen. Anschließend lädt sie die erforderlichen Umgebungsvariablen für den Endpunkt.

const { APP_SECRET, PRIVATE_KEY, PASSPHRASE, PORT = "3000" } = process.env;

APP_SECRET wird zur Signaturverifizierung verwendet. So wird überprüft, ob eine Nachricht per WhatsApp gesendet wurde und entsprechend sicher verarbeitet werden kann. Diesen App-Geheimcode fügst du zur .env-Datei hinzu.

Rufe dein App-Dashboard bei Meta for Developers auf, um deinen App-Geheimcode (APP_SECRET) abzurufen. Wähle dazu unter App-Einstellungen links im Navigationsbereich Allgemeines aus. Klicke unter App-Geheimcode auf Anzeigen und kopiere den Code. Kehre dann zurück zu Glitch, öffne die .env-Datei und erstelle eine Variable mit dem Namen APP_SECRET und dem Wert, den du gerade kopiert hast.

PRIVATE_KEY dient dazu, eingehende Nachrichten zu entschlüsseln. Der private Schlüssel wird mithilfe der PASSPHRASE verifiziert. Neben dem privaten Schlüssel benötigst du auch den zugehörigen öffentlichen Schlüssel, den du später hochladen wirst. Verwende an dieser Stelle niemals die privaten Schlüssel deiner Produktionskonten. Erstelle zum Testen auf Glitch einen temporären privaten Schlüssel und ersetze ihn erst in deiner eigenen Infrastruktur durch den Produktionsschlüssel.

Führe im Glitch-Terminal den folgenden Befehl aus, um das Paar aus öffentlichem und privatem Schlüssel zu generieren. Ersetze <your-passphrase> durch deine eigene Passphrase. Klicke unten auf der Seite auf den Tab TERMINAL, um das Glitch-Terminal aufzurufen.

node src/keyGenerator.js <your-passphrase>

Kopiere die Passphrase und den privaten Schlüssel und füge sie in die .env-Datei ein. Klicke in der linken Seitenleiste auf die .env-Datei und dann oben auf ✏️ Plain text (Klartext). Nimm keine Änderungen direkt über die UI vor, da hierbei die Formatierung deines Schlüssels verloren gehen kann.

Nachdem du die Umgebungsvariablen festgelegt hast, kopiere den öffentlichen Schlüssel, den du generiert hast, und lade ihn per Graph API hoch.

Die Datei server.js umfasst auch einen POST-Endpunkt, der verschiedene Schritte ausführt:

  • Er überprüft, ob ein privater Schlüssel vorhanden ist:
       if (!PRIVATE_KEY) { throw new Error('Private key is empty. Please check your env variable "PRIVATE_KEY".'); }
  • Er validiert die Anfragesignatur mithilfe der isRequestSignatureValid-Funktion am Ende der Datei:
if(!isRequestSignatureValid(req)) { // Return status code 432 if request signature does not match. // To learn more about return error codes visit: https://developers.facebook.com/docs/whatsapp/flows/reference/error-codes#endpoint_error_codes return res.status(432).send(); }
  • Er entschlüsselt eingehende Nachrichten mithilfe der decryptRequest-Funktion aus der encryption.js-Datei:
      let decryptedRequest = null; try { decryptedRequest = decryptRequest(req.body, PRIVATE_KEY, PASSPHRASE); } catch (err) { console.error(err); if (err instanceof FlowEndpointException) { return res.status(err.statusCode).send(); } return res.status(500).send(); } const { aesKeyBuffer, initialVectorBuffer, decryptedBody } = decryptedRequest; console.log("💬 Decrypted Request:", decryptedBody);
  • Er bestimmt, welcher Flow-Bildschirm dem*der Nutzer*in angezeigt wird. Die Funktion getNextScreen sehen wir uns später noch im Detail an.

const screenResponse = await getNextScreen(decryptedBody);

       console.log("👉 Response to Encrypt:", screenResponse);
  • Er verschlüsselt die Antwort an den*die Nutzer*in:
res.send(encryptResponse(screenResponse, aesKeyBuffer, initialVectorBuffer));

encryption.js

Diese Datei dient dem Schutz der Nachrichten und enthält die Logik für die Ver- bzw. Entschlüsselung. Die genaue Funktionsweise wird in diesem Tutorial nicht näher erläutert.

keyGenerator.js

Mit dieser Datei werden der private und der öffentliche Schlüssel erstellt, die du vorher gesehen hast. Wie bei der encryption.js-Datei konzentrieren wir uns in diesem Tutorial auch bei der keyGenerator.js-Datei nicht auf die genaue Funktionsweise.

flow.js

Diese Datei enthält die Logik zur Verarbeitung des Flows. Sie beginnt mit dem Objekt SCREEN_RESPONSES. Das Objekt enthält die Screen-IDs mit ihren jeweiligen Details, etwa die voreingestellten Daten für den Datenaustausch. Dieses Objekt wird im Flow Builder unter  > Endpunkt > Ausschnitte > Antworten generiert. Im selben Objekt findest du außerdem die ID SUCCESS, die nach erfolgreichem Abschluss des Flows an das Client-Gerät zurückgesendet wird. Damit wird der Flow geschlossen.

Die Funktion getNextScreen enthält die Logik, die bestimmt, welche Flow-Daten der Endpunkt dem*der Nutzer*in anzeigt. Dazu extrahiert sie zunächst die erforderlichen Daten aus der entschlüsselten Nachricht.

const { screen, data, version, action, flow_token } = decryptedBody;

WhatsApp Flows-Endpunkte erhalten üblicherweise drei Anfragen:

Die jeweiligen Details findest du in der Endpunkt-Dokumentation.

Die Funktion wickelt die Systemdiagnose und Fehlermeldungen mithilfe von if-Bedingungen ab und gibt entsprechende Antworten aus, wie im nachstehenden Ausschnitt zu sehen:

// handle health check request if (action === "ping") { return { version, data: { status: "active", }, }; } // handle error notification if (data?.error) { console.warn("Received client error:", data); return { version, data: { acknowledged: true, }, }; }
        

Klickt ein*e Nutzer*in auf den Call-to-Action-Button (CTA) des Flows, wird eine INIT-Handlung ausgelöst. Diese Handlung gibt den Bildschirm zur Terminvereinbarung und die entsprechenden Daten aus. Sie deaktiviert außerdem die Dropdowns für Ort, Datum und Zeit, um sicherzustellen, dass alle Felder ausgefüllt werden.

Beispielsweise wird das Datums-Dropdown erst aktiv, nachdem ein Ort ausgewählt wurde. Felder werden (de-)aktiviert, wenn eine data_exchange-Anfrage empfangen wird.

// handle initial request when opening the flow and display APPOINTMENT screen if (action === "INIT") { return { ...SCREEN_RESPONSES.APPOINTMENT, data: { ...SCREEN_RESPONSES.APPOINTMENT.data, // these fields are disabled initially. Each field is enabled when previous fields are selected is_location_enabled: false, is_date_enabled: false, is_time_enabled: false, }, }; }

Bei data_exchange-Handlungen wird mithilfe einer Switch-Struktur und anhand der Screen-ID bestimmt, welche Daten zurückzusenden sind. Lautet die Screen-ID APPOINTMENT, werden die Dropdowns immer nur aktiviert, nachdem im vorigen Feld eine Auswahl getroffen wurde.

// Each field is enabled only when previous fields are selected is_location_enabled: Boolean(data.department), is_date_enabled: Boolean(data.department) && Boolean(data.location), is_time_enabled: Boolean(data.department) && Boolean(data.location) && Boolean(data.date)

Auf dem DETAILS-Bildschirm werden die Titel von Datenobjekten wie Ort und Abteilung aus dem Objekt SCREEN_RESPONSES.APPOINTMENT.data extrahiert. Der Code rechnet mit einem gültigen Match. Er kann also einen Fehler ausgeben, falls kein passendes Objekt vorhanden ist.

Nun sehen wir uns eine Instanz des Standort-Objekts an. Die Auswahl eines bestimmten Standort-Objekts erfolgt durch Abgleich der id-Eigenschaft des Objekts im Array mit dem Wert von data.location.

const departmentName = SCREEN_RESPONSES.APPOINTMENT.data.department.find( (dept) => dept.id === data.department ).title; const locationName = SCREEN_RESPONSES.APPOINTMENT.data.location.find( (loc) => loc.id === data.location ).title; const dateName = SCREEN_RESPONSES.APPOINTMENT.data.date.find( (date) => date.id === data.date

).title;

Die Werte werden anschließend verknüpft und mit der Antwort zurückgegeben, um den Bildschirm ZUSAMMENFASSUNG zu rendern.

const appointment = `${departmentName} at ${locationName} ${dateName} at ${data.time}`; const details = `Name: ${data.name} Email: ${data.email} Phone: ${data.phone} "${data.more_details}"`; return { ...SCREEN_RESPONSES.SUMMARY, data: { appointment, details, // return the same fields sent from client back to submit in the next step ...data, }, };
        

Nachdem der ZUSAMMENFASSUNG-Bildschirm durch den Client gesendet wird, erhält das Client-Gerät eine Antwort zur Bestätigung des erfolgreichen Abschlusses des Flows. flow_token ist eine individuelle ID, die du bestimmen kannst, wenn du den Flow an eine*n Nutzer*in sendest.

// send success response to complete and close the flow return { ...SCREEN_RESPONSES.SUCCESS, data: { extension_message_response: { params: { flow_token, }, }, }, };

Für den Bildschirm NUTZUNGSBEDINGUNGEN erfolgt kein Datenaustausch, der Endpunkt ist also nicht beteiligt.

Endpunkt zum Flow hinzufügen

Klicke oben rechts auf der Glitch-Seite auf das Menü-Symbol und wähle Copy Link (Link kopieren) aus, um die URL zu kopieren. Du kannst auch oben rechts auf Share (Teilen) klicken, um den Link abzurufen.

Rufe nun den Flow-Editor auf. Oben im Editor siehst du ein braunes Banner. Klicke dort auf Einrichten.

Es wird ein Pop-up angezeigt, über das du die Endpunkt-URL, die Telefonnummer des Unternehmens und die App auf Meta for Developers konfigurieren kannst. Nachdem du die Konfiguration angepasst hast, führe eine Systemdiagnose durch. Führe zunächst die interaktive Vorschau aus und wähle in den Einstellungen unter Daten auf erstem Bildschirm anfordern die Option Daten anfordern aus. Dabei wird eine Anfrage an den Endpunkt gesendet, um Daten für den ersten Bildschirm abzurufen. Es wird verifiziert, dass der Endpunkt verfügbar ist und du eine Systemdiagnose implementiert hast.

Klicke dann zum Veröffentlichen auf das Menü-Symbol () und wähle Veröffentlichen aus. Dabei wird eine Systemdiagnose-Anfrage mit action === "ping" an deinen Endpunkt gesendet, um vor der Veröffentlichung sicherzustellen, dass der Endpunkt korrekt eingerichtet ist.

Grafik: Endpunkt.

Flow testen

Nachdem du die Konfiguration abgeschlossen hast, aktiviere in der WhatsApp Builder UI erneut die interaktive Vorschau, um den Flow zu testen. Wähle im angezeigten Pop-up die Telefonnummer aus und unter Daten auf erstem Bildschirm anfordern die Option Daten anfordern. Schließe den Flow mit dem X-Button, um ihn ab dem CTA-Button erneut zu testen.

Öffne über den Tab LOGS (Protokolle) das Glitch-Protokoll. Klicke auf Clear (Löschen), um es zu leeren. Kehre dann zur Vorschau in der WhatsApp Builder UI zurück. Klicke auf Vorschau des Flows ansehen. Dort siehst du eine Ansicht wie diese:

Grafik: Flow-Vorschau.

Gehe nun zurück zu den Glitch-Protokollen. Hier siehst du eine INIT-Handlung, den Flow-Token und andere Details der entschlüsselten Anfrage. Außerdem wird an den Nutzer*innen-Flow eine Verschlüsselungsantwort gesendet, sobald das Abteilungs-Dropdown ausgewählt wurde.

Grafik: Entschlüsselte Anfrage.

Wähle nun eine Abteilung aus. Du siehst, wie für is_location_enabled jetzt true konfiguriert ist und die Handlung zu data_exchange wechselt.

Grafik: data_exchange.

Teste weiter den Flow und beobachte die Änderungen an den Daten in den Glitch-Protokollen. Ähnliche Protokolle werden auch generiert, wenn Nutzer*innen über ihre Mobilgeräte mit dem Flow interagieren.

Im nächsten Abschnitt erstellst du einen Webhook, der nach erfolgreicher Terminvereinbarung eine Bestätigung an den*die Nutzer*in sendet.

Webhook einrichten

Wenn ein*e Nutzer*in den Flow abschließt, wird an den abonnierten Webhook eine Nachricht gesendet, die den Flow als abgeschlossen markiert. Über diesen Webhook bestätigst du dann dem*der Nutzer*in per Chat-Nachricht, dass die Terminbuchung erfolgreich war. Wie beim Endpunkt verwenden wir auch hier zu Testzwecken Glitch. Du kannst den Code hier abrufen und remixen.

Die Verwendung von Glitch ist optional und nicht für Flows erforderlich. Du kannst den Webhook-Code von GitHub klonen und in deiner bevorzugten Umgebung ausführen.

Umgebungsvariablen festlegen

Öffne die .env-Datei auf Glitch, um die Umgebungsvariablen festzulegen. Gib bei VERIFY_TOKEN einen beliebigen String an, bei FLOW_ID die ID deines Flows und bei GRAPH_API_TOKEN den Zugriffsschlüssel deines WhatsApp-Unternehmenskontos. Den Zugriffsschlüssel kannst du über das Dashboard deiner App auf Meta for Developers abrufen. Klicke dazu im linken Navigationsbereich unter WhatsApp auf API-Setup.

Grafik: API-Setup.

Klicke auf der angezeigten Seite unter Temporärer Zugriffsschlüssel auf den Button Kopieren. Füge den Schlüssel in deiner .env-Datei ein.

Webhook im Meta-Dashboard abonnieren

Klicke in deinem Konto auf Meta for Developers in der linken Navigationsleiste unter WhatsApp auf das Menü Konfiguration.

Grafik: Konfiguration.

Klicke in der Karte Webhook auf Bearbeiten. Füge im angezeigten Dialog die kopierte Glitch-URL ein und füge /webhook im Feld Callback-URL hinzu. Füge im Feld Verifizierungstoken den Token aus der TOKEN-Variable deiner .env-Datei hinzu. Wenn du fertig bist, klicke auf Bestätigen und speichern. Anschließend schließt sich der Dialog und du kehrst zum Hauptbildschirm zurück. Klicke auf Verwalten und wähle das Feld Nachrichten aus. Dein Webhook ist jetzt einsatzbereit.

Webhook-Code im Detail

Der Code enthält zwei Routen: POST /webhook und GET /webhook. Die GET-Route verarbeitet Webhook-Verifizierungsanfragen. Dazu wird der angegebene Schlüssel mit einem vordefinierten Verifizierungsschlüssel abgeglichen und als Antwort der jeweilige Statuscode und ein Challenge-Token ausgegeben.

const verify_token = process.env.VERIFY_TOKEN; // Parse params from the webhook verification request let mode = req.query["hub.mode"]; let token = req.query["hub.verify_token"]; let challenge = req.query["hub.challenge"]; if (mode && token) { if (mode === "subscribe" && token === verify_token) { console.log("WEBHOOK_VERIFIED"); res.status(200).send(challenge); } else { res.sendStatus(403); } }

Die POST /webhook-Route verarbeitet eingehende Webhook-Benachrichtigungen. Webhook-Anfragen können verschiedene Payloads umfassen. Der nachstehende Code liest also die Nachricht und Unternehmens-Telefonnummer sicher aus, indem er auf die Anfragefelder zugreift, falls diese nicht definiert sind.

const message = req.body.entry?.[0]?.changes[0]?.value?.messages?.[0]; const business_phone_number_id =

req.body.entry?.[0].changes?.[0].value?.metadata?.phone_number_id;

Dann wird die eingehende Anfrage auf eine "text"-Nachricht geprüft, die das Wort „Termin“ enthält. Ist der Begriff in der Nachricht enthalten, wird der Flow an den*die Nutzer*in gesendet. Die Flow-Nachricht wird mit flow_action: "data_exchange," gesendet. Der Flow sendet also beim Start eine INIT-Anfrage an den Endpunkt, um den ersten Bildschirm und die zugehörigen Daten abzurufen.

if ( message.type === "text" && // for demo purposes, send the flow message whenever a user sends a message containing "appointment" message.text.body.toLowerCase().includes("appointment") ) { // send flow message as per the docs here https://developers.facebook.com/docs/whatsapp/flows/gettingstarted/sendingaflow#interactive-message-parameters await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", to: message.from, type: "interactive", interactive: { type: "flow", header: { type: "text", text: "Hello there 👋", }, body: { text: "Ready to transform your space? Schedule a personalized consultation with our expert team!", }, footer: { text: "Click the button below to proceed", }, action: { name: "flow", parameters: { flow_id: FLOW_ID, flow_message_version: "3", // replace flow_token with a unique identifier for this flow message to track it in your endpoint & webhook flow_token: "<FLOW_TOKEN_PLACEHOLDER>", flow_cta: "Book an appointment", flow_action: "data_exchange", }, }, }, }, }); } ...

Falls die Art der eingehenden Nachricht nicht "text" ist, überprüft der Code, ob die Art der Nachricht "interactive" ist. Ist es eine interaktive "nfm_reply"-Antwort, handelt es sich bei der eingehenden Nachricht um eine Flow-Antwort. Anschließend erhält der*die Nutzer*in eine Nachricht zur Bestätigung der erfolgreichen Terminbuchung.

... if ( message.type === "interactive" && message.interactive?.type === "nfm_reply" ) { // send confirmation message await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", to: message.from, text: { body: "You've successfully booked an appointment" }, }, }); } ...

Die eingehenden Nachrichten werden dann als gelesen markiert, der*die Nutzer*in sieht also blaue Häkchen.

... // mark incoming message as read await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", status: "read", message_id: message.id, }, }); ...
        

Nutzungserlebnis

In diesem Beispiel erhältst du von dem*der Nutzer*in eine Nachricht mit dem Wort „Termin“. Er*Sie erhält als Antwort die Flow-Nachricht. Du kannst auch festlegen, den Flow nach einer anderen Interaktion zu senden oder als Nachrichtenvorlage.

Der*Die Nutzer*in erhält eine Flow-Nachricht mit einem CTA-Button zur Terminbuchung, wo er*sie ihre Details eingeben kann. Nach Abschluss des Flows erhält er*sie eine Bestätigungsnachricht.

Grafik: Flow an Nutzer*in senden.

In diesem Guide haben wir dir gezeigt, wie man einen WhatsApp Flow einrichtet, um die Terminvereinbarung zu erleichtern. Du hast mit der Flow Builder UI ein Formular erstellt, um Termindetails von Nutzer*innen zu erfassen.

Dank Flows musst du Nutzer*innen für die Terminvereinbarung nicht mehr zu externen Websites leiten und verbesserst so das Nutzungserlebnis. Dieser einfache Prozess ermöglicht Terminbuchungen direkt über WhatsApp. Neben Terminbuchungen eignen sich WhatsApp Flows auch, um Feedback zum Kund*innenservice einzuholen und für die Anmeldung zu Promotions oder Mailing-Listen. Außerdem bleibst du mit WhatsApp Flows flexibel, da du externe APIs oder andere Apps mit deinem Endpunkt verknüpfen kannst.

Mit der Flow Builder UI ist es ganz einfach, WhatsApp Flows zu erstellen. Alternativ kannst du auch die Flow API nutzen, um Flows programmgestützt zu erstellen. Weitere Informationen findest du in der Dokumentation zu WhatsApp Flows.