This workflow corresponds to n8n.io template #15309 — we link there as the canonical source.
This workflow follows the Chainllm → Google Sheets recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "U_a9avmwO6AE_ClMJTGbt",
"name": "Log meal nutrition from LINE food photos to Google Sheets using Gemini AI",
"tags": [
{
"id": "e5ecf79207a14e18",
"name": "ai",
"createdAt": "2026-04-30T18:38:11.353194Z",
"updatedAt": "2026-04-30T18:38:11.353194Z"
},
{
"id": "6ac628eef1b2473a",
"name": "gemini",
"createdAt": "2026-04-30T18:38:11.353194Z",
"updatedAt": "2026-04-30T18:38:11.353194Z"
},
{
"id": "2bb9824184974e92",
"name": "line",
"createdAt": "2026-04-30T18:38:11.353194Z",
"updatedAt": "2026-04-30T18:38:11.353194Z"
},
{
"id": "aa7747e028ea416f",
"name": "google-sheets",
"createdAt": "2026-04-30T18:38:11.353194Z",
"updatedAt": "2026-04-30T18:38:11.353194Z"
},
{
"id": "a61c6101c12e4c68",
"name": "health",
"createdAt": "2026-04-30T18:38:11.353194Z",
"updatedAt": "2026-04-30T18:38:11.353194Z"
},
{
"id": "14793b0260ce4d8b",
"name": "nutrition-tracking",
"createdAt": "2026-04-30T18:38:11.353194Z",
"updatedAt": "2026-04-30T18:38:11.353194Z"
},
{
"id": "a1ebde5bd03e4795",
"name": "image-recognition",
"createdAt": "2026-04-30T18:38:11.353194Z",
"updatedAt": "2026-04-30T18:38:11.353194Z"
},
{
"id": "255d67afb6174a5a",
"name": "automation",
"createdAt": "2026-04-30T18:38:11.353194Z",
"updatedAt": "2026-04-30T18:38:11.353194Z"
}
],
"nodes": [
{
"id": "6d4d3654-d529-45e6-ac69-ee3782f9e29b",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
80,
-64
],
"parameters": {
"width": 480,
"height": 896,
"content": "## Log meal nutrition from LINE food photos to Google Sheets using Gemini AI\n\n## How it works\n\n1. The system receives an image via LINE webhook.\n2. It verifies if it's an image message and downloads it.\n3. The image is converted to Base64 and analyzed using LLM.\n4. Nutrition data is parsed and logged to Google Sheets.\n5. Daily calorie totals are calculated and users are notified if limits are exceeded.\n\n## Setup steps\n\n- [ ] Set LINE_CHANNEL_ACCESS_TOKEN in \"Set Configuration Fields\".\n- [ ] Set GOOGLE_SHEET_ID in \"Set Configuration Fields\".\n- [ ] Configure trigger settings in \"Receive LINE Event via Webhook\".\n- [ ] Ensure Google Sheets is accessible for logging.\n- [ ] Set CALORIE_LIMIT, LINE_USER_ID in \"Set Configuration Fields\".\n\n## Customization\n\nYou may adjust the calorie limit and message configurations to tailor user notifications."
},
"typeVersion": 1
},
{
"id": "76b0cf4f-86f8-44b3-b992-5a5e88f13dc3",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
640,
192
],
"parameters": {
"color": 7,
"width": 240,
"height": 352,
"content": "## Set configuration fields\n\nInitialize configuration settings for LINE and Google Sheets."
},
"typeVersion": 1
},
{
"id": "08257482-6e5a-4d9b-875e-3286a7e987d0",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
928,
272
],
"parameters": {
"color": 7,
"width": 928,
"height": 688,
"content": "## Receive and verify message type\n\nReceive incoming messages and determine if it contains an image or a report request."
},
"typeVersion": 1
},
{
"id": "3170e657-9682-46d8-914b-486ade3255b9",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1408,
-64
],
"parameters": {
"color": 7,
"width": 432,
"height": 304,
"content": "## Download and convert image\n\nDownload image from LINE and convert it to a Base64 format."
},
"typeVersion": 1
},
{
"id": "604494f5-b873-4173-8081-0b713e042ee5",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1888,
64
],
"parameters": {
"color": 7,
"width": 432,
"height": 304,
"content": "## Analyze image and parse data\n\nUse LLM to analyze food image and extract nutrition data."
},
"typeVersion": 1
},
{
"id": "771237bf-1ed2-4a91-8e35-64f6177af348",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2368,
80
],
"parameters": {
"color": 7,
"width": 672,
"height": 272,
"content": "## Log and calculate nutrition\n\nLog nutrition information to Google Sheets and calculate daily statistics."
},
"typeVersion": 1
},
{
"id": "06598ecc-e366-49f6-96cd-e8df29d19573",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
3088,
-32
],
"parameters": {
"color": 7,
"width": 432,
"height": 496,
"content": "## Notify via LINE based on calorie\n\nCheck if user exceeds their daily calorie limit and notify them."
},
"typeVersion": 1
},
{
"id": "3e5c8059-6e10-4874-bc52-e6dcfa5b70da",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
928,
992
],
"parameters": {
"color": 7,
"width": 1200,
"height": 384,
"content": "## Weekly report scheduling and stats\n\nSchedule weekly statistics and report generation and pushing via LINE."
},
"typeVersion": 1
},
{
"id": "442fb6f7-f01e-4fe4-a5f6-66b9967754ff",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
3568,
32
],
"parameters": {
"color": 7,
"width": 240,
"height": 320,
"content": "## Respond to LINE webhook\n\nSend an acknowledgment response back to the LINE webhook."
},
"typeVersion": 1
},
{
"id": "16197d11-fd60-4629-ba99-376a0c209cb9",
"name": "Set Config Fields",
"type": "n8n-nodes-base.set",
"position": [
688,
384
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "config-line-token",
"name": "LINE_CHANNEL_ACCESS_TOKEN",
"type": "string",
"value": ""
},
{
"id": "config-sheet-id",
"name": "GOOGLE_SHEET_ID",
"type": "string",
"value": ""
},
{
"id": "config-calorie-limit",
"name": "CALORIE_LIMIT",
"type": "number",
"value": 2000
},
{
"id": "config-line-uid",
"name": "LINE_USER_ID",
"type": "string",
"value": ""
}
]
}
},
"typeVersion": 3.4
},
{
"id": "ac0cab7d-91e4-4aba-b79b-ea37ce6417ad",
"name": "When LINE Event Received",
"type": "n8n-nodes-base.webhook",
"position": [
976,
384
],
"parameters": {
"path": "nutrition-webhook",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "c2b2cfa1-f4a3-4f98-8d9c-b7c9a993ae83",
"name": "If Image Message",
"type": "n8n-nodes-base.if",
"position": [
1216,
384
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-is-image",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.events[0].message.type }}",
"rightValue": "image"
}
]
}
},
"typeVersion": 2
},
{
"id": "0591ffbf-4689-46b4-abb2-d1a45975a6e3",
"name": "If Report Command",
"type": "n8n-nodes-base.if",
"position": [
1456,
592
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-is-report",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.events[0].message.text }}",
"rightValue": "\u30ec\u30dd\u30fc\u30c8"
}
]
}
},
"typeVersion": 2
},
{
"id": "cb636ab9-1547-448e-9b80-3ffcad8f75f8",
"name": "Send Help Reply via LINE",
"type": "n8n-nodes-base.httpRequest",
"position": [
1712,
800
],
"parameters": {
"url": "https://api.line.me/v2/bot/message/reply",
"method": "POST",
"options": {},
"jsonBody": "={\n \"replyToken\": \"{{ $('When LINE Event Received').item.json.events[0].replyToken }}\",\n \"messages\": [\n {\n \"type\": \"text\",\n \"text\": \"\ud83d\udcf8 \u98df\u4e8b\u306e\u5199\u771f\u3092\u9001\u308b\u3068\u6804\u990a\u3092\u5206\u6790\u3057\u307e\u3059\u3002\\n\ud83d\udcca \\\"\u30ec\u30dd\u30fc\u30c8\\\" \u3068\u9001\u308b\u3068\u9031\u6b21\u30b5\u30de\u30ea\u30fc\u3092\u8868\u793a\u3057\u307e\u3059\u3002\"\n }\n ]\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Set Config Fields').item.json.LINE_CHANNEL_ACCESS_TOKEN }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "d400bf8f-1e0a-4b02-b072-8473290a8df8",
"name": "Fetch LINE Image Data",
"type": "n8n-nodes-base.httpRequest",
"position": [
1456,
64
],
"parameters": {
"url": "=https://api-data.line.me/v2/bot/message/{{ $('When LINE Event Received').item.json.events[0].message.id }}/content",
"options": {
"response": {
"response": {
"responseFormat": "file"
}
}
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Set Config Fields').item.json.LINE_CHANNEL_ACCESS_TOKEN }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "01211b04-0440-479a-a170-0185e048c5be",
"name": "Encode Image to Base64",
"type": "n8n-nodes-base.code",
"position": [
1696,
64
],
"parameters": {
"jsCode": "const items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n const binaryData = item.binary;\n if (binaryData && binaryData.data) {\n const buffer = await this.helpers.getBinaryDataBuffer(item.index || 0, 'data');\n const base64String = buffer.toString('base64');\n const mimeType = binaryData.data.mimeType || 'image/jpeg';\n results.push({\n json: {\n ...item.json,\n imageBase64: base64String,\n imageMimeType: mimeType,\n replyToken: $('When LINE Event Received').item.json.events[0].replyToken,\n lineUserId: $('When LINE Event Received').item.json.events[0].source.userId\n }\n });\n }\n}\n\nreturn results;"
},
"typeVersion": 2
},
{
"id": "5386bcd1-d8b3-444b-87bc-1a49d283366c",
"name": "Gemini Food Analysis Config",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
2064,
384
],
"parameters": {
"options": {}
},
"typeVersion": 1
},
{
"id": "77b4ccbd-80e9-4d48-abea-82312d07c636",
"name": "Process Food Analysis",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
1936,
192
],
"parameters": {},
"typeVersion": 1.4
},
{
"id": "77218d29-8189-4ceb-ab39-af6a579ddc80",
"name": "Extract Nutrition Data",
"type": "n8n-nodes-base.code",
"position": [
2176,
192
],
"parameters": {
"jsCode": "const items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n let text = item.json.text || item.json.response || '';\n \n // Extract JSON from response\n const jsonMatch = text.match(/\\{[\\s\\S]*?\\}/); \n let nutrition = {\n food_name: 'Unknown',\n meal_type: 'snack',\n calories: 0,\n protein: 0,\n fat: 0,\n carbs: 0,\n confidence: 'low'\n };\n\n if (jsonMatch) {\n try {\n nutrition = JSON.parse(jsonMatch[0]);\n } catch (e) {\n // Keep defaults if parsing fails\n }\n }\n\n results.push({\n json: {\n ...nutrition,\n timestamp: new Date().toISOString(),\n lineUserId: item.json.lineUserId || $('Encode Image to Base64').item.json.lineUserId,\n replyToken: item.json.replyToken || $('Encode Image to Base64').item.json.replyToken\n }\n });\n}\n\nreturn results;"
},
"typeVersion": 2
},
{
"id": "fb20d687-4819-45b6-baac-c74251c6ae95",
"name": "Append Meal to Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
2416,
192
],
"parameters": {
"columns": {
"value": {
"Fat": "={{ $json.fat }}",
"Carbs": "={{ $json.carbs }}",
"Protein": "={{ $json.protein }}",
"Calories": "={{ $json.calories }}",
"LINE_UID": "={{ $json.lineUserId }}",
"Food_Name": "={{ $json.food_name }}",
"Meal_Type": "={{ $json.meal_type }}",
"Timestamp": "={{ $json.timestamp }}",
"Confidence": "={{ $json.confidence }}"
},
"schema": [
{
"id": "Timestamp",
"type": "string",
"display": true,
"required": false,
"displayName": "Timestamp",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "LINE_UID",
"type": "string",
"display": true,
"required": false,
"displayName": "LINE_UID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Food_Name",
"type": "string",
"display": true,
"required": false,
"displayName": "Food_Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Meal_Type",
"type": "string",
"display": true,
"required": false,
"displayName": "Meal_Type",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Calories",
"type": "number",
"display": true,
"required": false,
"displayName": "Calories",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Protein",
"type": "number",
"display": true,
"required": false,
"displayName": "Protein",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Fat",
"type": "number",
"display": true,
"required": false,
"displayName": "Fat",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Carbs",
"type": "number",
"display": true,
"required": false,
"displayName": "Carbs",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Confidence",
"type": "string",
"display": true,
"required": false,
"displayName": "Confidence",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": []
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Config Fields').item.json.GOOGLE_SHEET_ID }}"
}
},
"typeVersion": 4.5
},
{
"id": "e6f4c262-973b-434d-aad3-51db66196bd7",
"name": "Read Today's Total from Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
2656,
192
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Config Fields').item.json.GOOGLE_SHEET_ID }}"
}
},
"typeVersion": 4.5
},
{
"id": "4f0d1aee-78b3-4059-b2be-cdb4525764f8",
"name": "Compute Daily Calorie Total",
"type": "n8n-nodes-base.code",
"position": [
2896,
192
],
"parameters": {
"jsCode": "const items = $input.all();\nconst today = new Date().toISOString().split('T')[0];\nconst lineUserId = $('Extract Nutrition Data').item.json.lineUserId;\n\nlet dailyTotal = 0;\nlet mealCount = 0;\n\nfor (const item of items) {\n const ts = item.json.Timestamp || '';\n const uid = item.json.LINE_UID || '';\n if (ts.startsWith(today) && uid === lineUserId) {\n dailyTotal += Number(item.json.Calories) || 0;\n mealCount++;\n }\n}\n\nconst nutrition = $('Extract Nutrition Data').item.json;\n\nreturn [{\n json: {\n daily_total: dailyTotal,\n meal_count: mealCount,\n food_name: nutrition.food_name,\n calories: nutrition.calories,\n protein: nutrition.protein,\n fat: nutrition.fat,\n carbs: nutrition.carbs,\n confidence: nutrition.confidence,\n replyToken: nutrition.replyToken,\n lineUserId: nutrition.lineUserId\n }\n}];"
},
"typeVersion": 2
},
{
"id": "f64f47bc-6615-450d-a04e-0566e35ab2c9",
"name": "If Over Calorie Limit",
"type": "n8n-nodes-base.if",
"position": [
3136,
192
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-over-limit",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.daily_total }}",
"rightValue": "={{ $('Set Config Fields').item.json.CALORIE_LIMIT }}"
}
]
}
},
"typeVersion": 2
},
{
"id": "156c908c-78c4-4399-be24-c6e80e8b5c7d",
"name": "Send Calorie Warning via LINE",
"type": "n8n-nodes-base.httpRequest",
"position": [
3376,
96
],
"parameters": {
"url": "https://api.line.me/v2/bot/message/reply",
"method": "POST",
"options": {},
"jsonBody": "={\n \"replyToken\": \"{{ $json.replyToken }}\",\n \"messages\": [\n {\n \"type\": \"text\",\n \"text\": \"\u26a0\ufe0f \u30ab\u30ed\u30ea\u30fc\u8d85\u904e!\\n\\n\ud83c\udf71 {{ $json.food_name }} / {{ $json.calories }}kcal\\nP:{{ $json.protein }}g F:{{ $json.fat }}g C:{{ $json.carbs }}g\\n\\n\ud83d\udcca \u672c\u65e5\u5408\u8a08: {{ $json.daily_total }}kcal\\n\u26a0\ufe0f \u4e0a\u9650 {{ $('Set Config Fields').item.json.CALORIE_LIMIT }}kcal \u3092\u8d85\u3048\u3066\u3044\u307e\u3059\"\n }\n ]\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Set Config Fields').item.json.LINE_CHANNEL_ACCESS_TOKEN }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "ff471b9c-397d-4361-b591-2248352a237d",
"name": "Send Nutrition Info via LINE",
"type": "n8n-nodes-base.httpRequest",
"position": [
3376,
288
],
"parameters": {
"url": "https://api.line.me/v2/bot/message/reply",
"method": "POST",
"options": {},
"jsonBody": "={\n \"replyToken\": \"{{ $json.replyToken }}\",\n \"messages\": [\n {\n \"type\": \"text\",\n \"text\": \"\ud83c\udf71 {{ $json.food_name }} / {{ $json.calories }}kcal\\nP:{{ $json.protein }}g F:{{ $json.fat }}g C:{{ $json.carbs }}g\\n\\n\ud83d\udcca \u672c\u65e5\u5408\u8a08: {{ $json.daily_total }}kcal\"\n }\n ]\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Set Config Fields').item.json.LINE_CHANNEL_ACCESS_TOKEN }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "ea2c773f-05ee-4ba1-bc05-0830a002e8a6",
"name": "Weekly 9AM Schedule",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
976,
1104
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9 * * 1"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "281a86f1-055c-4faf-b708-6dabfd81fe72",
"name": "Read Weekly Data from Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
1216,
1104
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Config Fields').item.json.GOOGLE_SHEET_ID }}"
}
},
"typeVersion": 4.5
},
{
"id": "bc1998c5-87c6-4a4a-a633-1a5526a5d798",
"name": "Summarize Weekly Stats",
"type": "n8n-nodes-base.code",
"position": [
1456,
1104
],
"parameters": {
"jsCode": "const items = $input.all();\nconst now = new Date();\nconst weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);\n\n// Filter last 7 days\nconst weeklyMeals = items.filter(item => {\n const ts = new Date(item.json.Timestamp || '');\n return ts >= weekAgo && ts <= now;\n});\n\n// Group by date\nconst dailyCalories = {};\nlet totalCalories = 0;\nlet totalProtein = 0;\nlet totalFat = 0;\nlet totalCarbs = 0;\nlet mealCount = weeklyMeals.length;\n\nfor (const meal of weeklyMeals) {\n const date = (meal.json.Timestamp || '').split('T')[0];\n const cal = Number(meal.json.Calories) || 0;\n dailyCalories[date] = (dailyCalories[date] || 0) + cal;\n totalCalories += cal;\n totalProtein += Number(meal.json.Protein) || 0;\n totalFat += Number(meal.json.Fat) || 0;\n totalCarbs += Number(meal.json.Carbs) || 0;\n}\n\nconst days = Object.keys(dailyCalories);\nconst avgCalories = days.length > 0 ? Math.round(totalCalories / days.length) : 0;\n\nlet highestDay = '';\nlet highestCal = 0;\nfor (const [date, cal] of Object.entries(dailyCalories)) {\n if (cal > highestCal) {\n highestCal = cal;\n highestDay = date;\n }\n}\n\nreturn [{\n json: {\n total_calories: totalCalories,\n avg_calories: avgCalories,\n highest_day: highestDay,\n highest_day_calories: highestCal,\n total_protein: Math.round(totalProtein),\n total_fat: Math.round(totalFat),\n total_carbs: Math.round(totalCarbs),\n meal_count: mealCount,\n days_tracked: days.length,\n daily_breakdown: dailyCalories\n }\n}];"
},
"typeVersion": 2
},
{
"id": "2cd4d4ca-e32c-46d7-a34d-72004418274f",
"name": "Gemini Weekly Report Config",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
1824,
1232
],
"parameters": {
"options": {}
},
"typeVersion": 1
},
{
"id": "8e86fda5-8380-4168-a8ee-d88fcf968609",
"name": "Create Weekly Comment with LLM",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
1680,
1104
],
"parameters": {},
"typeVersion": 1.4
},
{
"id": "c1aad51f-6aa6-4675-aaad-6172e5575323",
"name": "Deliver Weekly Report via LINE",
"type": "n8n-nodes-base.httpRequest",
"position": [
1984,
1104
],
"parameters": {
"url": "https://api.line.me/v2/bot/message/push",
"method": "POST",
"options": {},
"jsonBody": "={\n \"to\": \"{{ $('Set Config Fields').item.json.LINE_USER_ID }}\",\n \"messages\": [\n {\n \"type\": \"text\",\n \"text\": \"\ud83d\udcca \u9031\u6b21\u6804\u990a\u30ec\u30dd\u30fc\u30c8\\n\\n\ud83d\udd25 \u5408\u8a08: {{ $('Summarize Weekly Stats').item.json.total_calories }}kcal\\n\ud83d\udcc5 1\u65e5\u5e73\u5747: {{ $('Summarize Weekly Stats').item.json.avg_calories }}kcal\\n\u2b06\ufe0f \u6700\u9ad8: {{ $('Summarize Weekly Stats').item.json.highest_day }} ({{ $('Summarize Weekly Stats').item.json.highest_day_calories }}kcal)\\n\ud83c\udf56 P:{{ $('Summarize Weekly Stats').item.json.total_protein }}g F:{{ $('Summarize Weekly Stats').item.json.total_fat }}g C:{{ $('Summarize Weekly Stats').item.json.total_carbs }}g\\n\\n\ud83d\udcac {{ $json.text || $json.response }}\"\n }\n ]\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Set Config Fields').item.json.LINE_CHANNEL_ACCESS_TOKEN }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "71a3d9c8-a0e5-43f7-99fe-cf585f5420de",
"name": "Send Webhook Response OK",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
3616,
192
],
"parameters": {
"options": {
"responseCode": 200
},
"respondWith": "noData"
},
"typeVersion": 1.1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "",
"connections": {
"If Image Message": {
"main": [
[
{
"node": "Fetch LINE Image Data",
"type": "main",
"index": 0
}
],
[
{
"node": "If Report Command",
"type": "main",
"index": 0
}
]
]
},
"If Report Command": {
"main": [
[
{
"node": "Read Weekly Data from Sheets",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Help Reply via LINE",
"type": "main",
"index": 0
}
]
]
},
"Append Meal to Sheets": {
"main": [
[
{
"node": "Read Today's Total from Sheets",
"type": "main",
"index": 0
}
]
]
},
"Fetch LINE Image Data": {
"main": [
[
{
"node": "Encode Image to Base64",
"type": "main",
"index": 0
}
]
]
},
"If Over Calorie Limit": {
"main": [
[
{
"node": "Send Calorie Warning via LINE",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Nutrition Info via LINE",
"type": "main",
"index": 0
}
]
]
},
"Process Food Analysis": {
"main": [
[
{
"node": "Extract Nutrition Data",
"type": "main",
"index": 0
}
]
]
},
"Encode Image to Base64": {
"main": [
[
{
"node": "Process Food Analysis",
"type": "main",
"index": 0
}
]
]
},
"Extract Nutrition Data": {
"main": [
[
{
"node": "Append Meal to Sheets",
"type": "main",
"index": 0
}
]
]
},
"Summarize Weekly Stats": {
"main": [
[
{
"node": "Create Weekly Comment with LLM",
"type": "main",
"index": 0
}
]
]
},
"When LINE Event Received": {
"main": [
[
{
"node": "If Image Message",
"type": "main",
"index": 0
}
]
]
},
"Compute Daily Calorie Total": {
"main": [
[
{
"node": "If Over Calorie Limit",
"type": "main",
"index": 0
}
]
]
},
"Gemini Weekly Report Config": {
"ai_languageModel": [
[
{
"node": "Create Weekly Comment with LLM",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Read Weekly Data from Sheets": {
"main": [
[
{
"node": "Summarize Weekly Stats",
"type": "main",
"index": 0
}
]
]
},
"Create Weekly Comment with LLM": {
"main": [
[
{
"node": "Deliver Weekly Report via LINE",
"type": "main",
"index": 0
}
]
]
},
"Read Today's Total from Sheets": {
"main": [
[
{
"node": "Compute Daily Calorie Total",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Health-conscious individuals, people on a diet, and anyone who wants to track daily nutrition without manual data entry. Designed especially for LINE users (Japan, Taiwan, Thailand, etc.) who want an effortless way to monitor calories and macronutrients from meal photos.
Source: https://n8n.io/workflows/15309/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
ANIS_HUB 1. Uses gmail, googleDrive, googleSheets, httpRequest. Webhook trigger; 89 nodes.
Resume Screening & Behavioral Interviews with Gemini, Elevenlabs, & Notion ATS copy. Uses outputParserStructured, chainLlm, googleDrive, stickyNote. Webhook trigger; 67 nodes.
Candidate Engagement | Resume Screening | AI Voice Interviews | Applicant Insights
leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.
Categories: Accounting Automation • OCR Processing • AI Data Extraction • Business Tools