{
  "name": "Youtube Flow",
  "nodes": [
    {
      "parameters": {
        "content": "## 📹 YouTube Data Collection\n\n**This section fetches:**\n- Video metadata (title, channel)\n- Top 100 comments\n- Comment statistics\n\n**API Limits:**\n- Max 100 comments per request\n- Ordered by relevance\n- Top-level comments only",
        "height": 200,
        "width": 280
      },
      "id": "6050ec72-5159-4927-85d0-a42cbf9bb95b",
      "name": "YouTube API Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1552,
        176
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "content": "## 🤖 AI Analysis Section\n\n**Chat Model analyzes comments for:**\n1. Sentiment breakdown (positive/negative/neutral)\n2. Main themes & topics\n3. Common viewer questions\n4. Key feedback points\n5. Actionable insights\n\n**Output Format:** Structured JSON with underscore keys",
        "height": 298,
        "width": 320,
        "color": 5
      },
      "id": "fd5fbfae-7eed-467a-ad87-71bc5ffe8132",
      "name": "AI Analysis Documentation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2288,
        80
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "content": "⚠️ **Data Preparation**\n\nThis node:\n- Extracts comment texts\n- Calculates statistics\n- Performs basic sentiment analysis\n- Prepares data for AI\n\nLimited to 50 comments for AI analysis to manage token usage",
        "height": 180,
        "width": 250,
        "color": 6
      },
      "id": "dee702fc-0687-4a2e-a56e-bfd37ec2776b",
      "name": "Data Processing Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2016,
        80
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Analyze these comments from the video \"{{ $json.videoTitle }}\":\n\nTotal Comments: {{ $json.totalComments }}\nAverage Likes: {{ $json.avgLikes }}\n\nComments to analyze:\n{{ $json.commentTexts }}",
        "options": {
          "systemMessage": "You are a YouTube comment analyst. Analyze the provided comments and return a structured analysis with:\n1. Overall sentiment breakdown (positive/negative/neutral percentages)\n2. Top 3-5 main themes or topics discussed\n3. Common questions from viewers\n4. Key feedback points and suggestions\n5. Actionable insights for content improvement\n\nFormat your response in clear sections with headers in json key value format and use underscore in the key name. Always use following keys for output json: \n1. overall_sentiment_breakdown\n2. main_themes\n3. common_questions\n4. key_feedback_points\n5. actionable_insights",
          "maxIterations": 100
        }
      },
      "id": "09b3d133-7f84-43fb-9b01-87a123217c78",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2320,
        432
      ],
      "typeVersion": 1.9,
      "notes": "Uses LLM to analyze comments and generate insights. Customize the prompt for different analysis needs."
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "chat-default",
          "mode": "list",
          "cachedResultName": "chat-default"
        },
        "options": {
          "responseFormat": "json_object"
        }
      },
      "id": "89a80653-f230-4bbb-8177-4f1ee1227221",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        2320,
        624
      ],
      "typeVersion": 1.2,
      "credentials": {
        "openAiApi": {
          "id": "Nv5QbwF91OrXzdse",
          "name": "OpenAi account"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "219795ef-daa4-4444-9865-c5d3856be63b",
              "name": "videoId",
              "type": "string",
              "value": "={{ $json.Video_ID }}"
            },
            {
              "id": "cd4f519d-4c84-496c-8974-29ef69c890fc",
              "name": "maxComments ",
              "type": "number",
              "value": 100
            }
          ]
        },
        "options": {}
      },
      "id": "1a6dd1f2-69bd-45fd-8c4b-1320b638b924",
      "name": "Set Video Details",
      "type": "n8n-nodes-base.set",
      "position": [
        1312,
        416
      ],
      "typeVersion": 3.4,
      "notes": "Prepares video ID and sets max comments limit (100)"
    },
    {
      "parameters": {
        "resource": "video",
        "operation": "get",
        "videoId": "={{ $json.videoId }}",
        "options": {}
      },
      "id": "f46626ff-b4b9-487a-b72a-dacea3574e5a",
      "name": "Get Youtube Video Details",
      "type": "n8n-nodes-base.youTube",
      "position": [
        1504,
        416
      ],
      "typeVersion": 1,
      "credentials": {
        "youTubeOAuth2Api": {
          "id": "GPcJrW7Hzyw0QaPm",
          "name": "YouTube account"
        }
      },
      "notes": "Fetches video metadata including title, channel name, and other details"
    },
    {
      "parameters": {
        "url": "https://www.googleapis.com/youtube/v3/commentThreads",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "youTubeOAuth2Api",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "part",
              "value": "snippet"
            },
            {
              "name": "videoId",
              "value": "={{ $json.id }}"
            },
            {
              "name": "maxResults",
              "value": "100"
            },
            {
              "name": "order",
              "value": "relevance"
            }
          ]
        },
        "options": {}
      },
      "id": "d49f0dec-912e-4453-be9f-ffce034409bd",
      "name": "Get Youtube Video Comments",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1664,
        416
      ],
      "typeVersion": 4.2,
      "credentials": {
        "youTubeOAuth2Api": {
          "id": "GPcJrW7Hzyw0QaPm",
          "name": "YouTube account"
        }
      },
      "onError": "continueErrorOutput",
      "notes": "Retrieves top 100 comments ordered by relevance using YouTube API"
    },
    {
      "parameters": {
        "jsCode": "// Get comments from HTTP Request node\nconst comments = $input.first().json.items\n//const comments = response.items || [];\n\n// Get video title from the YouTube node (step 5)\n//const videoData = ;\nconst videoTitle = $('Get Youtube Video Details').first().json.snippet.title;\n\n// Extract comment data\nconst processedComments = comments.map(item => {\n  const comment = item.snippet.topLevelComment.snippet;\n  return {\n    text: comment.textDisplay,\n    author: comment.authorDisplayName,\n    likes: comment.likeCount || 0,\n    publishedAt: comment.publishedAt,\n    replyCount: item.snippet.totalReplyCount || 0\n  };\n});\n\n// Calculate statistics\nconst totalComments = processedComments.length;\nconst totalLikes = processedComments.reduce((sum, c) => sum + c.likes, 0);\nconst avgLikes = totalComments > 0 ? (totalLikes / totalComments).toFixed(2) : 0;\nconst totalReplies = processedComments.reduce((sum, c) => sum + c.replyCount, 0);\n\n// Get top comments by likes\nconst topComments = processedComments\n  .sort((a, b) => b.likes - a.likes)\n  .slice(0, 5);\n\n// Prepare comment texts for AI analysis\nconst commentTexts = processedComments\n  .slice(0, 50) // Limit to 50 comments for AI analysis\n  .map(c => c.text)\n  .join('\\n---\\n');\n\n// Basic sentiment analysis (count positive/negative keywords)\nconst positiveWords = ['love', 'great', 'awesome', 'amazing', 'excellent', 'good', 'fantastic', 'helpful', 'thanks'];\nconst negativeWords = ['hate', 'terrible', 'awful', 'bad', 'worst', 'horrible', 'useless', 'waste'];\n\nlet positiveCount = 0;\nlet negativeCount = 0;\n\nprocessedComments.forEach(comment => {\n  const lowerText = comment.text.toLowerCase();\n  positiveWords.forEach(word => {\n    if (lowerText.includes(word)) positiveCount++;\n  });\n  negativeWords.forEach(word => {\n    if (lowerText.includes(word)) negativeCount++;\n  });\n});\n\nreturn [{\n  json: {\n    videoTitle,\n    totalComments,\n    avgLikes,\n    totalReplies,\n    topComments,\n    commentTexts,\n    processedComments,\n    sentimentCounts: {\n      positive: positiveCount,\n      negative: negativeCount,\n      neutral: totalComments - positiveCount - negativeCount\n    }\n  }\n}];\n\n"
      },
      "id": "85139e1d-bf75-4a71-a94e-1f62e41c0863",
      "name": "Prepare Comments Data",
      "type": "n8n-nodes-base.code",
      "position": [
        2112,
        272
      ],
      "typeVersion": 2,
      "notes": "Processes raw comments: extracts text, calculates stats, performs basic sentiment analysis, limits to 50 comments for AI"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const output = JSON.parse($json.output)\n //return output; \n//return output.overall_sentiment_breakdown.Positive;\n\nlet common_question = \"\";\n  \noutput.common_questions.map(rec => {\n  return common_question += \"<li>\"+rec.trim().replace(new RegExp(\".*,$\"), \"\")+\"</li>\"\n})\n\nlet key_feedback_points_and_suggestions = \"\";\n\noutput.key_feedback_points.map(rec => {\n  return key_feedback_points_and_suggestions += \"<li>\"+rec.trim().replace(new RegExp(\".*,$\"), \"\")+\"</li>\"\n})\n\n\nlet actionable_insights_for_content_improvement = \"\"\n\noutput.actionable_insights.map(rec => {\n  return actionable_insights_for_content_improvement += \"<li>\"+rec.trim().replace(new RegExp(\".*,$\"), \"\")+\"</li>\"\n})\n\n\n// return {common_question};\n\n\n     \n\n//return output; \n// Generate the email HTML\nconst emailHTML = `\n<!DOCTYPE html>\n<html>\n<head>\n  <style>\n    body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }\n    .header { background: #f4f4f4; padding: 20px; border-radius: 5px; }\n    .section { margin: 20px 0; }\n    .stats { background: #e9ecef; padding: 15px; border-radius: 5px; }\n    .analysis { background: #f8f9fa; padding: 20px; border-radius: 5px; }\n    h2 { color: #2c3e50; }\n    h3 { color: #34495e; }\n    h4 { color: #7f8c8d; }\n    .footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; font-size: 0.9em; }\n  </style>\n</head>\n<body>\n  <div class=\"header\">\n    <h2>YouTube Comments Analysis Report</h2>\n    <h3>Video: ${$(\"Prepare Comments Data\").item.json.videoTitle}</h3>\n  </div>\n\n  <div class=\"section stats\">\n    <h3> Quick Statistics</h3>\n    <ul>\n      <li><strong>Total Comments Analyzed:</strong> ${$('Prepare Comments Data').item.json.totalComments}</li>\n      <li><strong>Average Likes per Comment:</strong> ${$('Prepare Comments Data').item.json.avgLikes}</li>\n      <li><strong>Total Replies:</strong> ${$('Prepare Comments Data').item.json.totalReplies}</li>\n      <li><strong>Sentiment Summary:</strong> \n        Positive: ${output.overall_sentiment_breakdown.positive}, \n        Negative: ${output.overall_sentiment_breakdown.negative}, \n        Neutral: ${output.overall_sentiment_breakdown.neutral}\n      </li>\n    </ul>\n  </div>\n\n  <div class=\"section stats\">\n    <h3>Common Questions</h3>\n    <ul>\n      ${common_question}\n    </ul>\n  </div>\n\n    <div class=\"section stats\">\n    <h3>Key feedback points and suggestions</h3>\n    <ul>\n      ${key_feedback_points_and_suggestions}\n    </ul>\n  </div>\n\n\n  <div class=\"section stats\">\n    <h3>Actionable insights for content improvement</h3>\n    <ul>\n      ${actionable_insights_for_content_improvement}\n    </ul>\n  </div>\n\n\n\n\n  <div class=\"footer\">\n    <p><em>Report generated on ${new Date().toLocaleString()}</em></p>\n    \n  </div>\n</body>\n</html>`;\n\n\n// Return the formatted data\nreturn  {\n  json: {\n    emailHTML,\n    subject: `YouTube Analysis: ${$('Prepare Comments Data').item.json.videoTitle} - ${$('Prepare Comments Data').item.json.totalComments} comments analyzed`,\n    videoTitle: $('Prepare Comments Data').item.json.videoTitle\n  }\n};"
      },
      "id": "d4c5ae9a-e614-4806-924d-5466ecc766b0",
      "name": "Prepare HTML for Email",
      "type": "n8n-nodes-base.code",
      "position": [
        2688,
        432
      ],
      "typeVersion": 2,
      "notes": "Converts AI analysis into formatted HTML email with statistics, insights, and professional styling"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1RrSSSddyUa-Czxkty1FZ4wOm0tDymHZT31-OYMFa4y0",
          "mode": "list",
          "cachedResultName": "Youtube_IDs",
          "cachedResultUrl": ""
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "",
          "cachedResultUrl": ""
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        928,
        432
      ],
      "id": "3c1b91c3-ec74-4f31-994c-89a7694cca8b",
      "name": "Get row(s) in sheet",
      "alwaysOutputData": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "months",
              "triggerAtHour": 7
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        768,
        432
      ],
      "id": "8ba8a674-06ed-4a2b-8fa0-2d779520b2eb",
      "name": "Schedule Trigger"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "5f1eae10-5270-4e99-8627-597e3451c82c",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.Video_ID }}",
              "rightValue": "Pending"
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "5ff36838-0a97-45dc-9299-0a9b669e943a",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        1088,
        432
      ],
      "typeVersion": 2.2,
      "notes": "Filters rows to process only videos with 'Pending' status"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "ba81dccc-e953-4000-9cc2-ecc8a60deeac",
              "leftValue": "={{ $json.items }}",
              "rightValue": "",
              "operator": {
                "type": "array",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1840,
        352
      ],
      "id": "6b133225-3323-41e6-bece-ab7ae3ce6348",
      "name": "If1"
    },
    {
      "parameters": {
        "content": "True: Sofern Liste mit Inhalt\nFalse: Liste ist leer"
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        576
      ],
      "typeVersion": 1,
      "id": "e0c1a0f3-a91e-4477-a06e-3b9073a5e386",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "Kommentare mit Errorhandling. Videos mit nicht deaktivierten Kommentaren werden im Zweig \"Success\" weitergegeben. "
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1600,
        592
      ],
      "typeVersion": 1,
      "id": "42e6f15b-95c0-4efa-94b4-c8bfbb96a3ad",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "Google Sheet mit Youtube video Ids"
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        864,
        256
      ],
      "typeVersion": 1,
      "id": "9e534d6e-6682-4445-8c19-591a1d7fa86a",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "html": "{{ $json.emailHTML }}"
      },
      "type": "n8n-nodes-base.html",
      "typeVersion": 1.2,
      "position": [
        2896,
        528
      ],
      "id": "17852f46-0e1b-4244-83ce-feb3bfb44e32",
      "name": "HTML"
    },
    {
      "parameters": {
        "html": "={{ $json.emailHTML }}",
        "options": {}
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        2896,
        384
      ],
      "id": "10dbff8b-aa7d-4589-9e03-21263a0d7b0b",
      "name": "Send email",
      "webhookId": "76322ade-8278-4e7b-8f5f-18695947d990"
    },
    {
      "parameters": {
        "jsCode": "/**\n * n8n Code node script: scrape all YouTube video IDs from a channel\n * without the YouTube API. Uses YouTube's internal browse API + continuations.\n */\n\nconst CHANNEL_HANDLE = (items?.[0]?.json?.handle) ?? $input.first().json['Youtube Channel Name'];\n\nconst UA =\n  \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36\";\n\nconst sleep = (ms) => new Promise((r) => setTimeout(r, ms));\n\n// ---------- Small helpers ----------\n\nfunction findBalancedJSON(src, startIndex) {\n  let depth = 0, i = startIndex;\n  let inStr = false, esc = false;\n  for (; i < src.length; i++) {\n    const c = src[i];\n    if (inStr) {\n      if (esc) esc = false;\n      else if (c === \"\\\\\") esc = true;\n      else if (c === '\"') inStr = false;\n    } else {\n      if (c === '\"') inStr = true;\n      else if (c === \"{\") depth++;\n      else if (c === \"}\") {\n        depth--;\n        if (depth === 0) {\n          return JSON.parse(src.slice(startIndex, i + 1));\n        }\n      }\n    }\n  }\n  throw new Error(\"Failed to parse balanced JSON.\");\n}\n\nfunction extractJSONByMarker(html, marker) {\n  const idx = html.indexOf(marker);\n  if (idx === -1) return null;\n  const brace = html.indexOf(\"{\", idx);\n  if (brace === -1) return null;\n  try {\n    return findBalancedJSON(html, brace);\n  } catch {\n    return null;\n  }\n}\n\nfunction extractStringByRegex(html, re) {\n  const m = re.exec(html);\n  return m?.[1] ?? null;\n}\n\nfunction collectVideoIdsFromTree(node, out) {\n  if (!node || typeof node !== \"object\") return;\n  if (node.videoRenderer?.videoId) out.push(node.videoRenderer.videoId);\n  if (node.gridVideoRenderer?.videoId) out.push(node.gridVideoRenderer.videoId);\n  if (node.compactVideoRenderer?.videoId) out.push(node.compactVideoRenderer.videoId);\n  for (const v of Object.values(node)) {\n    if (v && typeof v === \"object\") collectVideoIdsFromTree(v, out);\n  }\n}\n\nfunction findContinuationTokens(node, tokens) {\n  if (!node || typeof node !== \"object\") return;\n  const token =\n    node?.continuationEndpoint?.continuationCommand?.token ||\n    node?.continuationItemRenderer?.continuationEndpoint?.continuationCommand?.token ||\n    node?.nextContinuationData?.continuation;\n  if (typeof token === \"string\") tokens.push(token);\n  for (const v of Object.values(node)) {\n    if (v && typeof v === \"object\") findContinuationTokens(v, tokens);\n  }\n}\n\nfunction uniqPreserveOrder(arr) {\n  const seen = new Set();\n  const out = [];\n  for (const x of arr) if (!seen.has(x)) { seen.add(x); out.push(x); }\n  return out;\n}\n\n// ---------- HTTP wrappers using n8n helpers ----------\n\nasync function fetchHTML(url) {\n  return await this.helpers.httpRequest({\n    method: \"GET\",\n    url,\n    headers: { \"user-agent\": UA, \"accept-language\": \"en-US,en;q=0.9\" },\n    returnFullResponse: false,\n  });\n}\n\nasync function postBrowse(apiKey, body) {\n  const url = `https://www.youtube.com/youtubei/v1/browse?key=${encodeURIComponent(apiKey)}`;\n  return await this.helpers.httpRequest({\n    method: \"POST\",\n    url,\n    body,\n    json: true,\n    headers: {\n      \"content-type\": \"application/json\",\n      \"user-agent\": UA,\n      \"x-youtube-client-name\": \"1\",\n      \"x-youtube-client-version\": \"2.20240718.00.00\",\n    },\n  });\n}\n\n// ---------- Main scraper ----------\n\nasync function getAllVideoIdsFromHandle(handle) {\n  const base = `https://www.youtube.com/${handle.replace(/^@?/, \"@\")}/videos`;\n  const html = await fetchHTML.call(this, base);\n\n  const apiKey =\n    extractStringByRegex(html, /\"INNERTUBE_API_KEY\":\"([^\"]+)\"/) ||\n    extractStringByRegex(html, /INNERTUBE_API_KEY\"\\s*:\\s*\"([^\"]+)\"/);\n  if (!apiKey) throw new Error(\"No INNERTUBE_API_KEY found\");\n\n  let context =\n    extractJSONByMarker(html, \"INNERTUBE_CONTEXT\") ||\n    { client: { clientName: \"WEB\", clientVersion: \"2.20240718.00.00\" } };\n\n  const initial = extractJSONByMarker(html, \"ytInitialData\");\n  if (!initial) throw new Error(\"No ytInitialData found\");\n\n  const ids = [];\n  collectVideoIdsFromTree(initial, ids);\n  const tokens = [];\n  findContinuationTokens(initial, tokens);\n\n  const visited = new Set();\n  while (tokens.length) {\n    const token = tokens.shift();\n    if (!token || visited.has(token)) continue;\n    visited.add(token);\n\n    const resp = await postBrowse.call(this, apiKey, { context, continuation: token });\n    collectVideoIdsFromTree(resp, ids);\n    findContinuationTokens(resp, tokens);\n    await sleep(150);\n  }\n\n  return uniqPreserveOrder(ids).filter((v) => v.length === 11);\n}\n\n// ---------- Run ----------\n\nconst videoIds = await getAllVideoIdsFromHandle.call(this, CHANNEL_HANDLE);\n\nreturn [\n  {\n    json: {\n      channel: CHANNEL_HANDLE,\n      count: videoIds.length,\n      videoIds,\n    },\n  },\n];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        944,
        864
      ],
      "id": "46cd7bcd-67cb-47dd-b1a2-f632355a6ec7",
      "name": "Code"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "73c7ff16-7bde-4a1f-a1df-30ef5ea87918",
              "name": "Youtube Channel Name",
              "value": "",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        768,
        864
      ],
      "id": "51d49050-f946-453d-b61f-5a99bdaf2e30",
      "name": "Set Youtube Channel"
    },
    {
      "parameters": {
        "content": "Alternative um nicht Google Sheets zu verwenden. Verwendet Java Script um die Video IDs zu bekommen. Über Set Youtube Channel kann der Channel definiert werden. "
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        784,
        704
      ],
      "typeVersion": 1,
      "id": "e043e9ae-d5c8-4610-9193-d499642e9a24",
      "name": "Sticky Note3"
    }
  ],
  "pinData": {},
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Prepare HTML for Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Set Video Details": {
      "main": [
        [
          {
            "node": "Get Youtube Video Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Comments Data": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare HTML for Email": {
      "main": [
        [
          {
            "node": "HTML",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Youtube Video Details": {
      "main": [
        [
          {
            "node": "Get Youtube Video Comments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Youtube Video Comments": {
      "main": [
        [
          {
            "node": "If1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get row(s) in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "Set Video Details",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "If1": {
      "main": [
        [
          {
            "node": "Prepare Comments Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Youtube Channel": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTML": {
      "main": [
        []
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "6921918e-44af-4b4b-826a-2c76ba4e85ca",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "0a321ad207b49fe5c7f5d099834b178f3fd05fb3ff318f7cb3ba14afd65867b3"
  },
  "id": "pJNgtCPbjE6we4HW",
  "tags": []
}