AutomationFlowsAI & RAG › Food Image Analysis for Calorie Estimation with Vision AI and Telegram

Food Image Analysis for Calorie Estimation with Vision AI and Telegram

ByToshiya Minami @minami on n8n.io

Teams building health/fitness apps, coaches running check-ins in chat, and anyone who needs quick, structured nutrition insights from food photos—without manual logging.

Event trigger★★★★☆ complexityAI-powered16 nodesAgentOutput Parser StructuredTelegram TriggerOpenRouter ChatGmail
AI & RAG Trigger: Event Nodes: 16 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #9619 — we link there as the canonical source.

This workflow follows the Agent → Gmail 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 →

Download .json
{
  "id": "aZU2RbdXlp3eXCZh",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Infer Ingredients from a Food Image and Estimate Calories",
  "tags": [],
  "nodes": [
    {
      "id": "agent1",
      "name": "AI Agent - \u98df\u6750\u5206\u6790",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        752,
        400
      ],
      "parameters": {
        "text": "=\u3042\u306a\u305f\u306f\u6599\u7406\u3068\u6804\u990a\u306e\u5c02\u9580\u5bb6\u3067\u3059\u3002\u63d0\u4f9b\u3055\u308c\u305f\u6599\u7406\u306e\u753b\u50cf\u3092\u5206\u6790\u3057\u3066\u3001\u4ee5\u4e0b\u306e\u60c5\u5831\u3092\u65e5\u672c\u8a9e\u3067\u63d0\u4f9b\u3057\u3066\u304f\u3060\u3055\u3044\uff1a\n\n1. \u6599\u7406\u540d\n2. \u63a8\u5b9a\u3055\u308c\u308b\u4e3b\u306a\u98df\u6750\u3068\u305d\u306e\u5206\u91cf\uff08\u30b0\u30e9\u30e0\uff09\n3. \u5404\u98df\u6750\u306e\u30ab\u30ed\u30ea\u30fc\n4. \u5408\u8a08\u63a8\u5b9a\u30ab\u30ed\u30ea\u30fc\n5. \u6804\u990a\u30d0\u30e9\u30f3\u30b9\u306e\u7c21\u5358\u306a\u8a55\u4fa1\n\n\u753b\u50cfURL: {{ $json.message.photo[0].file_id }}",
        "options": {
          "systemMessage": "\u4ee5\u4e0b\u306e\u6307\u793a\u306b\u5f93\u3063\u3066\u3001\u30e6\u30fc\u30b6\u30fc\u304c\u5165\u529b\u3057\u305f\u6599\u7406\u306e\u60c5\u5831\u3092\u5206\u6790\u3057\u3001JSON\u5f62\u5f0f\u3067\u51fa\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n1.  **\u5f79\u5272:** \u6599\u7406\u30ec\u30b7\u30d4\u30a2\u30ca\u30e9\u30a4\u30b6\u30fc\n2.  **\u5165\u529b:** \u30e6\u30fc\u30b6\u30fc\u304b\u3089\u306e\u6599\u7406\u540d\u3084\u98df\u6750\u306b\u95a2\u3059\u308b\u30c6\u30ad\u30b9\u30c8\n3.  **\u51e6\u7406:**\n    *   \u6599\u7406\u540d\u3092\u7279\u5b9a\u3059\u308b\u3002\n    *   \u98df\u6750\u30ea\u30b9\u30c8\u3092\u62bd\u51fa\u3057\u3001\u5404\u98df\u6750\u306e\u300c\u540d\u524d(name)\u300d\u300c\u5206\u91cf(amount)\u300d\u300c\u30ab\u30ed\u30ea\u30fc(calories)\u300d\u3092\u7279\u5b9a\u3059\u308b\u3002\u5206\u91cf\u3068\u30ab\u30ed\u30ea\u30fc\u306f\u4e00\u822c\u7684\u306a\u5024\u3092\u63a8\u5b9a\u3057\u3066\u3082\u3088\u3044\u3002\n    *   \u5168\u98df\u6750\u306e\u30ab\u30ed\u30ea\u30fc\u3092\u5408\u8a08\u3057\u3001\u300c\u5408\u8a08\u30ab\u30ed\u30ea\u30fc(totalCalories)\u300d\u3092\u8a08\u7b97\u3059\u308b\u3002\n    *   \u8a08\u7b97\u7d50\u679c\u306b\u57fa\u3065\u304d\u3001\u300c\u6804\u990a\u30d0\u30e9\u30f3\u30b9\u306e\u8a55\u4fa1(nutritionEvaluation)\u300d\u3092\u4f5c\u6210\u3059\u308b\u3002\n4.  **\u51fa\u529b:** \u4ee5\u4e0b\u306eJSON\u30b9\u30ad\u30fc\u30de\u306b\u53b3\u5bc6\u306b\u5f93\u3063\u305fJSON\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u307f\u3092\u51fa\u529b\u3059\u308b\u3002\n    *   `dishName`: string\n    *   `ingredients`: array of objects\n        *   `name`: string\n        *   `amount`: number (\u5358\u4f4d: g)\n        *   `calories`: number (\u5358\u4f4d: kcal)\n    *   `totalCalories`: number (\u5358\u4f4d: kcal)\n    *   `nutritionEvaluation`: string\n5.  **\u6ce8\u610f:**\n    *   \u51fa\u529b\u306bJSON\u4ee5\u5916\u306e\u30c6\u30ad\u30b9\u30c8\uff08\u6328\u62f6\u3001\u8aac\u660e\u306a\u3069\uff09\u3092\u542b\u3081\u306a\u3044\u3067\u304f\u3060\u3055\u3044\u3002\n    *   JSON\u306e\u30ad\u30fc\u540d\u306f\u6307\u5b9a\u901a\u308a\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2
    },
    {
      "id": "parser1",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        912,
        608
      ],
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"dishName\": {\n      \"type\": \"string\",\n      \"description\": \"\u6599\u7406\u540d\"\n    },\n    \"ingredients\": {\n      \"type\": \"array\",\n      \"description\": \"\u98df\u6750\u30ea\u30b9\u30c8\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"name\": {\n            \"type\": \"string\",\n            \"description\": \"\u98df\u6750\u540d\"\n          },\n          \"amount\": {\n            \"type\": \"number\",\n            \"description\": \"\u5206\u91cf\uff08\u30b0\u30e9\u30e0\uff09\"\n          },\n          \"calories\": {\n            \"type\": \"number\",\n            \"description\": \"\u30ab\u30ed\u30ea\u30fc\uff08kcal\uff09\"\n          }\n        }\n      }\n    },\n    \"totalCalories\": {\n      \"type\": \"number\",\n      \"description\": \"\u5408\u8a08\u30ab\u30ed\u30ea\u30fc\uff08kcal\uff09\"\n    },\n    \"nutritionEvaluation\": {\n      \"type\": \"string\",\n      \"description\": \"\u6804\u990a\u30d0\u30e9\u30f3\u30b9\u306e\u8a55\u4fa1\"\n    }\n  },\n  \"required\": [\"dishName\", \"ingredients\", \"totalCalories\", \"nutritionEvaluation\"]\n}"
      },
      "typeVersion": 1.2
    },
    {
      "id": "52bb2509-5870-4f4c-96ea-c607548b15b1",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        480,
        400
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {
          "download": true
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "a8ee11fd-47bc-4524-89a5-a19380041613",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        608,
        608
      ],
      "parameters": {
        "model": "openai/gpt-5-mini",
        "options": {
          "temperature": 0.3
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7397c3e2-c057-4f6b-9afa-010b0a610789",
      "name": "Format for Gmail",
      "type": "n8n-nodes-base.code",
      "position": [
        1152,
        400
      ],
      "parameters": {
        "jsCode": "// AI Agent\u304b\u3089\u51fa\u529b\u3055\u308c\u305fJSON\u5f62\u5f0f\u306e\u6599\u7406\u5206\u6790\u7d50\u679c\u3092\u53d6\u5f97\u3057\u307e\u3059\u3002\nconst analysisResult = $input.first().json.output;\n\n// \u7d50\u679c\u304c\u5b58\u5728\u3057\u306a\u3044\u3001\u307e\u305f\u306f\u671f\u5f85\u3057\u305f\u5f62\u5f0f\u3067\u306a\u3044\u5834\u5408\u306f\u30a8\u30e9\u30fc\u3092\u9632\u3050\u305f\u3081\u306e\u51e6\u7406\nif (!analysisResult || !analysisResult.dishName) {\n  return [{\n    json: {\n      subject: \"\u6599\u7406\u5206\u6790\u30a8\u30e9\u30fc\",\n      htmlBody: \"<p>AI\u306b\u3088\u308b\u5206\u6790\u7d50\u679c\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002</p>\"\n    }\n  }];\n}\n\n// 1. \u30e1\u30fc\u30eb\u306e\u4ef6\u540d\u3092\u4f5c\u6210\nconst subject = `\u3010\u6599\u7406\u5206\u6790\u30ec\u30dd\u30fc\u30c8\u3011 ${analysisResult.dishName}`;\n\n// 2. HTML\u30e1\u30fc\u30eb\u306e\u672c\u6587\u3092\u751f\u6210\nlet htmlBody = `\n<!DOCTYPE html>\n<html lang=\"ja\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <style>\n    body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; line-height: 1.6; color: #333; background-color: #f9f9f9; margin: 0; padding: 0; }\n    .container { max-width: 600px; margin: 20px auto; padding: 25px; background-color: #ffffff; border: 1px solid #e0e0e0; border-radius: 12px; box-shadow: 0 4px 8px rgba(0,0,0,0.05); }\n    h1 { font-size: 26px; color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 10px; margin-top: 0; }\n    h2 { font-size: 20px; color: #e74c3c; margin-bottom: 15px; }\n    h3 { font-size: 18px; color: #34495e; margin-top: 30px; border-bottom: 1px solid #eeeeee; padding-bottom: 8px;}\n    p { margin-top: 0; color: #555; }\n    table { width: 100%; border-collapse: collapse; margin-top: 15px; }\n    th, td { text-align: left; padding: 12px; border-bottom: 1px solid #dddddd; }\n    th { background-color: #f2f2f2; color: #333; }\n    tr:last-child td { border-bottom: none; }\n    .total-calories { text-align: center; background-color: #fff3cd; padding: 15px; border-radius: 8px; margin-top: 20px; }\n    .evaluation { background-color: #eafaf1; padding: 15px; border-left: 5px solid #2ecc71; border-radius: 5px; margin-top: 20px;}\n    .footer { text-align: center; margin-top: 25px; font-size: 12px; color: #999; }\n  </style>\n</head>\n<body>\n  <div class=\"container\">\n    <h1>${analysisResult.dishName}</h1>\n    \n    <div class=\"total-calories\">\n      <h2>\u5408\u8a08\u63a8\u5b9a\u30ab\u30ed\u30ea\u30fc: ${analysisResult.totalCalories} kcal</h2>\n    </div>\n\n    <h3>\u8a73\u7d30\u306a\u98df\u6750\u30ea\u30b9\u30c8</h3>\n    <table>\n      <thead>\n        <tr>\n          <th>\u98df\u6750\u540d</th>\n          <th>\u5206\u91cf (g)</th>\n          <th>\u30ab\u30ed\u30ea\u30fc (kcal)</th>\n        </tr>\n      </thead>\n      <tbody>\n`;\n\n// \u5404\u98df\u6750\u306e\u60c5\u5831\u3092\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u3068\u3057\u3066\u8ffd\u52a0\nif (analysisResult.ingredients && analysisResult.ingredients.length > 0) {\n  analysisResult.ingredients.forEach(ingredient => {\n    htmlBody += `\n        <tr>\n          <td>${ingredient.name}</td>\n          <td>${ingredient.amount}</td>\n          <td>${ingredient.calories}</td>\n        </tr>\n    `;\n  });\n}\n\nhtmlBody += `\n      </tbody>\n    </table>\n\n    <h3>\u6804\u990a\u30d0\u30e9\u30f3\u30b9\u306e\u8a55\u4fa1</h3>\n    <div class=\"evaluation\">\n      <p>${analysisResult.nutritionEvaluation}</p>\n    </div>\n\n    <div class=\"footer\">\n      <p>\u3053\u306e\u30ec\u30dd\u30fc\u30c8\u306fAI\u306b\u3088\u3063\u3066\u81ea\u52d5\u751f\u6210\u3055\u308c\u307e\u3057\u305f\u3002</p>\n    </div>\n  </div>\n</body>\n</html>\n`;\n\n// 3. \u5f8c\u7d9a\u306eGmail\u30ce\u30fc\u30c9\u3067\u4f7f\u3048\u308b\u3088\u3046\u306b\u30c7\u30fc\u30bf\u3092\u8fd4\u3059\nreturn [{\n  json: {\n    subject: subject,\n    htmlBody: htmlBody\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "ae0d956b-599d-4bba-b14b-67245e3287fb",
      "name": "Send a message1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1408,
        400
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "={{ $json.htmlBody }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $json.subject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "9be8d7fb-d689-41bb-b638-755bddd7a618",
      "name": "Sticky 1: Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -16,
        80
      ],
      "parameters": {
        "color": 5,
        "width": 520,
        "height": 260,
        "content": "## \ud83d\uddd2\ufe0f Sticky 1: Overview\n**What this template does**\n- Accepts a **food image** and runs **AI-based ingredient inference** \u2192 **calorie estimation** \u2192 **concise summary**\n- Input can be an **image URL** or **Base64 image** (via Webhook or a messaging app)\n\n**Why it\u2019s useful**\n- Quickly generates a calorie rough estimate and a short nutrition comment for logging, sharing, or feedback loops\n\n**No hardcoded secrets**\n- All API keys must be set via **Credentials/Environment Variables** (never hardcode keys in nodes)"
      },
      "typeVersion": 1
    },
    {
      "id": "0bd3d0eb-a030-4d79-a390-17e41d705f7e",
      "name": "Sticky 2: Prerequisites",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        -16
      ],
      "parameters": {
        "color": 4,
        "width": 520,
        "height": 280,
        "content": "## \ud83d\uddd2\ufe0f Sticky 2: Prerequisites & Credentials\n**Connections**\n- LLM (OpenAI or compatible) with **vision** capability\n- One input channel (Webhook **or** Telegram/LINE, etc.)\n\n**Environment Variables (examples)**\n- `LLM_MODEL` (e.g., `gpt-4o` or any vision-capable model)\n- `LLM_TEMPERATURE` (e.g., `0.3`)\n- `WEBHOOK_SECRET` (optional, if you validate requests)\n\n**Scopes / Permissions**\n- If image URLs are external, ensure they are publicly accessible or use signed/temporary URLs"
      },
      "typeVersion": 1
    },
    {
      "id": "ff6d1495-3223-4305-a354-57a22adf71f3",
      "name": "Sticky 3: Input Format",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -144,
        560
      ],
      "parameters": {
        "color": 2,
        "width": 520,
        "height": 300,
        "content": "## \ud83d\uddd2\ufe0f Sticky 3: Input Format (Sample)\n**Expected JSON**\n```json\n{\n  \"imageUrl\": \"https://example.com/dish.jpg\"\n  // or\n  // \"imageBase64\": \"data:image/jpeg;base64,....\"\n}\n```\n**Notes**\n- Template assumes **one image** per request (extend with arrays for multiple images)\n- If you receive platform-specific payloads (e.g., Telegram `file_id`, LINE message objects), **normalize** them first to `imageUrl` or `imageBase64` before calling the LLM"
      },
      "typeVersion": 1
    },
    {
      "id": "1bd2ee7d-a5f9-42f4-9974-4698fb05b553",
      "name": "Sticky 4: Model & Prompt",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        528,
        864
      ],
      "parameters": {
        "color": 3,
        "width": 520,
        "height": 260,
        "content": "## \ud83d\uddd2\ufe0f Sticky 4: Model & Prompt Policy\n**Model**\n- Use a **vision-capable** LLM (e.g., `gpt-4o`)\n- Keep `temperature` low (0.2\u20130.3) for stable outputs\n\n**Prompt Rules**\n- The model must return **strict JSON only**, with the fields below (no extra text)\n- Keep names concise (e.g., \"grilled chicken\", \"white rice\", \"mixed salad\")\n- Provide amounts in grams when possible (estimates are fine)"
      },
      "typeVersion": 1
    },
    {
      "id": "84f0f90c-e819-4022-a76e-b63353ca9968",
      "name": "Sticky 5: Output Schema",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1184,
        -16
      ],
      "parameters": {
        "width": 520,
        "height": 280,
        "content": "## \ud83d\uddd2\ufe0f Sticky 5: Output Schema (Fixed)\n**JSON structure**\n```json\n{\n  \"dishName\": \"string\",\n  \"ingredients\": [\n    { \"name\": \"string\", \"amount\": 0, \"calories\": 0 }\n  ],\n  \"totalCalories\": 0,\n  \"nutritionEvaluation\": \"string\"\n}\n```\n**Validation**\n- If fields are empty/missing, route to an **error branch** and return a helpful message for the user"
      },
      "typeVersion": 1
    },
    {
      "id": "fe6e558b-4f6a-4c0a-9b06-e3939b07be3f",
      "name": "Sticky 6: Test Steps",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1200,
        816
      ],
      "parameters": {
        "color": 5,
        "width": 520,
        "height": 220,
        "content": "## \ud83d\uddd2\ufe0f Sticky 6: Test Steps (Under 1 Minute)\n1) Turn on the **Webhook** node and copy the **Test URL**\n2) Send a sample payload with a valid `imageUrl`\n3) Confirm the run returns the **strict JSON** structure\n4) If using Telegram/Slack replies, authenticate and send a test message to verify delivery"
      },
      "typeVersion": 1
    },
    {
      "id": "24bbd216-9c8b-4a4f-b446-dd82585117fd",
      "name": "Sticky 7: Errors & Limits",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1696,
        0
      ],
      "parameters": {
        "color": 4,
        "width": 520,
        "height": 260,
        "content": "## \ud83d\uddd2\ufe0f Sticky 7: Error Handling & Limits\n**Retries**\n- For LLM calls, implement **exponential backoff** on 429/5xx\n- If the image fetch fails, **retry**, then go to **error branch**\n\n**Limits**\n- Watch for model **context/vision limits**; downscale/thumbnail images if needed\n- When rate-limited, use **Delay** + **Retry** to recover gracefully"
      },
      "typeVersion": 1
    },
    {
      "id": "55619913-3b70-43d8-ab38-f5b8eb4a6375",
      "name": "Sticky 8: Security",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1776,
        784
      ],
      "parameters": {
        "color": 3,
        "width": 520,
        "height": 220,
        "content": "## \ud83d\uddd2\ufe0f Sticky 8: Security & PII\n- Store API keys/tokens in **Credentials/Env Vars** (never in node fields)\n- If image URLs are private, use **signed/temporary URLs**\n- **Do not log** personal data; mask/remove PII from execution logs\n- Use **HTTPS only** for all external requests"
      },
      "typeVersion": 1
    },
    {
      "id": "95833663-f776-40c5-9225-30f07e580acb",
      "name": "Sticky 9: Delivery",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2272,
        0
      ],
      "parameters": {
        "color": 2,
        "width": 520,
        "height": 220,
        "content": "## \ud83d\uddd2\ufe0f Sticky 9: Delivery Options (No Gmail)\n- Reply via **Telegram/Slack** or return via **HTTP Response**\n- Optionally log results to **Google Sheets** or **Notion** for history/analytics\n- If email is required, use **SMTP** or a non-Gmail provider (per your stack)"
      },
      "typeVersion": 1
    },
    {
      "id": "d1f31505-89ed-4483-bb52-d1f98c02db36",
      "name": "Sticky 10: Extensibility",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1840,
        400
      ],
      "parameters": {
        "width": 520,
        "height": 240,
        "content": "## \ud83d\uddd2\ufe0f Sticky 10: Extensibility (Review Bonus)\n- **Multilingual output** (ja/en) switch\n- Normalize serving units (piece/slice) \u2192 **gram conversion**\n- Categorize components (main dish / side / sauce)\n- Add **evidence-based** nutrition comments (e.g., high fat/high carb) with short rationale"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "timezone": "Asia/Tokyo",
    "errorWorkflow": "",
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "saveExecutionProgress": true,
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all"
  },
  "versionId": "8766f935-4d36-4d19-9f54-9179e0a59262",
  "connections": {
    "Format for Gmail": {
      "main": [
        [
          {
            "node": "Send a message1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "AI Agent - \u98df\u6750\u5206\u6790",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent - \u98df\u6750\u5206\u6790",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent - \u98df\u6750\u5206\u6790": {
      "main": [
        [
          {
            "node": "Format for Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent - \u98df\u6750\u5206\u6790",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Teams building health/fitness apps, coaches running check-ins in chat, and anyone who needs quick, structured nutrition insights from food photos—without manual logging.

Source: https://n8n.io/workflows/9619/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Pitch Paul. Uses lmChatOpenRouter, telegram, outputParserStructured, supabaseTool. Event-driven trigger; 33 nodes.

OpenRouter Chat, Telegram, Output Parser Structured +10
AI & RAG

RAG CHATBOT Main. Uses telegram, telegramTrigger, lmChatOpenAi, n8n-nodes-mcp. Event-driven trigger; 87 nodes.

Telegram, Telegram Trigger, OpenAI Chat +8
AI & RAG

Who is this for? Agencies, consultants, and service providers who conduct discovery calls and need to quickly turn conversations into professional proposals.

Tool Think, Tool Calculator, Agent Tool +18
AI & RAG

This workflow transforms your Telegram bot into an intelligent creative assistant. It can chat conversationally, fetch trending image prompts from PromptHero for inspiration, or perform a deep "remix"

Telegram Trigger, Output Parser Structured, Telegram +6
AI & RAG

leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.

Supabase, Gmail, Form Trigger +13