{"id":"nZ4j1Y5HdguFrisS","meta":{"instanceId":"dd69efaf8212c74ad206700d104739d3329588a6f3f8381a46a481f34c9cc281","templateCredsSetupCompleted":true},"name":"Food Menu Update Notifier via WhatsApp, Email & Twilio SMS","tags":[],"nodes":[{"id":"7e2bce2a-74b1-4f6d-af45-87e4e9b67739","name":"Send WhatsApp Notification","type":"n8n-nodes-base.httpRequest","position":[-280,-160],"parameters":{"url":"https://api.whatsapp.com/send","method":"POST","options":{},"sendBody":true,"sendHeaders":true,"bodyParameters":{"parameters":[{"name":"to","value":"={{ $json['WhatsApp Number'] }}"},{"name":"message","value":"={{ $node['Merge Menu with Customer Data'].json.messageData.whatsappMessage }}"}]},"headerParameters":{"parameters":[{"name":"Authorization","value":"Bearer YOUR_WHATSAPP_API_TOKEN"},{"name":"Content-Type","value":"application/json"}]}},"typeVersion":4.1},{"id":"f7ce15a2-4f0e-4547-ab59-5ea71c9cea16","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-1660,-620],"parameters":{"color":3,"width":880,"height":220,"content":"## Automatically detects changes in the restaurant's special menu from Google Sheets and notifies customers via their preferred channel – WhatsApp, Email, or SMS (Twilio).\n\n## This ensures real-time updates to all customers about new or updated food offers.\n\n"},"typeVersion":1},{"id":"5a177091-496b-46e2-8243-a2aef1d7a877","name":"Daily Menu Update Scheduler","type":"n8n-nodes-base.scheduleTrigger","position":[-2260,40],"parameters":{"rule":{"interval":[{"field":"minutes","minutesInterval":30}]}},"typeVersion":1.1},{"id":"90184fe7-7c85-4e1c-8a50-db72dfbbeab6","name":"Fetch Special Menu Data","type":"n8n-nodes-base.googleSheets","position":[-2040,40],"parameters":{"options":{},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ/edit#gid=0","cachedResultName":"Special Menu"},"documentId":{"__rl":true,"mode":"list","value":"185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ/edit?usp=drivesdk","cachedResultName":"Special Offers"}},"credentials":{"googleSheetsOAuth2Api":{"id":"rgIk6EXVdPbmPYAl","name":"Google Sheets account"}},"typeVersion":4.4},{"id":"4f38bdba-10fb-4664-bd82-0562fb5c1236","name":"Detect Menu Changes","type":"n8n-nodes-base.code","position":[-1820,40],"parameters":{"jsCode":"// Check if menu data has changed since last check\nconst currentData = $input.all();\nconst lastCheckKey = 'last_menu_check';\n\n// Get stored last check data from workflow static data\nconst lastCheckData = $getWorkflowStaticData('global')[lastCheckKey] || null;\n\n// Convert current data to string for comparison\nconst currentDataString = JSON.stringify(currentData);\n\n// Initialize result\nlet hasChanged = false;\nlet newItems = [];\nlet updatedItems = [];\nlet removedItems = [];\n\nif (lastCheckData) {\n // Compare with previous data\n if (currentDataString !== lastCheckData.dataString) {\n hasChanged = true;\n \n // Detailed comparison logic\n const previousItems = JSON.parse(lastCheckData.dataString);\n const currentItems = currentData;\n \n // Find new items (compare by Item ID)\n newItems = currentItems.filter(current => \n !previousItems.some(prev => prev['Item ID'] === current['Item ID'])\n );\n \n // Find updated items (same ID but different content)\n updatedItems = currentItems.filter(current => {\n const prevItem = previousItems.find(prev => prev['Item ID'] === current['Item ID']);\n return prevItem && JSON.stringify(prevItem) !== JSON.stringify(current);\n });\n \n // Find removed items (existed before but not now)\n removedItems = previousItems.filter(prev => \n !currentItems.some(current => current['Item ID'] === prev['Item ID'])\n );\n }\n} else {\n // First run - all items are considered new\n hasChanged = true;\n newItems = currentData;\n}\n\n// Store current data for next comparison\n$getWorkflowStaticData('global')[lastCheckKey] = {\n dataString: currentDataString,\n timestamp: new Date().toISOString()\n};\n\n// Return results\nreturn [{\n json: {\n hasChanged,\n newItems,\n updatedItems,\n removedItems,\n totalItems: currentData.length,\n checkTime: new Date().toISOString()\n }\n}];"},"typeVersion":2},{"id":"9453db00-32a5-4077-9e2c-dd581bdcc151","name":"Generate Menu Alert Message","type":"n8n-nodes-base.code","position":[-1600,40],"parameters":{"jsCode":"// Format menu updates for notifications\nconst data = $json;\nlet message = \"🍽️ **SPECIAL MENU UPDATE** 🍽️\\n\\n\";\n\n// Helper function to extract item data from the nested structure\nfunction extractItemData(items) {\n if (!items || !Array.isArray(items)) return [];\n \n return items.map(item => {\n // Check if item has a 'json' property (nested structure)\n if (item.json) {\n return item.json;\n }\n // Otherwise, assume it's already the item data\n return item;\n });\n}\n\n// Add new items\nif (data.newItems && data.newItems.length > 0) {\n message += \"🆕 **NEW ITEMS:**\\n\";\n const newItemsData = extractItemData(data.newItems);\n newItemsData.forEach(item => {\n message += `• ${item['Item Name']} - $${item['Price']}\\n`;\n if (item['Description']) message += ` ${item['Description']}\\n`;\n });\n message += \"\\n\";\n}\n\n// Add updated items\nif (data.updatedItems && data.updatedItems.length > 0) {\n message += \"📝 **UPDATED ITEMS:**\\n\";\n const updatedItemsData = extractItemData(data.updatedItems);\n updatedItemsData.forEach(item => {\n message += `• ${item['Item Name']} - $${item['Price']}\\n`;\n if (item['Description']) message += ` ${item['Description']}\\n`;\n });\n message += \"\\n\";\n}\n\n// Add removed items\nif (data.removedItems && data.removedItems.length > 0) {\n message += \"❌ **REMOVED ITEMS:**\\n\";\n const removedItemsData = extractItemData(data.removedItems);\n removedItemsData.forEach(item => {\n message += `• ${item['Item Name']}\\n`;\n });\n message += \"\\n\";\n}\n\nmessage += `📊 Total Menu Items: ${data.totalItems}\\n`;\nmessage += `🕐 Updated: ${new Date(data.checkTime).toLocaleString()}\\n\\n`;\nmessage += \"Visit our restaurant or call to place your order! 📞\";\n\n// Create different formats for different channels\nconst whatsappMessage = message;\nconst emailMessage = message.replace(/\\*\\*/g, '').replace(/🍽️|🆕|📝|❌|📊|🕐|📞/g, '');\nconst smsMessage = message.replace(/\\*\\*/g, '').replace(/🍽️|🆕|📝|❌|📊|🕐|📞/g, '').substring(0, 160);\n\n// Calculate lengths properly\nconst newItemsLength = data.newItems ? data.newItems.length : 0;\nconst updatedItemsLength = data.updatedItems ? data.updatedItems.length : 0;\nconst removedItemsLength = data.removedItems ? data.removedItems.length : 0;\n\nreturn [{\n json: {\n whatsappMessage,\n emailMessage,\n smsMessage,\n hasNewItems: newItemsLength > 0,\n hasUpdatedItems: updatedItemsLength > 0,\n hasRemovedItems: removedItemsLength > 0,\n totalChanges: newItemsLength + updatedItemsLength + removedItemsLength\n }\n}];"},"typeVersion":2},{"id":"bc83e5ce-dce8-4873-8d0a-9a7a7180c88c","name":"Fetch Customer Contact List","type":"n8n-nodes-base.googleSheets","position":[-1380,40],"parameters":{"options":{},"sheetName":{"__rl":true,"mode":"list","value":1419004309,"cachedResultUrl":"https://docs.google.com/spreadsheets/d/185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ/edit#gid=1419004309","cachedResultName":"Customers"},"documentId":{"__rl":true,"mode":"list","value":"185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ/edit?usp=drivesdk","cachedResultName":"Special Offers"}},"credentials":{"googleSheetsOAuth2Api":{"id":"rgIk6EXVdPbmPYAl","name":"Google Sheets account"}},"typeVersion":4.4},{"id":"a7f5a4ab-415d-45aa-b83e-c4e000ea06ee","name":"Merge Menu with Customer Data","type":"n8n-nodes-base.set","position":[-940,40],"parameters":{"options":{},"assignments":{"assignments":[{"id":"customer-data","name":"customers","type":"array","value":"={{ $(\"Fetch Customer Contact List\").all() }}"},{"id":"message-data","name":"=messageData","type":"array","value":"={{ [$(\"Generate Menu Alert Message\").first()] }}"}]}},"typeVersion":3.3},{"id":"fcb2d175-6f06-4dae-8db6-5f10aa998f1b","name":"Split by Notification Preference","type":"n8n-nodes-base.splitInBatches","position":[-720,40],"parameters":{"options":{}},"typeVersion":3},{"id":"36c04dee-c856-412b-9477-f51bf2f9be7a","name":"Filter WhatsApp Users","type":"n8n-nodes-base.if","position":[-500,-160],"parameters":{"options":{},"conditions":{"options":{"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"whatsapp-condition","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json['WhatsApp Notifications'] }}","rightValue":"Yes"}]}},"typeVersion":2},{"id":"ccada993-edbe-4cd5-8d0e-53b3e1b7454b","name":"Log WhatsApp Status","type":"n8n-nodes-base.googleSheets","position":[-60,-160],"parameters":{"columns":{"value":{"Status":"Sent","Message":"={{ $node['Merge Menu with Customer Data'].json.messageData.whatsappMessage.substring(0, 100) }}...","Timestamp":"={{ new Date().toISOString() }}","Customer Name":"={{ $json['Customer Name'] }}","Notification Type":"WhatsApp"},"schema":[{"id":"Timestamp","type":"string","display":true,"required":false,"displayName":"Timestamp","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Customer Name","type":"string","display":true,"required":false,"displayName":"Customer Name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Notification Type","type":"string","display":true,"required":false,"displayName":"Notification Type","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Status","type":"string","display":true,"required":false,"displayName":"Status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Message","type":"string","display":true,"required":false,"displayName":"Message","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":1731532192,"cachedResultUrl":"https://docs.google.com/spreadsheets/d/185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ/edit#gid=1731532192","cachedResultName":"Notification Logs"},"documentId":{"__rl":true,"mode":"list","value":"185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ/edit?usp=drivesdk","cachedResultName":"Special Offers"}},"credentials":{"googleSheetsOAuth2Api":{"id":"rgIk6EXVdPbmPYAl","name":"Google Sheets account"}},"typeVersion":4.4},{"id":"8ca7e47b-219a-4be1-8ca1-5aea15b4e0c8","name":"Filter Email Users","type":"n8n-nodes-base.if","position":[-500,40],"parameters":{"options":{},"conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"email-condition","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.customers[0].json['Email Notifications'] }}","rightValue":"Yes"}]}},"typeVersion":2},{"id":"1510f575-b745-485f-9eaa-10ec1e7e1896","name":"Send Menu Email","type":"n8n-nodes-base.emailSend","position":[-280,40],"webhookId":"95c603c6-4b4a-4fcf-861b-d1dc81df76fe","parameters":{"text":"={{ $json.messageData[0].json.emailMessage }}","options":{},"subject":"🍽️ Special Menu Update - Your Favorite Restaurant","toEmail":"={{ $json.customers.map(customer => customer.json.Email).join(', ') }}","fromEmail":"vrushti@itoneclick.com","emailFormat":"text"},"credentials":{"smtp":{"id":"3QSx1pWoS0BZcK4c","name":"SMTP account"}},"typeVersion":2.1},{"id":"45fa2573-9b05-4f64-976f-e4693d40b7d7","name":"Filter SMS Users","type":"n8n-nodes-base.if","position":[-500,240],"parameters":{"options":{},"conditions":{"options":{"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"sms-condition","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json['SMS Notifications'] }}","rightValue":"Yes"}]}},"typeVersion":2},{"id":"91eca46b-30ca-4e83-8897-cff4ae3d2f1b","name":"Log Email Status1","type":"n8n-nodes-base.googleSheets","position":[-60,40],"parameters":{"columns":{"value":{"Status":"Sent","Message":"={{ $('Filter Email Users').item.json.messageData[0].emailMessage }}","Timestamp":"={{ new Date().toISOString() }}","Customer Name":"={{ $('Filter Email Users').item.json.customers[0]['Customer Name'] }}","Notification Type":"Email"},"schema":[{"id":"Timestamp","type":"string","display":true,"required":false,"displayName":"Timestamp","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Customer Name","type":"string","display":true,"required":false,"displayName":"Customer Name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Notification Type","type":"string","display":true,"required":false,"displayName":"Notification Type","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Status","type":"string","display":true,"required":false,"displayName":"Status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Message","type":"string","display":true,"required":false,"displayName":"Message","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":1731532192,"cachedResultUrl":"https://docs.google.com/spreadsheets/d/185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ/edit#gid=1731532192","cachedResultName":"Notification Logs"},"documentId":{"__rl":true,"mode":"list","value":"185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ/edit?usp=drivesdk","cachedResultName":"Special Offers"}},"credentials":{"googleSheetsOAuth2Api":{"id":"rgIk6EXVdPbmPYAl","name":"Google Sheets account"}},"typeVersion":4.4},{"id":"e3fe9db7-df90-491a-b471-e1a0eafc4f86","name":"Send Twilio SMS Alert","type":"n8n-nodes-base.httpRequest","position":[-280,240],"parameters":{"url":"https://api.twilio.com/2010-04-01/Accounts/YOUR_TWILIO_ACCOUNT_SID/Messages.json","method":"POST","options":{},"sendBody":true,"sendHeaders":true,"authentication":"genericCredentialType","bodyParameters":{"parameters":[{"name":"From","value":"YOUR_TWILIO_PHONE_NUMBER"},{"name":"To","value":"={{ $json['Phone Number'] }}"},{"name":"Body","value":"={{ $node['Merge Menu with Customer Data'].json.messageData.smsMessage }}"}]},"genericAuthType":"httpBasicAuth","headerParameters":{"parameters":[{"name":"Content-Type","value":"application/x-www-form-urlencoded"}]}},"credentials":{"httpBasicAuth":{"id":"SS8MHWya3vb8KVFr","name":"temporary cred"}},"typeVersion":4.1},{"id":"36a469d9-6966-4d14-917d-9d619c4220b7","name":" Log SMS Status","type":"n8n-nodes-base.googleSheets","position":[-60,240],"parameters":{"columns":{"value":{"Status":"Sent","Message":"={{ $node['Merge Menu with Customer Data'].json.messageData.smsMessage.substring(0, 100) }}...","Timestamp":"={{ new Date().toISOString() }}","Customer Name":"={{ $json['Customer Name'] }}","Notification Type":"SMS"},"schema":[{"id":"Timestamp","type":"string","display":true,"required":false,"displayName":"Timestamp","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Customer Name","type":"string","display":true,"required":false,"displayName":"Customer Name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Notification Type","type":"string","display":true,"required":false,"displayName":"Notification Type","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Status","type":"string","display":true,"required":false,"displayName":"Status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Message","type":"string","display":true,"required":false,"displayName":"Message","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":1731532192,"cachedResultUrl":"https://docs.google.com/spreadsheets/d/185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ/edit#gid=1731532192","cachedResultName":"Notification Logs"},"documentId":{"__rl":true,"mode":"list","value":"185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/185-CpBG7aVkoiq25NXjTW5gnNJC75n7w3TOBH9HpzhQ/edit?usp=drivesdk","cachedResultName":"Special Offers"}},"credentials":{"googleSheetsOAuth2Api":{"id":"rgIk6EXVdPbmPYAl","name":"Google Sheets account"}},"typeVersion":4.4},{"id":"14957f37-83b2-48b6-b61d-0244147f77db","name":"Wait For All data","type":"n8n-nodes-base.wait","position":[-1160,40],"webhookId":"162f6374-f15b-465c-9e71-0f52131bfab8","parameters":{},"typeVersion":1.1},{"id":"3694a6bc-9a67-4b03-a1ea-cb059e330431","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[-2320,-240],"parameters":{"color":5,"width":620,"height":660,"content":"## Automatically checks the special menu sheet on a schedule (daily/hourly) and detects any updates or changes in menu items."},"typeVersion":1},{"id":"5ece58c5-bec4-4b12-bf45-b75654a032c0","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[-1660,-240],"parameters":{"height":660,"content":"## Creates a custom message (with updated menu info) to be sent to customers in a readable and attractive format."},"typeVersion":1},{"id":"bf1b3482-a6cb-4015-942d-45277284575e","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[-1380,-240],"parameters":{"color":6,"width":780,"height":660,"content":"## Reads customer contact info and communication preferences (WhatsApp, Email, or SMS), then prepares data for each user."},"typeVersion":1},{"id":"984c9afc-655e-45e1-a0de-aea5a2fe7bfb","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[-540,-400],"parameters":{"color":4,"width":680,"height":820,"content":"## Sends the menu update to customers via their selected channel and logs the status for confirmation and audit.\n\n"},"typeVersion":1}],"active":false,"pinData":{},"settings":{"executionOrder":"v1"},"versionId":"0ef23c49-3d4e-4a28-91a9-19037f3121c2","connections":{"Send Menu Email":{"main":[[{"node":"Log Email Status1","type":"main","index":0}]]},"Filter SMS Users":{"main":[[{"node":"Send Twilio SMS Alert","type":"main","index":0}]]},"Log Email Status1":{"main":[[]]},"Wait For All data":{"main":[[{"node":"Merge Menu with Customer Data","type":"main","index":0}]]},"Filter Email Users":{"main":[[{"node":"Send Menu Email","type":"main","index":0}]]},"Detect Menu Changes":{"main":[[{"node":"Generate Menu Alert Message","type":"main","index":0}]]},"Log WhatsApp Status":{"main":[[]]},"Filter WhatsApp Users":{"main":[[{"node":"Send WhatsApp Notification","type":"main","index":0}]]},"Send Twilio SMS Alert":{"main":[[{"node":" Log SMS Status","type":"main","index":0}]]},"Fetch Special Menu Data":{"main":[[{"node":"Detect Menu Changes","type":"main","index":0}]]},"Send WhatsApp Notification":{"main":[[{"node":"Log WhatsApp Status","type":"main","index":0}]]},"Daily Menu Update Scheduler":{"main":[[{"node":"Fetch Special Menu Data","type":"main","index":0}]]},"Fetch Customer Contact List":{"main":[[{"node":"Wait For All data","type":"main","index":0}]]},"Generate Menu Alert Message":{"main":[[{"node":"Fetch Customer Contact List","type":"main","index":0}]]},"Merge Menu with Customer Data":{"main":[[{"node":"Split by Notification Preference","type":"main","index":0}]]},"Split by Notification Preference":{"main":[[{"node":"Filter WhatsApp Users","type":"main","index":0},{"node":"Filter SMS Users","type":"main","index":0},{"node":"Filter Email Users","type":"main","index":0}],[]]}}}