N
n8n Store
Workflow Market
WhatsApp Marketing Dashboard with Dynamic Broadcasts from Google Sheets to Meta Templates

WhatsApp Marketing Dashboard with Dynamic Broadcasts from Google Sheets to Meta Templates

by anirudhaeran0 views

Description

Categories

⚙️ Automation

Nodes Used

n8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.waitn8n-nodes-base.webhookn8n-nodes-base.webhookn8n-nodes-base.dataTablen8n-nodes-base.dataTablen8n-nodes-base.dataTablen8n-nodes-base.dataTablen8n-nodes-base.dataTable
PriceFree
Views0
Last Updated11/28/2025
workflow.json
{
  "meta": {
    "instanceId": "8ada9bcc9ea45588c6264d0e90c5cf681653b47a083291c1f78d0cc01f7e596b",
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "db4a7719-3571-45d5-b921-8fdb1b5cac37",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        -224
      ],
      "parameters": {
        "color": 5,
        "width": 1456,
        "height": 448,
        "content": "## Flow 1: Send Broadcast Message"
      },
      "typeVersion": 1
    },
    {
      "id": "deb7f928-b17d-4317-b4f1-eaea4db151eb",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        240
      ],
      "parameters": {
        "color": 4,
        "width": 1456,
        "height": 464,
        "content": "## Flow 2: Get All Template Details from Meta and Store it in n8n Data Table"
      },
      "typeVersion": 1
    },
    {
      "id": "03f19e66-4f45-4242-bcad-71da85fc6c2e",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1216,
        96
      ],
      "parameters": {
        "color": 3,
        "width": 800,
        "height": 336,
        "content": "## Flow 3: Get meta templates for Frontend "
      },
      "typeVersion": 1
    },
    {
      "id": "0804ef5f-f7b7-434f-92f1-6d6014833598",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1056,
        -560
      ],
      "parameters": {
        "width": 544,
        "height": 464,
        "content": "=========================\n Send dynamic WhatsApp broadcast messages to contacts from Google Sheets (From Dashboard)\n=======================================\nFor any questions or support, please contact:\n    [email protected]\n\nExplore more tips here:\n    - LinkedIn: https://www.linkedin.com/in/anirudh-narayan-a-/\n\nHappy learning! -- Anirudh Aeran\n\n\n======================================="
      },
      "typeVersion": 1
    },
    {
      "id": "d3c19cc7-7dfa-4104-b2fb-be3dd76ee95b",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1056,
        -16
      ],
      "parameters": {
        "color": 5,
        "width": 544,
        "height": 1024,
        "content": "## Try It Out!\n\nThis workflow is the complete backend for a powerful WhatsApp marketing dashboard.\n\n**Pre-requisites**:\n\n1. **Set Up Your Tools:** You will need a Meta App (with WhatsApp configured), a Google Sheet for contacts, and an n8n Data Table. You need Both Meta for developer and meta for business account. Refer this [PLAYLIST](https://www.youtube.com/playlist?list=PLl_MEz33D2N0G_fvCpAuvEcYidC8pGPfb) if you don't have these accounts. \n\n2. **Set up the Dashboard**: Using this [CODE](https://github.com/anirudhaeran/Whatsapp-Dashboard), create a new index.html file in your editor and replace all the webhook link placeholders with your original webhook links. Then for free hosting use [NETLIFY](https://youtu.be/9srnyNC1e_o?si=nSFDZRks_19p43jc)\n\n3. **Connect Credentials:** Create an \"HTTP Header Auth\" credential with your \"permanent WhatsApp Access Token\" and connect your Google Sheets account in n8n. Refer this - [Video](https://youtu.be/4HRjp2c5EoQ?si=-tKktorFRpmcFuLA) and [n8n docs](https://docs.n8n.io/) if you need help.\n\n4. **Configure Google sheets and n8n Data Table**: Create a google sheet using, Phone Number, Full Name and Marketing Image URL. Create n8n Data Table using these columns, template_name, language_code, components_structure, template_id, status, category\n\n5. **Configure the Workflow:**: Add your WABA ID in \"Get Templates from Meta\" Node and Phone Number ID in \"Send Message\" Node to the URLs in these \"HTTP Request\" nodes. Select your credentials and add your Sheet/Data Table IDs in all relevant nodes.\n\n6. **Sync Your Templates:** Manually run the second flow (starting with the \"Schedule Trigger\") once. This will pull all your approved templates from Meta and load them into your n8n Data Table.\n\n7. **Test the API Endpoint:**\n- Copy the URL from the webhook named \"API: Get Templates for Frontend\".\n- Paste this URL into your browser. You should see a JSON list of all the templates you just synced. \n\n\n**8. Send a Test Broadcast:**\n- Make sure your Google Sheet has at least one test contact (with a real phone number).\n- Trigger the main webhook (\"Trigger: Send Broadcast\") using a tool like Postman or by building a simple front-end. Send a JSON body like: {\"templateName\": \"your_template_name\"}.\n- Check your WhatsApp. You should receive the personalized message!\n\n\n**9. Go Live:** Activate all three parts of the workflow and integrate the webhook URLs into your production dashboard."
      },
      "typeVersion": 1
    },
    {
      "id": "ec716c6f-f442-4416-a392-3d97c54df2cd",
      "name": "Get Templates from Meta",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        512,
        416
      ],
      "parameters": {
        "url": "https://graph.facebook.com/v20.0/\"Your WABA ID\"/message_templates",
        "options": {},
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "fields",
              "value": "name,language,components,category,status"
            }
          ]
        }
      },
      "credentials": {
        "whatsAppApi": {
          "id": "2uJI7woBGmTyCHHR",
          "name": "WhatsApp account"
        },
        "httpHeaderAuth": {
          "id": "27OkNJYcv12xxQkz",
          "name": "Whatsapp header auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "8aedf0be-a405-45ce-8a99-f7c581dc0945",
      "name": "Trigger: Send Broadcast",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -112,
        -64
      ],
      "webhookId": "56e00faa-448a-4465-ae17-06eee704ed66",
      "parameters": {
        "path": "56e00faa-448a-4465-ae17-06eee704ed66",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "f4df7e6d-6382-4395-8b43-d4968a3dcd42",
      "name": "Get Template Structure from Table",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        96,
        -64
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "template_name",
              "keyValue": "={{ $json.body.templateName.split(' - ')[0] }}"
            }
          ]
        },
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "rnYrJGkmc76p92Ti",
          "cachedResultUrl": "/projects/WIEOgu5DFMTn81iD/datatables/rnYrJGkmc76p92Ti",
          "cachedResultName": "WhatsApp Templates"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ecc942ec-f6e5-4305-adc6-e6e2bed14726",
      "name": "Get Contacts from Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        304,
        -64
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1W2UvoNmlbouj1WhkQLEkHncqAcwNH1Rk6UqVTNVirg0/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1W2UvoNmlbouj1WhkQLEkHncqAcwNH1Rk6UqVTNVirg0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1W2UvoNmlbouj1WhkQLEkHncqAcwNH1Rk6UqVTNVirg0/edit?usp=drivesdk",
          "cachedResultName": "Google review Agent"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "9jbOpr1QoJsk9P2J",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "29757e0f-3d32-47d0-9cd5-f7c4e6a945b8",
      "name": "Dynamically Build Message Body",
      "type": "n8n-nodes-base.code",
      "position": [
        512,
        -64
      ],
      "parameters": {
        "jsCode": "// Get data from the correct nodes using their names\nconst contactData = $('Get Contacts from Google Sheet').item.json;\nconst tableData = $('Get Template Structure from Table').item.json;\n\n// Parse the template structure from our n8n Table\nconst templateStructure = JSON.parse(tableData.components_structure);\n\nlet finalComponents = [];\n\n// Loop through the template structure to build all components\nfor (const component of templateStructure) {\n  \n  // --- NEW: HANDLES DYNAMIC IMAGE HEADERS ---\n  if (component.type === 'HEADER') {\n      let headerParams = [];\n      // Check if the template expects a dynamic image\n      if (component.format === 'IMAGE') {\n          headerParams.push({\n              type: 'image',\n              image: {\n                  link: contactData['Marketing Image URL'] // Get URL from the new sheet column\n              }\n          });\n      }\n      // Add more logic here for VIDEO or DOCUMENT if you use them in the future\n      finalComponents.push({ type: 'header', parameters: headerParams });\n  }\n\n  // Handles dynamic body\n  if (component.type === 'BODY' && component.text && component.text.includes('{{1}}')) {\n    finalComponents.push({ \n      type: 'body', \n      parameters: [{ type: 'text', text: contactData['Full Name'] }] \n    });\n  }\n\n  // Handles all button types\n  if (component.type === 'BUTTONS') {\n    let urlButtonIndex = 0;\n    for (const button of component.buttons) {\n      if (button.type === 'URL' && button.url && button.url.includes('{{1}}')) {\n        finalComponents.push({ \n          type: 'button', \n          sub_type: 'url', \n          index: String(urlButtonIndex),\n          parameters: [{ \n            type: 'payload', \n            payload: String(contactData['Phone Number']) \n          }] \n        });\n        urlButtonIndex++;\n      }\n    }\n  }\n}\n\n// Build the final API request body\nconst finalApiRequestBody = {\n  messaging_product: \"whatsapp\",\n  to: String(contactData['Phone Number']),\n  type: \"template\",\n  template: {\n    name: tableData.template_name,\n    language: {\n      code: tableData.language_code\n    },\n    components: finalComponents\n  }\n};\n\n// Return this single object for the HTTP Request node\nreturn {\n  json: {\n    finalApiRequestBody: finalApiRequestBody\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "c014376b-d87d-4006-8f00-e936e658811a",
      "name": "Wait 1s Between Messages",
      "type": "n8n-nodes-base.wait",
      "position": [
        896,
        -64
      ],
      "webhookId": "1b1544f9-2401-4d10-90ea-29ffab135335",
      "parameters": {
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "90499008-8f0f-49b5-b653-7f4b0a4e3213",
      "name": "Send WhatsApp Message",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        704,
        -64
      ],
      "parameters": {
        "url": "https://graph.facebook.com/v20.0/\"Your Phone Number ID\"/messages",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ $json.finalApiRequestBody }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "27OkNJYcv12xxQkz",
          "name": "Whatsapp header auth"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "e3de75f5-7c4d-4abd-a3ec-6c8eef6d9826",
      "name": "Sync Daily",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -112,
        416
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7205aca8-f635-45b8-a0c6-0317b49bf922",
      "name": "Get Existing Templates from Table",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        96,
        416
      ],
      "parameters": {
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "rnYrJGkmc76p92Ti",
          "cachedResultUrl": "/projects/WIEOgu5DFMTn81iD/datatables/rnYrJGkmc76p92Ti",
          "cachedResultName": "WhatsApp Templates"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "26d6af35-d61c-46ef-93f7-d3859510a675",
      "name": "Clear Existing Templates",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        304,
        416
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyValue": "={{ $json.id }}"
            }
          ]
        },
        "options": {},
        "operation": "deleteRows",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "rnYrJGkmc76p92Ti",
          "cachedResultUrl": "/projects/WIEOgu5DFMTn81iD/datatables/rnYrJGkmc76p92Ti",
          "cachedResultName": "WhatsApp Templates"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": false
    },
    {
      "id": "bd0ed918-c9db-43a9-90d2-9e782086d7a3",
      "name": "Split Templates into Items",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        416
      ],
      "parameters": {
        "jsCode": "// Get the array of templates from the previous node\nconst templates = items[0].json.data;\n\n// Create a new n8n item for each template in the array\nconst newItems = templates.map(template => {\n  return {\n    json: template\n  };\n});\n\n// Return the new list of items\nreturn newItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "bbc218db-2176-48c4-a784-8fcb24d0bba4",
      "name": "Save Templates to Data Table",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        928,
        416
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "={{ $json.status }}",
            "category": "={{ $json.category }}",
            "template_id": "={{ $json.id }}",
            "language_code": "={{ $json.language }}",
            "template_name": "={{ $json.name }}",
            "components_structure": "={{ JSON.stringify($json.components) }}"
          },
          "schema": [
            {
              "id": "template_name",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "template_name",
              "defaultMatch": false
            },
            {
              "id": "language_code",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "language_code",
              "defaultMatch": false
            },
            {
              "id": "components_structure",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "components_structure",
              "defaultMatch": false
            },
            {
              "id": "template_id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "template_id",
              "defaultMatch": false
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "status",
              "defaultMatch": false
            },
            {
              "id": "category",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "category",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "filters": {
          "conditions": [
            {
              "keyName": "template_name",
              "keyValue": "={{ $json.name }}"
            }
          ]
        },
        "operation": "upsert",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "rnYrJGkmc76p92Ti",
          "cachedResultUrl": "/projects/WIEOgu5DFMTn81iD/datatables/rnYrJGkmc76p92Ti",
          "cachedResultName": "WhatsApp Templates"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "47b36730-bf0c-4cc8-9433-4b6e478dba21",
      "name": "API: Get Templates for Frontend",
      "type": "n8n-nodes-base.webhook",
      "position": [
        1360,
        224
      ],
      "webhookId": "127eeea4-2009-4555-83a8-42111ce1de73",
      "parameters": {
        "path": "127eeea4-2009-4555-83a8-42111ce1de73",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "ffc9d6c2-f3f4-4770-b8dd-aa7bc726d347",
      "name": "Get All Templates from Table",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        1568,
        224
      ],
      "parameters": {
        "operation": "get",
        "returnAll": true,
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "rnYrJGkmc76p92Ti",
          "cachedResultUrl": "/projects/WIEOgu5DFMTn81iD/datatables/rnYrJGkmc76p92Ti",
          "cachedResultName": "WhatsApp Templates"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "48022296-90c8-4146-9621-6e98a99f30b2",
      "name": "Return Template List as JSON",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1776,
        224
      ],
      "parameters": {
        "options": {},
        "respondWith": "allIncomingItems"
      },
      "typeVersion": 1.4
    },
    {
      "id": "a25c446b-4ff9-445c-b008-a406f57c869a",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        -400
      ],
      "parameters": {
        "content": "**This Flow 1 will send messages to All the prospects from your Contact List in Google Sheets. You should regulate the number of messages sent per minute according to meta policies**"
      },
      "typeVersion": 1
    },
    {
      "id": "32580cac-a3a0-417c-b219-ebf59690e8ad",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2032,
        208
      ],
      "parameters": {
        "height": 112,
        "content": "**This flow 3 is the reason that you are able to view and select the templates from the frontend.**"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {
    "Trigger: Send Broadcast": [
      {
        "body": {
          "templateName": "marketing_message_template_1 - en"
        },
        "query": {},
        "params": {},
        "headers": {
          "via": "2.0 Caddy",
          "host": "n8n-digital.firmflare.in",
          "accept": "*/*",
          "origin": "https://fastidious-buttercream-d9284b.netlify.app",
          "referer": "https://fastidious-buttercream-d9284b.netlify.app/",
          "priority": "u=1, i",
          "sec-ch-ua": "\"Google Chrome\";v=\"141\", \"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"141\"",
          "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36",
          "content-type": "application/json",
          "content-length": "52",
          "sec-fetch-dest": "empty",
          "sec-fetch-mode": "cors",
          "sec-fetch-site": "cross-site",
          "accept-encoding": "gzip, deflate, br, zstd",
          "accept-language": "en-US,en;q=0.9",
          "x-forwarded-for": "49.47.196.219",
          "sec-ch-ua-mobile": "?0",
          "x-forwarded-host": "n8n-digital.firmflare.in",
          "x-forwarded-proto": "https",
          "sec-ch-ua-platform": "\"Windows\""
        },
        "webhookUrl": "https://n8n-digital.firmflare.in/webhook/56e00faa-448a-4465-ae17-06eee704ed66",
        "executionMode": "production"
      }
    ]
  },
  "connections": {
    "Sync Daily": {
      "main": [
        [
          {
            "node": "Get Existing Templates from Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send WhatsApp Message": {
      "main": [
        [
          {
            "node": "Wait 1s Between Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Templates from Meta": {
      "main": [
        [
          {
            "node": "Split Templates into Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger: Send Broadcast": {
      "main": [
        [
          {
            "node": "Get Template Structure from Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clear Existing Templates": {
      "main": [
        [
          {
            "node": "Get Templates from Meta",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 1s Between Messages": {
      "main": [
        []
      ]
    },
    "Split Templates into Items": {
      "main": [
        [
          {
            "node": "Save Templates to Data Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get All Templates from Table": {
      "main": [
        [
          {
            "node": "Return Template List as JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dynamically Build Message Body": {
      "main": [
        [
          {
            "node": "Send WhatsApp Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Contacts from Google Sheet": {
      "main": [
        [
          {
            "node": "Dynamically Build Message Body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API: Get Templates for Frontend": {
      "main": [
        [
          {
            "node": "Get All Templates from Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Existing Templates from Table": {
      "main": [
        [
          {
            "node": "Clear Existing Templates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Template Structure from Table": {
      "main": [
        [
          {
            "node": "Get Contacts from Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

相关工作流