Maintainers
Pricing
Enterprise
More
More
Documentation
Try Live
Rankings
Install
Blog
Docs7
Ask
New
Add Docs
WhatsApp Flows Tools
https://github.com/whatsapp/whatsapp-flows-tools
Admin
This repo contains scripts and code examples for building WhatsApp Flows
Tokens:
48,021
Snippets:
297
Trust Score:
8.4
License:
MIT
Update:
3 weeks ago
Context
Chat
Benchmark
53
Latest
Show doc for...
Code
Info
Show Results
Tokens:
Raw
Copy
Link
Tokens:
Raw
Copy
Link
### Start server with flow logic (server.js) Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-endpoint-server.md Standard integration from examples/endpoint/nodejs/*. Reads APP_SECRET, PRIVATE_KEY, PASSPHRASE (default empty), and PORT (default "3000") from env. Handles POST / by validating signature (432 on failure), decrypting the request, getting the next screen, and sending an encrypted response. Logs 'Server is listening on port: ${PORT}' on start. ```javascript // only crypto/flow imports vary per variant; the wiring is exactly this file const { APP_SECRET, PRIVATE_KEY, PASSPHRASE = "", PORT = "3000" } = process.env; app.post("/", async (req, res) => { if (!PRIVATE_KEY) throw new Error('Private key is empty. Please check your env variable "PRIVATE_KEY".'); if (!isRequestSignatureValid(req)) return res.status(432).send(); let decryptedRequest = null; try { decryptedRequest = decryptRequest(req.body, PRIVATE_KEY, PASSPHRASE); } catch (err) { if (err instanceof FlowEndpointException) return res.status(err.statusCode).send(); return res.status(500).send(); } const { aesKeyBuffer, initialVectorBuffer, decryptedBody } = decryptedRequest; const screenResponse = await getNextScreen(decryptedBody); res.send(encryptResponse(screenResponse, aesKeyBuffer, initialVectorBuffer)); }); app.listen(PORT, () => console.log(`Server is listening on port: ${PORT}`)); ``` -------------------------------- ### GET / Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/endpoints.md Liveness/info route. Responds with an HTML snippet: "Nothing to see here.\nCheckout README.md to start." No auth. ```APIDOC ## GET / ### Description Liveness/info route. Responds with an HTML snippet: `"Nothing to see here.\nCheckout README.md to start."` No auth. ### Method GET ### Endpoint / ### Response - **200 OK** - HTML snippet: `"Nothing to see here.\nCheckout README.md to start."` ``` -------------------------------- ### Basic usage of encryptResponse with decryptRequest and getNextScreen Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-endpoint-encryption.md Example from server.js showing how to decrypt an incoming request, get the next screen, and send an encrypted response. Requires the PRIVATE_KEY and PASSPHRASE environment variables. ```javascript import { decryptRequest, encryptResponse } from "./encryption.js"; import { getNextScreen } from "./flow.js"; const { aesKeyBuffer, initialVectorBuffer, decryptedBody } = decryptRequest(req.body, PRIVATE_KEY, PASSPHRASE); const screenResponse = await getNextScreen(decryptedBody); console.log("๐ Response to Encrypt:", screenResponse); res.send(encryptResponse(screenResponse, aesKeyBuffer, initialVectorBuffer)); ``` -------------------------------- ### app.listen Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-echo-webhook.md Starts the HTTP server on the configured PORT and logs a message when listening. ```APIDOC ## app.listen ### Description Binds the server to the port specified by the PORT environment variable and logs a message indicating the server is listening. ### Method LISTEN ### Endpoint N/A (server startup) ### Parameters #### Environment Variables - **PORT** (string) - Required - The port number to listen on. ### Response #### Success Response - **log** (string) - "Server is listening on port: ${PORT}" ``` -------------------------------- ### Run reference server with PRIVATE_KEY and PASSPHRASE Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/go-endpoint.md Starts the Go server with the private key and passphrase environment variables, serving a POST endpoint at http://localhost:3000/. ```bash PRIVATE_KEY="$(cat ./private.pem)" PASSPHRASE="secret" go run . # gin now serves POST http://localhost:3000/ ``` -------------------------------- ### Bootstrap and serve the survey (bash) Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/python-survey-server.md Installs dependencies, prepares .env with required credentials, runs main.py, and triggers the create-flow endpoint. Expects "FLOW CREATED" with HTTP 200. ```bash pip install flask requests python-dotenv # prepare .env: PHONE_NUMBER_ID, VERIFY_TOKEN, ACCESS_TOKEN, WHATSAPP_BUSINESS_ACCOUNT_ID python main.py curl -X POST http://localhost:5000/create-flow # โ "FLOW CREATED" (200) ``` -------------------------------- ### getNextScreen Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-endpoint-flow.md Documentation for the `getNextScreen` function exported from `flow.js` in the Node.js Flow endpoints examples. It describes the function signature, parameters, return values, behavior, error handling, and usage examples. ```APIDOC ## `getNextScreen` ### Description Asynchronously processes a decrypted WhatsApp Flows request envelope and returns the next screen response. It dispatches on the `action` field (`ping`, `INIT`, `data_exchange`) and handles client-reported errors. ### Signature ```js export const getNextScreen = async (decryptedBody) => { ``` ### Parameters - **decryptedBody** (object) - Required - The decrypted request envelope produced by `decryptRequest().decryptedBody`. The function destructures `{ screen, data, version, action, flow_token }` and throws for unhandled `action`/`screen` combinations. ### Return Value `Promise<object>` - A promise resolving to a screen response envelope. Exactly one of: - `{ data: { status: "active" } }` when `action === "ping"` (health-check reply) - `{ data: { acknowledged: true } }` when `data.error` is truthy (client reported an error) - `{ screen, data }` when `action === "INIT"` (first screen) or `action === "data_exchange"` (next screen per the switch) ### Behavior - `action = "ping"` โ `{ data: { status: "active" } }` - `data.error` present โ `{ data: { acknowledged: true } }` - `action = "INIT"` โ `{ screen: "MY_SCREEN", data: { greeting: "Hey there! ๐" } }` (basic variant) - `action = "data_exchange"` โ switch on `screen`: - `"MY_SCREEN"` โ SUCCESS payload with `extension_message_response.params.flow_token` (completes the flow) - default โ falls out of switch - Any other path โ logs error and throws `Error("Unhandled endpoint request. Make sure you handle the request action & screen logged above.")` ### Throws - `Error("Unhandled endpoint request. Make sure you handle the request action & screen logged above.")` when `action` is not `"ping"`/`"INIT"`/`"data_exchange"`, or the `data_exchange` `screen` has no matching `case`. The function first logs the full body via `console.error("Unhandled request body:", decryptedBody)`. ### Example (Basic happy path) ```js import { getNextScreen } from "./flow.js"; import { decryptRequest, encryptResponse } from "./encryption.js"; const { decryptedBody, aesKeyBuffer, initialVectorBuffer } = decryptRequest(req.body, PRIVATE_KEY, PASSPHRASE); const screenResponse = await getNextScreen(decryptedBody); res.send(encryptResponse(screenResponse, aesKeyBuffer, initialVectorBuffer)); ``` ### Example (Error handling) ```js import { getNextScreen } from "./flow.js"; import { encryptResponse } from "./encryption.js"; try { const next = await getNextScreen(decryptedBody); return res.send(encryptResponse(next, aesKeyBuffer, ivBuffer)); } catch (err) { console.error("Router error for action/screen:", decryptedBody.action, decryptedBody.screen); return res.status(500).send(); } ``` ``` -------------------------------- ### GET / Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-echo-webhook.md Info route that returns a static message indicating the server is running and pointing to the README. ```APIDOC ## GET / ### Description Returns a static info message: "Nothing to see here.\nCheckout README.md to start." with HTTP 200 status. ### Method GET ### Endpoint / ### Response #### Success Response (200) - **body** (string) - The static message "Nothing to see here.\nCheckout README.md to start." #### Response Example ``` Nothing to see here. Checkout README.md to start. ``` ``` -------------------------------- ### Run the echo webhook server with env vars Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-echo-webhook.md Starts the Node.js echo webhook server with required environment variables: WEBHOOK_VERIFY_TOKEN, GRAPH_API_TOKEN, and PORT. Subscribe to https://<host>/webhook after deployment. ```bash WEBHOOK_VERIFY_TOKEN="my-verify-token" GRAPH_API_TOKEN="EAAG..." PORT=3000 npm start ``` -------------------------------- ### GET /webhook Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/python-llama-chatbot-server.md Verifies the webhook subscription for the WhatsApp Flows chatbot server. ```APIDOC ## [GET] /webhook ### Description Verifies the webhook subscription by responding to Facebook's verification request. It checks the `hub.mode` and `hub.verify_token` query parameters against the expected values and returns the challenge if valid. ### Method GET ### Endpoint /webhook ### Parameters #### Query Parameters - **hub.mode** (string) - Required - Must be `subscribe` to confirm subscription. - **hub.verify_token** (string) - Required - Must match the `TOKEN` environment variable. - **hub.challenge** (string) - Required - The challenge string to return for verification. ### Response #### Success Response (200) - Returns the value of `hub.challenge` as the response body. #### Error Response (403) - Returns the string `"Success"` with status 403 if verification fails. ### Response Example `<hub.challenge value>` ``` -------------------------------- ### Run the server and chat Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/python-llama-chatbot-server.md Basic setup and run commands for the WhatsApp chatbot server. Requires a .env file with PHONE_NUMBER_ID, TOKEN, and ACCESS_TOKEN. After running, register the webhook URL with TOKEN as the verify token and send "hello" on WhatsApp to test. ```bash pip install flask requests python-dotenv llama-cpp-python # .env: PHONE_NUMBER_ID, TOKEN, ACCESS_TOKEN python main.py # register https://<host>/webhook with TOKEN as verify token; send "hello" on WhatsApp ``` -------------------------------- ### Setup Express and environment variables Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-echo-webhook.md Initializes the Express app with JSON middleware and extracts WEBHOOK_VERIFY_TOKEN, GRAPH_API_TOKEN, and PORT from process.env. These environment variables are documented in configuration.md ยง2. ```javascript const app = express(); app.use(express.json()); const { WEBHOOK_VERIFY_TOKEN, GRAPH_API_TOKEN, PORT } = process.env; ``` -------------------------------- ### FlowEndpointException Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-endpoint-encryption.md Documentation for the FlowEndpointException class, including its constructor parameters, properties, and usage example. ```APIDOC ## FlowEndpointException ### Description Custom error class for WhatsApp Flows endpoint encryption. Thrown when request decryption fails or other endpoint errors occur, carrying an HTTP status code to be returned to the client. ### Constructor `new FlowEndpointException(statusCode, message)` ### Parameters - **statusCode** (number) - Required - HTTP status code assigned to the exception (the server echoes it as the response status) - **message** (string) - Required - Error message (also `err.message` via `Error` superclass) ### Properties - **message** (string) - inherited from `Error` - **name** (string) - `"FlowEndpointException"` (set to `this.constructor.name`) - **statusCode** (number) - HTTP status to return to the client ### Example ```js import { FlowEndpointException } from "./encryption.js"; try { // create a private key that does not match the uploaded public key decryptRequest(req.body, WRONG_PRIVATE_KEY, PASSPHRASE); } catch (err) { console.log(err instanceof FlowEndpointException); // true console.log(err.statusCode); // 421 console.log(err.message); // "Failed to decrypt the request. Please verify your private key." } ``` ``` -------------------------------- ### Basic usage example for keyGenerator.js Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-key-generator.md Generate a fresh key pair with a passphrase. After running, wire the key into the endpoint server .env and upload the public key via the Postman request POST {{base-url}}/{{business-phone-number-id}}/whatsapp_business_encryption with form field business_public_key. ```bash node src/keyGenerator.js "my-secret-passphrase" ``` -------------------------------- ### Error handling example for keyGenerator.js Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-key-generator.md Recreates the script's own guard in consumer tooling. Uses execSync to run the script without a passphrase and catches the error, logging a message with the stderr output. ```javascript import { execSync } from "child_process"; try { execSync("node src/keyGenerator.js", { stdio: "pipe" }); } catch (err) { // "Passphrase is empty. Please include passphrase argument to generate the keys // like: node src/keyGenerator.js {passphrase}" console.error("Key generation requires a passphrase argument:", err.stderr); } ``` -------------------------------- ### Build agent flow payload with flow_details Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/python-llama-chatbot-server.md Programmatic example showing how to build an agent flow payload using the flow_details function and send it via requests.post. This bypasses the HTTP layer and is useful for testing or integrating directly. ```python payload = flow_details( flow_header="Talk to an Agent", flow_body="You have indicated that you would like to talk to an agent.", flow_footer="Click the button below to proceed", flow_id="1234567890", flow_cta="Proceed", recipient_phone_number="15551234567", screen_id="TALK_TO_AN_AGENT", ) requests.post(url, headers=headers, data=payload) ``` -------------------------------- ### Import dependencies for the Node.js Flow endpoint server Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-endpoint-server.md Imports the Express framework, the encryption helpers (decryptRequest, encryptResponse, FlowEndpointException), the flow logic (getNextScreen), and Node's crypto module. This is the dependency setup for the server. ```javascript import express from "express"; import { decryptRequest, encryptResponse, FlowEndpointException } from "./encryption.js"; import { getNextScreen } from "./flow.js"; import crypto from "crypto"; ``` -------------------------------- ### Graph API Echo Reply Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-echo-webhook.md Programmatic example of sending an echo reply to a WhatsApp message using the Graph API, as performed by the webhook handler. ```APIDOC ## POST https://graph.facebook.com/v18.0/{phone-number-id}/messages ### Description Sends an echo reply to a WhatsApp message using the Graph API. This is the call made by the webhook handler for each incoming text message. ### Method POST ### Endpoint `https://graph.facebook.com/v18.0/{phone-number-id}/messages` ### Parameters #### Path Parameters - **phone-number-id** (string) - Required - The ID of the phone number sending the message. #### Headers - **Authorization** (string) - Required - Bearer token for authentication, using the Graph API token. #### Request Body - **messaging_product** (string) - Required - Must be "whatsapp". - **to** (string) - Required - The recipient's phone number (from the original message). - **text** (object) - Required - The text message object. - **body** (string) - Required - The message body, typically "Echo: " + original message text. - **context** (object) - Optional - Context information. - **message_id** (string) - Required - The ID of the original message being replied to. ### Request Example ```json { "messaging_product": "whatsapp", "to": "<message.from>", "text": { "body": "Echo: <message.text.body>" }, "context": { "message_id": "<message.id>" } } ``` ### Response #### Success Response (200) - **status** (number) - HTTP status code 200 on success. #### Error Response (500) - **status** (number) - HTTP status code 500 if the Graph API call fails. ### Error Handling If the Graph API call fails, the error is logged and a 500 status is returned to the webhook platform, which will retry delivery. ``` -------------------------------- ### GET /webhook Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-echo-webhook.md Handles the Meta webhook verification handshake. It checks the hub.mode and hub.verify_token query parameters and returns the hub.challenge value on success or a 403 status on mismatch. ```APIDOC ## [GET] /webhook ### Description Handles the Meta webhook verification handshake. It checks the hub.mode and hub.verify_token query parameters and returns the hub.challenge value on success or a 403 status on mismatch. ### Method GET ### Endpoint /webhook ### Parameters #### Query Parameters - **hub.mode** (string) - Required - must equal `"subscribe"`. - **hub.verify_token** (string) - Required - must equal `WEBHOOK_VERIFY_TOKEN`. - **hub.challenge** (string) - Required - echoed verbatim on success. ### Response #### Success Response (200) Returns the `hub.challenge` string. #### Error Response (403) Returned when `hub.mode` is not `"subscribe"` or `hub.verify_token` does not match `WEBHOOK_VERIFY_TOKEN`. ### Response Example Success: `200` with the challenge string. Failure: `403` status. ``` -------------------------------- ### Read mandatory PRIVATE_KEY and PASSPHRASE environment variables Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/csharp-endpoint.md Reads the PRIVATE_KEY and PASSPHRASE environment variables, throwing an InvalidOperationException if either is not set. Both are mandatory, unlike the Node.js example where PASSPHRASE defaults to an empty string. ```csharp var PRIVATE_KEY = Environment.GetEnvironmentVariable("PRIVATE_KEY") ?? throw new InvalidOperationException("The environment variable 'PRIVATE_KEY' is not set."); var PASSPHRASE = Environment.GetEnvironmentVariable("PASSPHRASE") ?? throw new InvalidOperationException("The environment variable 'PASSPHRASE' is not set."); ``` -------------------------------- ### Send echo reply via Graph API (axios) Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-echo-webhook.md Programmatic example of the Graph API call used to send an echo reply. The app performs two sequential Graph API calls per text message; replicate this with your own HTTP client when embedding the flow elsewhere. ```js // the app performs two sequential Graph API calls per text message; replicate with your // own HTTP client when embedding the flow elsewhere await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${phoneNumberId}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}` }, data: { messaging_product: "whatsapp", to: message.from, text: { body: "Echo: " + message.text.body }, context: { message_id: message.id }, }, }); ``` -------------------------------- ### GET /webhook verification handshake Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-echo-webhook.md Handles the Meta webhook verification request. Checks that hub.mode equals 'subscribe' and hub.verify_token matches WEBHOOK_VERIFY_TOKEN; on success responds with the challenge string and logs 'Webhook verified successfully!', otherwise sends 403. ```javascript app.get("/webhook", (req, res) => { const mode = req.query["hub.mode"]; const token = req.query["hub.verify_token"]; const challenge = req.query["hub.challenge"]; // check the mode and token sent are correct if (mode === "subscribe" && token === WEBHOOK_VERIFY_TOKEN) { res.status(200).send(challenge); console.log("Webhook verified successfully!"); } else { res.sendStatus(403); } }); ``` -------------------------------- ### Set up gin server and POST / handler Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/go-endpoint.md Sets up the gin server, loads required environment variables, and defines the POST / handler that processes requests and returns encrypted responses. ```go func main() { privateKey := os.Getenv("PRIVATE_KEY") passphrase := os.Getenv("PASSPHRASE") if privateKey == "" || passphrase == "" { log.Fatal("Environment variables 'PRIVATE_KEY' and 'PASSPHRASE' are required.") } r := gin.Default() r.POST("/", func(c *gin.Context) { encryptedResponse, err := processRequest(c, privateKey, passphrase) if err != nil { log.Print(err) c.String(500, "Internal Server Error") return } c.String(200, encryptedResponse) }) r.Run(":3000") } ``` -------------------------------- ### GET / info route Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-endpoint-server.md Defines the GET / route in an Express app. Responds with status 200 and static HTML, with no authentication or parameters required. ```javascript app.get("/", (req, res) => { res.send(`<pre>Nothing to see here. Checkout README.md to start.</pre>`); }); ``` -------------------------------- ### Survey app nfm_reply response_json example Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/types.md Example `response_json` payload parsed by the survey app (`articles/creating-surveys/main.py:91โ107`). The survey reads the form field ids directly from this JSON object. ```json { "source": "0", "tour_type": "1", "tour_quality": "2", "decision_influencer": "2", "tour_guides": "0", "aspects_enjoyed": "3", "improvements": "4", "recommend": "1", "return_booking": "0" } ``` -------------------------------- ### Llama-chatbot app nfm_reply response_json example Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/types.md Example `response_json` payload parsed by the llama-chatbot app (`articles/llama-chatbot-flows/main.py:84โ102`). It reads a `flow_key` discriminator (`"agentconnect"` vs anything else) plus the form fields. ```json { "flow_key": "agentconnect", "first_name": "...", "second_name": "...", "services_interested": ["accomodation_services"], "additional_info": "...", "issue": "..." } ``` -------------------------------- ### GET {{base-url}}/{{flow-id}}/assets Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/endpoints.md Lists assets associated with a flow. ```APIDOC ## GET {{base-url}}/{{flow-id}}/assets ### Description Retrieves a list of assets for the flow. ### Method GET ### Endpoint {{base-url}}/{{flow-id}}/assets ### Response ```json { "data": [ { "name": "string", "asset_type": "string", "download_url": "string" } ], "paging": {} } ``` ``` -------------------------------- ### GET {{base-url}}/{{waba-id}}/flows Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/endpoints.md Lists all flows in the WhatsApp Business Account. ```APIDOC ## GET {{base-url}}/{{waba-id}}/flows ### Description Retrieves a list of flows. ### Method GET ### Endpoint {{base-url}}/{{waba-id}}/flows ### Response ```json { "data": [ { "name": "string", "status": "string", "categories": ["string"], "validation_errors": ["string"], "id": "string" } ] } ``` ``` -------------------------------- ### GET {{base-url}}/{{waba-id}}/phone_numbers Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/endpoints.md Lists phone numbers in the WhatsApp Business Account. ```APIDOC ## GET {{base-url}}/{{waba-id}}/phone_numbers ### Description Retrieves phone numbers associated with the WABA. ### Method GET ### Endpoint {{base-url}}/{{waba-id}}/phone_numbers ### Response ```json { "data": [ { "verified_name": "string", "code_verification_status": "string", "display_phone_number": "string", "quality_rating": "string", "thread_limit_per_day": "number", "id": "string" } ], "paging": {} } ``` ``` -------------------------------- ### GET {{base-url}}/{{business-phone-number-id}}/whatsapp_business_encryption Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/endpoints.md Retrieves the business public key and its signature status. ```APIDOC ## GET {{base-url}}/{{business-phone-number-id}}/whatsapp_business_encryption ### Description Retrieves the business public key and signature status. ### Method GET ### Endpoint {{base-url}}/{{business-phone-number-id}}/whatsapp_business_encryption ### Response ```json { "data": [ { "business_public_key": "string", "business_public_key_signature_status": "string" } ] } ``` ``` -------------------------------- ### Output template from keyGenerator.js Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-key-generator.md On success, stdout prints a template with four labelled sections: PASSPHRASE, PRIVATE_KEY, and PUBLIC KEY. The PASSPHRASE and PRIVATE_KEY go into the server .env file, and the PUBLIC KEY is uploaded to the business phone number. ```text Successfully created your public private key pair. Please copy the below values into your /.env file ************* COPY PASSPHRASE & PRIVATE KEY BELOW TO .env FILE ************* PASSPHRASE="${passphrase}" PRIVATE_KEY="${keyPair.privateKey}" ************* COPY PASSPHRASE & PRIVATE KEY ABOVE TO .env FILE ************* ************* COPY PUBLIC KEY BELOW ************* ${keyPair.publicKey} ************* COPY PUBLIC KEY ABOVE ************* ``` -------------------------------- ### Set environment variables for private key and passphrase Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/examples/endpoint/nodejs/basic/README.md Set the PASSPHRASE and PRIVATE_KEY environment variables. Ensure the multiline private key has correct line breaks. Replace the placeholder with your actual RSA private key. ```bash PASSPHRASE="my-secret" PRIVATE_KEY="-----[REPLACE THIS] BEGIN RSA PRIVATE KEY----- MIIE... ... ...xyz -----[REPLACE THIS] END RSA PRIVATE KEY-----" ``` -------------------------------- ### GET {{base-url}}/{{flow-id}} Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/endpoints.md Retrieves flow details, preview, or metrics based on query parameters. ```APIDOC ## GET {{base-url}}/{{flow-id}} ### Description Retrieves flow details, preview URL, or metrics. ### Method GET ### Endpoint {{base-url}}/{{flow-id}} ### Parameters #### Query Parameters - **fields** (string) - Required - Comma-separated list of fields to return. Supported fields: id, name, categories, preview, status, validation_errors, json_version, data_api_version, data_channel_uri, health_status, whatsapp_business_account, application. - **fields=preview.invalidate(false)** - Optional - Returns preview URL. Use invalidate(true) to regenerate. - **fields=metric.name(ENDPOINT_REQUEST_COUNT|ENDPOINT_REQUEST_ERROR|ENDPOINT_REQUEST_ERROR_RATE|ENDPOINT_REQUEST_LATENCY_SECONDS_CEIL|ENDPOINT_AVAILABILITY).granularity(DAY|LIFETIME).since(YYYY-MM-DD).until(YYYY-MM-DD)** - Optional - Returns metrics. ### Response Returns requested fields. For preview, includes preview_url and expires_at. ``` -------------------------------- ### Node.js Endpoint Server Configuration (server.js:24) Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/configuration.md Destructures configuration variables from process.env for the Node.js endpoint server. PRIVATE_KEY is required; if missing or empty, POST / throws an error. PASSPHRASE defaults to an empty string and must match the keyGenerator.js output. PORT defaults to "3000". ```javascript const { APP_SECRET, PRIVATE_KEY, PASSPHRASE = "", PORT = "3000" } = process.env; ``` -------------------------------- ### Basic usage of decryptRequest Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/nodejs-endpoint-encryption.md Example of using `decryptRequest` in an Express route to decrypt an incoming request body. ```javascript import { decryptRequest } from "./encryption.js"; app.post("/", (req, res) => { const { decryptedBody, aesKeyBuffer, initialVectorBuffer } = decryptRequest( req.body, process.env.PRIVATE_KEY, process.env.PASSPHRASE ); console.log("๐ฌ Decrypted Request:", decryptedBody); // { action: "INIT", screen: "...", ... } // โฆ route the user to the next screen, then encryptResponse(...) }); ``` -------------------------------- ### POST / Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/api-reference/csharp-endpoint.md Handles incoming WhatsApp Flow requests by decrypting the payload, processing the action, and returning an encrypted response. ```APIDOC ## POST / ### Description Handles incoming WhatsApp Flow requests by decrypting the payload, processing the action, and returning an encrypted response. ### Method POST ### Endpoint / ### Parameters #### Request Body - **body** (EndpointPayload) - Required - Parsed request body containing encrypted fields. ### Request Example { "encrypted_aes_key": "base64-encoded", "encrypted_flow_data": "base64-encoded", "initial_vector": "base64-encoded" } ### Response #### Success Response (200) - **Content-Type**: text/plain - **Body**: base64 ciphertext of the encrypted response. #### Response Example base64-encoded-encrypted-response ``` -------------------------------- ### POST / Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/endpoints.md The WhatsApp data-channel endpoint. The platform calls it with encrypted bodies; handles all request lifecycle actions (ping, INIT, data_exchange). ```APIDOC ## POST / ### Description The WhatsApp data-channel endpoint. The platform calls it with encrypted bodies; handles all request lifecycle actions (`ping`, `INIT`, `data_exchange`). ### Method POST ### Endpoint / ### Parameters #### Headers - **x-hub-signature-256** (string) - Required - `sha256=<hex HMAC-SHA256 of raw body with APP_SECRET>`; when `APP_SECRET` is set a mismatch yields `432` - **Content-Type** (string) - Required - `application/json` #### Request Body - **encrypted_aes_key** (string, base64) - Required - RSA-OAEP-SHA256 encrypted AES key - **encrypted_flow_data** (string, base64) - Required - `ciphertext โ 16-byte GCM tag` - **initial_vector** (string, base64) - Required - 16-byte GCM IV ### Processing Sequence 1. Fail 500 if `PRIVATE_KEY` unset. 2. Verify `x-hub-signature-256` โ 432 on failure. 3. `decryptRequest` โ 421 (`FlowEndpointException`) or 500 (anything else). 4. Optional flow-token validation (commented out; would return 427 with an encrypted `error_msg` body). 5. `getNextScreen(decryptedBody)`. 6. Encrypt result and send. ### Response - **200 OK** - body `text/plain` base64 ciphertext (`encryptResponse` output). - Error statuses: `421`, `427`, `432`, `500` (see `errors.md`). ``` -------------------------------- ### Migrate Flows per-flow failure payload Source: https://github.com/whatsapp/whatsapp-flows-tools/blob/main/_autodocs/errors.md Example per-flow failure object from the failed_flows array in Migrate Flows responses. Contains source_name, error_code, and error_message fields. ```json { "source_name": "leadgen", "error_code": "4233041", "error_message": "Flows Migration Error: Flow name not found in source WABA." } ```