javascriptnode.jswhatsappwhatsapp-cloud-apiwhatsapp-flows

WhatsApp Cloud API Flow Integration – Not Receiving Full Flow Submission Data


I'm trying to integrate WhatsApp Cloud API Flows (without endpoint) into my Node.js backend. My goal is to send a Flow via a template button and receive the form submission response in my /webhook endpoint.

Question

Any help or working example would be highly appreciated!

Flow JSON (used in Meta Flow Builder)
{
  "routing_model": {
    "ADDITIONAL_INCOME": ["SUMMARY"],
    "SUMMARY": []
  },
  "data_api_version": "3.0",
  "version": "7.0",
  "screens": [
    {
      "id": "ADDITIONAL_INCOME",
      "title": "Additional Income Sources",
      "data": {
        "houseLoanInterest": { "type": "number", "__example__": 180000 },
        "businessIncome": { "type": "number", "__example__": 30000 },
        "savingsInterest": { "type": "number", "__example__": 5000 },
        "fdInterest": { "type": "number", "__example__": 8000 },
        "giftIncome": { "type": "number", "__example__": 60000 },
        "otherIncomes": { "type": "number", "__example__": 10000 }
      },
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          {
            "type": "Form",
            "name": "additional_income_form",
            "children": [
              {
                "type": "TextInput",
                "label": "šŸ  Home Loan",
                "name": "houseLoanInterest",
                "input-type": "number",
                "required": true
              },
              {
                "type": "TextInput",
                "label": "šŸ‘Øā€šŸ’¼ Business Income",
                "name": "businessIncome",
                "input-type": "number",
                "required": true
              },
              {
                "type": "TextInput",
                "label": "šŸ’µ Savings Interest",
                "name": "savingsInterest",
                "input-type": "number",
                "required": true
              },
              {
                "type": "TextInput",
                "label": "šŸ¦ FD Interest",
                "name": "fdInterest",
                "input-type": "number",
                "required": true
              },
              {
                "type": "TextInput",
                "label": "šŸŽ Gift Income",
                "name": "giftIncome",
                "input-type": "number",
                "required": true
              },
              {
                "type": "TextInput",
                "label": "šŸ“¦ Other Income",
                "name": "otherIncomes",
                "input-type": "number",
                "required": true
              },
              {
                "type": "Footer",
                "label": "Continue",
                "on-click-action": {
                  "name": "navigate",
                  "next": {
                    "type": "screen",
                    "name": "SUMMARY"
                  }
                }
              }
            ]
          }
        ]
      }
    },
    {
      "id": "SUMMARY",
      "title": "Summary",
      "terminal": true,
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          {
            "type": "TextHeading",
            "text": "All Set!"
          },
          {
            "type": "TextBody",
            "text": "Your income details have been collected successfully."
          },
          {
            "type": "Footer",
            "label": "Finish",
            "on-click-action": {
              "name": "complete",
              "payload": {
                "houseLoanInterest": "${form.houseLoanInterest}",
                "businessIncome": "${form.businessIncome}",
                "savingsInterest": "${form.savingsInterest}",
                "fdInterest": "${form.fdInterest}",
                "giftIncome": "${form.giftIncome}",
                "otherIncomes": "${form.otherIncomes}"
              }
            }
          }
        ]
      }
    }
  ]
}

------
Backend Code
/send-flow endpoint

app.post('/send-flow', async (req, res) => {
  const { phone_number, template_name, flow_token } = req.body;

  if (!phone_number || !template_name || !flow_token) {
    return res.status(400).json({ error: 'Missing phone_number or template_name or flow_token' });
  }

  const url = `https://graph.facebook.com/v18.0/${PHONE_NUMBER_ID}/messages`;
  const headers = {
    Authorization: `Bearer ${WHATSAPP_TOKEN}`,
    'Content-Type': 'application/json'
  };

  const data = {
    messaging_product: "whatsapp",
    to: phone_number,
    type: "template",
    template: {
      name: template_name,
      language: {
        code: "en"
      },
      components: [
        {
          type: "button",
          sub_type: "flow",
          index: "0",
          parameters: [
            {
              type: "payload",
              payload: flow_token
            }
          ]
        }
      ]
    }
  };

  try {
    const response = await axios.post(url, data, { headers });
    console.log(`āœ… WhatsApp Flow template '${template_name}' sent to ${phone_number}`);
    res.status(200).json({ success: true, data: response.data });
  } catch (err) {
    console.error('āŒ Failed to send WhatsApp Flow:', err.response?.data || err.message);
    res.status(500).json({
      error: 'Failed to send WhatsApp Flow',
      details: err.response?.data || err.message
    });
  }
});

-------------
/webhook handler 

app.post('/webhook', async (req, res) => {
  const data = req.body;
  console.log("==== Webhook Data ====");
  console.log("šŸ“© WhatsApp Flow Webhook:", JSON.stringify(data, null, 2));
  res.sendStatus(200);

  const message = data?.entry?.[0]?.changes?.[0]?.value?.messages?.[0];

  if (message?.interactive?.type === "nfm_reply") {
    const phone = message.from;
    const flowName = message.interactive.nfm_reply.name;
    const responseString = message.interactive.nfm_reply.response_json;

    try {
      const parsedAnswers = JSON.parse(responseString);

      console.log("āœ… WhatsApp Flow Submission Received:");
      console.log("šŸ“ž Phone:", phone);
      console.log("šŸ“‹ Flow Name:", flowName);
      console.log("šŸ“¦ Submitted Data:", parsedAnswers);

    } catch (err) {
      console.error("āŒ Failed to parse flow response JSON:", err.message);
    }
  }
});

---
 Webhook Logs

šŸ“© WhatsApp Flow Webhook:
{
  "object": "whatsapp_business_account",
  "entry": [
    {
      "id": "XXXXXXXXXXXXXXX",
      "changes": [
        {
          "value": {
            "messaging_product": "whatsapp",
            "messages": [
              {
                "interactive": {
                  "type": "nfm_reply",
                  "nfm_reply": {
                    "response_json": "{\"flow_token\":\"unused\"}",
                    "body": "Sent",
                    "name": "flow"
                  }
                }
              }
            ]
          },
          "field": "messages"
        }
      ]
    }
  ]
}

-----
Problem : 
Even though the flow contains a form with required fields (like Home Loan, FD Interest, etc.), I'm only getting this in the webhook:
{ "flow_token": "unused" }
- Expected: Full form submission data as key-value pairs
- Actual: Only flow_token

--------------

What I Tried
- Verified that the flow is published and connected to the template
- Flow submission shows success on WhatsApp
- Confirmed the webhook works and logs other message types correctly


Solution

  • Update the footer of SUMMARY screen to following, it should work

    {
      "type": "Footer",
      "label": "Finish",
      "on-click-action": {
        "name": "complete",
        "payload": {
          "houseLoanInterest": "${screen.ADDITIONAL_INCOME.form.houseLoanInterest}",
          "businessIncome": "${screen.ADDITIONAL_INCOME.form.businessIncome}",
          "savingsInterest": "${screen.ADDITIONAL_INCOME.form.savingsInterest}",
          "fdInterest": "${screen.ADDITIONAL_INCOME.form.fdInterest}",
          "giftIncome": "${screen.ADDITIONAL_INCOME.form.giftIncome}",
          "otherIncomes": "${screen.ADDITIONAL_INCOME.form.otherIncomes}"
        }
      }
    }