# templates

name: Automated Stock Analysis Reports with Technical & News Sentiment using GPT-4o
author_name: Elay Guez
author_username: elay96
categories: []
created_at: 2025-04-29T17:33:05.653Z
updated_at: 2025-04-29T17:33:05.653Z
url: https://n8n.io/workflows/3790
scraped_at: 2025-06-19 22:07:56
description:
  Stock Analysis Agent (Hebrew, RTL, GPT-4o)

  Overview

  Get comprehensive stock analysis with this AI-powered workflow that provides actionable insights for your investment decisions. On a weekly basis, this workflow:

  Analyzes stock data from multiple sources (Chart-img, Twelve Data API, Alphavantage)
  Performs technical analysis using advanced indicators (RSI, MACD, Bollinger Bands, Resistance and Support Levels)
  Scans financial news from Alpha Vantage to capture market sentiment
  Uses OpenAI's GPT-4o to identify patterns, trends, and trading opportunities
  Generates a fully styled, responsive HTML email (with proper RTL layout) in Hebrew
  Sends detailed recommendations directly to your inbox

  Perfect for investors, traders, and financial analysts who want data-driven stock insights - combining technical indicators with news sentiment for more informed decisions.

  Setup Instructions

  Estimated setup time:
  15 minutes

  Required credentials:
  OpenAI API Key
  Chart-img API Key (free tier)
  Twelve Data API Key (free tier)
  Alpha Vantage API Key (free tier)
  SMTP credentials (for email delivery)

  Steps:

  Import this template into your n8n instance.
  Add your API keys under credentials.
  Configure the SMTP Email node with: Host (e.g., smtp.gmail.com), Port (465 or 587), Username (your email), Password (app-specific password or login).
  Activate the workflow.
  Fill in the Form.
  Enjoy! (Check your Spam mailbox)

  Customization Tips

  Modify the analysis timeframe (daily, weekly, monthly)
  Add integrations with trading platforms or portfolio management tools
  Adjust the recommendation criteria based on your risk tolerance

  Why Use This?
  This is more than just stock data. It's an intelligent financial assistant that combines technical analysis with market sentiment to provide actionable recommendations - automatically.

  Important Note:
  This report is being generated automatically and does not constitute an investment recommendation. Please consult a licensed investment advisor before making any investment decisions.

nodes_used:
  ["n8n-nodes-base.emailSend","n8n-nodes-base.httpRequest","n8n-nodes-base.code","n8n-nodes-base.html","@n8n/n8n-nodes-langchain.agent","@n8n/n8n-nodes-langchain.lmChatOpenAi","@n8n/n8n-nodes-langchain.memoryBufferWindow","@n8n/n8n-nodes-langchain.outputParserStructured","@n8n/n8n-nodes-langchain.toolWorkflow","@n8n/n8n-nodes-langchain.openAi","@n8n/n8n-nodes-langchain.toolThink"]

workflow_json:
  {"meta":{"instanceId":"6c3d8936583f8a98fa8ebe06f510117c0e8fff2df771e73deba4126a853eb55e"},"nodes":[{"id":"a9bbe9d0-51aa-40f8-8931-f405c695c732","name":"Window Buffer Memory","type":"@n8n/n8n-nodes-langchain.memoryBufferWindow","position":[1140,140],"parameters":{"sessionKey":"=335458847","sessionIdType":"customKey"},"typeVersion":1.3},{"id":"2d6315d6-959d-4e16-97ed-30839d826ce2","name":"AI Agent","type":"@n8n/n8n-nodes-langchain.agent","position":[1080,-100],"parameters":{"text":"=Ticker = {{ $json[\"Ticker symbol:\"] }}","options":{"systemMessage":"=# Overview\nYou are an AI agent specialized in stock analysis. You provide technical analysis and sentiment for stock investments by combining chart data and news sentiment.\n\n# Instructions\n1. When a user requests an analysis of a stock with its symbol:\n  - Send the stock symbol to both tools **technical_analysis** and **trends_analysis**\n  - Analyze the combined data and prepare a JSON report with your insights\n  - Provide a clear recommendation (positive, neutral, or negative)\n2. Your output must be in the format of a structured JSON object that will be used to fill an HTML template.\n3. Translate the article titles in topArticles to Hebrew\n4. Translate the sentimentHebrew results to only one of these values:\n\"חיובי-חזק/חיובי-חלש/נייטרלי/שלילי-חלש/שלילי-חזק\". Somewhat=חלש.\n5. Write the Date value in each article: \"topArticles\" only in this format: \"DD/MM/YYYY\".\n6. Update the technicalAnalysis value as a detailed technical analysis of three paragraphs, which explains even to those who don't understand economics what you did and how you reached your conclusions. Touch on all the indicators examined (Volume, EMA, RSI, Fibonacci retracement, MACD, Bollinger bands, Resistance and support levels)\n7. Ensure that the text in the technicalAnalysis value is written in proper Hebrew, like a professional analyst. Use the think tool\n8. In the Recommendation value - recommend to buy or sell only if you think with high probability that there will be a rise or fall. Use the think tool to verify your Recommendation based on recommendationText. Advise something only if you really believe it. Your default is the \"ממליץ לחכות\" value.\n\n## Tools\n- **technical_analysis**: Generates technical analysis based on stock charts\n- **trends_analysis**: Analyzes news sentiment for the requested stock\n\n## Response Format\nYou must respond with a JSON object containing exactly the following keys to fill the HTML template:\n\n```json\n{\n \"stockSymbol\": \"סימול\",\n \"analysisDate\": \"DD/MM/YYYY\",\n \"recommendationClass\": \"positive/neutral/negative\",\n \"recommendationTitle\": \"כותרת המלצה בעברית\",\n \"recommendationText\": \"הסבר מפורט של ההמלצה בעברית\",\n \"bullishCount\": 0,\n \"neutralCount\": 0, \n \"bearishCount\": 0,\n \"bullishHeight\": 0,\n \"neutralHeight\": 0,\n \"bearishHeight\": 0,\n \"overallSentiment\": \"חיובי/נייטרלי/שלילי\",\n \"Recommendation\": \"ממליץ לקנות/ ממליץ לחכות/ ממליץ למכור\",\n \"sentimentScore\": 0.00,\n \"chartImageUrl\": \"URL_PLACEHOLDER\",\n \"technicalAnalysis\": \"ניתוח טכני מפורט בעברית עם תגי <p>\",\n \"topArticles\": [\n   {\n     \"title\": \"כותרת המאמר בעברית\",\n     \"url\": \"כתובת URL של המאמר\",\n     \"source\": \"שם המקור באנגלית\",\n     \"date\": \"DD/MM/YYYY\",\n     \"sentimentClass\": \"bullish/neutral/bearish\",\n     \"sentimentHebrew\": \"חיובי-חזק/חיובי-חלש/נייטרלי/שלילי-חלש/שלילי-חזק\"\n   }\n ],\n \"hotTopics\": [\n   {\n     \"topic\": \"שם הנושא בעברית\",\n     \"article_count\": 0,\n     \"average_relevance\": \"0.00\"\n   }\n ]\n}"},"promptType":"define","hasOutputParser":true},"typeVersion":1.7},{"id":"14112026-19eb-493f-971b-28455a8d4412","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[680,-220],"parameters":{"color":4,"width":1820,"height":580,"content":"# AI Agent\nAI agent powered by GPT-4o that analyses stocks by combining technical analysis and news sentiment, generating detailed reports in Hebrew with data-driven investment recommendations"},"typeVersion":1},{"id":"8b2e573e-7acc-4b0b-a708-4ce33873a893","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[680,380],"parameters":{"width":2820,"height":920,"content":"# Technical Analysis Tool\nA tool that performs in-depth technical analysis of stock charts by combining visual pattern recognition with quantitative indicators. It fetches data from Chart-img API for generating visual charts, Twelve Data API for historical prices and technical indicators (Bollinger Bands, MACD), and uses OpenAI's GPT-4o for visual chart pattern recognition.\nThe system synthesizes this multi-source data into a comprehensive technical assessment with actionable trading insights based on support/resistance levels, Fibonacci retracements, and candlestick patterns."},"typeVersion":1},{"id":"b0d49fa6-5c57-4ab5-a752-93d7d278b8fa","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[2520,-220],"parameters":{"width":980,"height":580,"content":"# Trends Analysis Tool\nA tool that analyses news sentiment for requested stocks by fetching recent financial news articles, calculating sentiment metrics, identifying influential stories, and extracting trending topics. It processes data from Alpha Vantage's news API, determines overall market sentiment, and delivers structured analysis on stock sentiment, relevance, and market outlook."},"typeVersion":1},{"id":"13a242cf-0a01-4aea-a58e-9b734aed912c","name":"Structured Output Parser","type":"@n8n/n8n-nodes-langchain.outputParserStructured","position":[1900,140],"parameters":{"schemaType":"manual","inputSchema":"{\n  \"stockSymbol\": \"סימול\",\n  \"analysisDate\": \"DD/MM/YYYY\",\n  \"recommendationClass\": \"positive/neutral/negative\",\n  \"recommendationTitle\": \"כותרת המלצה בעברית\",\n  \"recommendationText\": \"הסבר מפורט של ההמלצה בעברית\",\n  \"bullishCount\": 0,\n  \"neutralCount\": 0, \n  \"bearishCount\": 0,\n  \"bullishHeight\": 0,\n  \"neutralHeight\": 0,\n  \"bearishHeight\": 0,\n  \"overallSentiment\": \"חיובי/נייטרלי/שלילי\",\n  \"Recommendation\": \"ממליץ לקנות/ ממליץ לחכות/ ממליץ למכור\",\n  \"sentimentScore\": 0.00,\n  \"chartImageUrl\": \"URL_PLACEHOLDER\",\n  \"technicalAnalysis\": \"ניתוח טכני מפורט בעברית עם תגי <p>\",\n  \"topArticles\": [\n    {\n      \"title\": \"כותרת המאמר\",\n      \"url\": \"כתובת URL של המאמר\",\n      \"source\": \"שם המקור\",\n      \"date\": \"DD/MM/YYYY\",\n      \"sentimentClass\": \"bullish/neutral/bearish\",\n      \"sentimentHebrew\": \"חיובי-חזק/חיובי-חלש/נייטרלי/שלילי-חלש/שלילי-חזק\"\n    }\n  ],\n  \"hotTopics\": [\n    {\n      \"topic\": \"שם הנושא בעברית\",\n      \"article_count\": 0,\n      \"average_relevance\": \"0.00\"\n    }\n  ]\n}"},"typeVersion":1.2},{"id":"bb5dd63a-a3e6-408e-a5c9-13e9f72f2b26","name":"GPT 4o","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","position":[960,140],"parameters":{"model":{"__rl":true,"mode":"list","value":"gpt-4o","cachedResultName":"gpt-4o"},"options":{}},"credentials":{"openAiApi":{"id":"2m1HH5crgPAhTJlv","name":"OpenAi account"}},"typeVersion":1.2},{"id":"94d820d2-eb20-4184-8e21-1ed5936c9166","name":"Generate HTML","type":"n8n-nodes-base.html","position":[1860,-100],"parameters":{"html":"<!DOCTYPE html>\n<html dir=\"rtl\" lang=\"he\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>ניתוח מניית {{ $('AI Agent').item.json.output.stockSymbol }}</title>\n</head>\n<body style=\"margin: 0; padding: 0; font-family: 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif; background-color: #f5f7fa; color: #333; line-height: 1.6; -webkit-font-smoothing: antialiased; font-size: 16px; text-align: right; direction: rtl;\">\n    <!-- עוטף ראשי -->\n    <div style=\"max-width: 650px; margin: 0 auto; background-color: #ffffff; border-radius: 16px; overflow: hidden; box-shadow: 0 4px 24px rgba(0,0,0,0.08); margin-top: 30px; margin-bottom: 30px; text-align: right; direction: rtl;\">\n        \n        <!-- כותרת עליונה -->\n        <div style=\"background: linear-gradient(135deg, #0057ff 0%, #00b2ff 100%); padding: 30px 40px; text-align: center; position: relative; overflow: hidden; margin-bottom: 20px;\">\n            <div style=\"position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxwYXR0ZXJuIGlkPSJwYXR0ZXJuIiB4PSIwIiB5PSIwIiB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHBhdHRlcm5UcmFuc2Zvcm09InJvdGF0ZSgzMCkiPjxwYXRoIGQ9Ik0wIDEwIEw0MCAxMCIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLW9wYWNpdHk9IjAuMSIvPjwvcGF0dGVybj48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNwYXR0ZXJuKSIvPjwvc3ZnPg=='); opacity: 0.2;\"></div>\n            <h1 style=\"color: #ffffff; font-weight: 700; font-size: 28px; margin: 0 0 5px 0; letter-spacing: -0.5px; position: relative;\">ניתוח מניית {{ $('AI Agent').item.json.output.stockSymbol }}</h1>\n            <div style=\"color: rgba(255,255,255,0.85); font-size: 15px; position: relative;\">תאריך: {{ $('AI Agent').item.json.output.analysisDate }}</div>\n        </div>\n        \n        <!-- תוכן המייל -->\n        <div style=\"padding: 40px; text-align: right; direction: rtl;\">\n            \n            <!-- תיבת המלצה -->\n            <div style=\"background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 12px rgba(0,0,0,0.04); padding: 25px; margin-bottom: 40px; position: relative; overflow: hidden; text-align: right;\">\n                <div style=\"position: absolute; right: 0; top: 0; bottom: 0; width: 6px; background-color: #f7b955;\"></div>\n                <div style=\"position: absolute; right: 0; top: 0; width: 100%; height: 100%; background: linear-gradient(90deg, rgba(247, 185, 85, 0.07) 0%, rgba(247, 185, 85, 0) 50%);\"></div>\n                <div style=\"text-align: center; position: relative;\">\n                    <div style=\"display: inline-block; width: 40px; height: 40px; border-radius: 50%; margin-bottom: 10px; background-color: rgba(247, 185, 85, 0.15); text-align: center;\">\n                        <span style=\"font-size: 20px; line-height: 40px;\">⚖️</span>\n                    </div>\n                    <h2 style=\"margin: 0 0 10px 0; color: #f7b955; font-size: 22px; font-weight: 700; text-align: center;\">{{ $('AI Agent').item.json.output.recommendationTitle }}</h2>\n                    <p style=\"margin: 0; font-size: 16px; line-height: 1.6; color: #4a5568; text-align: right;\">{{ $json.message.content.recommendationText }}</p>\n                    <div style=\"margin-top: 25px;\">\n                        <a style=\"display: inline-block; background-color: #29cc7a; color: white; font-weight: 600; font-size: 16px; padding: 12px 30px; border-radius: 8px; text-decoration: none; box-shadow: 0 4px 6px rgba(41, 204, 122, 0.25); transition: all 0.2s ease;\">{{ $('AI Agent').item.json.output.Recommendation }}</a>\n                    </div>\n                </div>\n            </div>\n\n            <!-- ניתוח טכני -->\n            <div style=\"margin-bottom: 40px; text-align: right;\">\n                <h2 style=\"font-size: 20px; color: #1a202c; margin: 0 0 20px 0; padding-bottom: 12px; border-bottom: 1px solid #edf2f7; font-weight: 700; text-align: right;\">ניתוח טכני</h2>\n                \n                <div style=\"background: #ffffff; border-radius: 12px; box-shadow: 0 2px 12px rgba(0,0,0,0.06); overflow: hidden; margin-bottom: 25px;\">\n                    <img src=\"{{ $('AI Agent').item.json.output.chartImageUrl }}\" alt=\"גרף טכני {{ $('AI Agent').item.json.output.stockSymbol }}\" style=\"width: 100%; display: block; max-height: 450px; object-fit: contain;\">\n                </div>\n                \n                <div style=\"background-color: #f8fafc; border-radius: 12px; padding: 25px; font-size: 15px; line-height: 1.6; color: #4a5568; text-align: right;\">\n                    {{ $json.message.content.technicalAnalysis }}\n                </div>\n            </div>\n                      \n            <!-- ניתוח סנטימנט -->\n            <div style=\"margin-bottom: 40px; text-align: right;\">\n                <h2 style=\"font-size: 20px; color: #1a202c; margin: 0 0 20px 0; padding-bottom: 12px; border-bottom: 1px solid #edf2f7; font-weight: 700; text-align: right;\">ניתוח סנטימנט שוק</h2>\n                \n                <!-- גרף סנטימנט - עם טבלה במקום flex -->\n                <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin: 45px 0 30px 0;\">\n                    <tr valign=\"bottom\" align=\"center\">\n                        <td width=\"33%\" style=\"text-align: center; padding: 0 10px;\">\n                            <div style=\"font-weight: 600; margin-bottom: 10px; color: #29cc7a;\">{{ $('AI Agent').item.json.output.bullishCount }}</div>\n                            <div style=\"background-color: #29cc7a; border-radius: 8px 8px 0 0; width: 100%; height: {{ $('AI Agent').item.json.output.bullishHeight }}px; margin: 0 auto; opacity: 0.85;\"></div>\n                            <div style=\"font-size: 14px; color: #4a5568; margin-top: 10px;\">חיובי</div>\n                        </td>\n                        <td width=\"33%\" style=\"text-align: center; padding: 0 10px;\">\n                            <div style=\"font-weight: 600; margin-bottom: 10px; color: #f7b955;\">{{ $('AI Agent').item.json.output.neutralCount }}</div>\n                            <div style=\"background-color: #f7b955; border-radius: 8px 8px 0 0; width: 100%; height: {{ $('AI Agent').item.json.output.neutralHeight }}px; margin: 0 auto; opacity: 0.85;\"></div>\n                            <div style=\"font-size: 14px; color: #4a5568; margin-top: 10px;\">נייטרלי</div>\n                        </td>\n                        <td width=\"33%\" style=\"text-align: center; padding: 0 10px;\">\n                            <div style=\"font-weight: 600; margin-bottom: 10px; color: #f55e5e;\">{{ $('AI Agent').item.json.output.bearishCount }}</div>\n                            <div style=\"background-color: #f55e5e; border-radius: 8px 8px 0 0; width: 100%; height: {{ $('AI Agent').item.json.output.bearishHeight }}px; margin: 0 auto; opacity: 0.85;\"></div>\n                            <div style=\"font-size: 14px; color: #4a5568; margin-top: 10px;\">שלילי</div>\n                        </td>\n                    </tr>\n                </table>\n                \n                <div style=\"background-color: #f8fafc; border-radius: 10px; padding: 15px; text-align: center; font-size: 15px;\">\n                    הסנטימנט הכללי למניית <strong>{{ $('AI Agent').item.json.output.stockSymbol }}</strong> הוא \n                    <span style=\"font-weight: 600; color: #f7b955;\">{{ $('AI Agent').item.json.output.overallSentiment }}</span> \n                    עם ציון של <strong>{{ $('AI Agent').item.json.output.sentimentScore }}</strong>\n                </div>\n            </div>\n            \n            <!-- מאמרים משפיעים -->\n            <div style=\"margin-bottom: 40px; text-align: right;\">\n                <h2 style=\"font-size: 20px; color: #1a202c; margin: 0 0 20px 0; padding-bottom: 12px; border-bottom: 1px solid #edf2f7; font-weight: 700; text-align: right;\">מאמרים משפיעים</h2>\n                \n                <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse;\">\n                    <!-- מאמר 1 -->\n                    <tr>\n                        <td style=\"padding-bottom: 16px;\">\n                            <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); overflow: hidden;\">\n                                <tr>\n                                    <td width=\"4\" style=\"background-color: #f7b955;\"></td>\n                                    <td style=\"padding: 18px 22px;\">\n                                        <h3 style=\"margin: 0 0 8px 0; font-size: 16px; font-weight: 600; line-height: 1.4; text-align: right;\">\n                                            <a href=\"{{ $('AI Agent').item.json.output.topArticles[0].url }}\" target=\"_blank\" style=\"color: #2b6cb0; text-decoration: none;\">{{ $('AI Agent').item.json.output.topArticles[0].title }}</a>\n                                        </h3>\n                                        <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin-top: 10px;\">\n                                            <tr>\n                                                <td style=\"font-size: 13px; color: #718096; text-align: right;\">{{ $('AI Agent').item.json.output.topArticles[0].source }} | {{ $('AI Agent').item.json.output.topArticles[0].date }}</td>\n                                                <td style=\"text-align: left;\">\n                                                    <div style=\"display: inline-block; padding: 3px 10px; border-radius: 30px; font-weight: 500; font-size: 12px; background-color: rgba(247, 185, 85, 0.1); color: #f7b955;\">\n                                                        {{ $('AI Agent').item.json.output.topArticles[0].sentimentHebrew }}\n                                                    </div>\n                                                </td>\n                                            </tr>\n                                        </table>\n                                    </td>\n                                </tr>\n                            </table>\n                        </td>\n                    </tr>\n                    \n                    <!-- מאמר 2 -->\n                    <tr>\n                        <td style=\"padding-bottom: 16px;\">\n                            <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); overflow: hidden;\">\n                                <tr>\n                                    <td width=\"4\" style=\"background-color: #f7b955;\"></td>\n                                    <td style=\"padding: 18px 22px;\">\n                                        <h3 style=\"margin: 0 0 8px 0; font-size: 16px; font-weight: 600; line-height: 1.4; text-align: right;\">\n                                            <a href=\"{{ $('AI Agent').item.json.output.topArticles[1].url }}\" target=\"_blank\" style=\"color: #2b6cb0; text-decoration: none;\">{{ $('AI Agent').item.json.output.topArticles[1].title }}</a>\n                                        </h3>\n                                        <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin-top: 10px;\">\n                                            <tr>\n                                                <td style=\"font-size: 13px; color: #718096; text-align: right;\">{{ $('AI Agent').item.json.output.topArticles[1].source }} | {{ $('AI Agent').item.json.output.topArticles[1].date }}</td>\n                                                <td style=\"text-align: left;\">\n                                                    <div style=\"display: inline-block; padding: 3px 10px; border-radius: 30px; font-weight: 500; font-size: 12px; background-color: rgba(247, 185, 85, 0.1); color: #f7b955;\">\n                                                        {{ $('AI Agent').item.json.output.topArticles[1].sentimentHebrew }}\n                                                    </div>\n                                                </td>\n                                            </tr>\n                                        </table>\n                                    </td>\n                                </tr>\n                            </table>\n                        </td>\n                    </tr>\n                    \n                    <!-- מאמר 3 -->\n                    <tr>\n                        <td style=\"padding-bottom: 16px;\">\n                            <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); overflow: hidden;\">\n                                <tr>\n                                    <td width=\"4\" style=\"background-color: #f7b955;\"></td>\n                                    <td style=\"padding: 18px 22px;\">\n                                        <h3 style=\"margin: 0 0 8px 0; font-size: 16px; font-weight: 600; line-height: 1.4; text-align: right;\">\n                                            <a href=\"{{ $('AI Agent').item.json.output.topArticles[2].url }}\" target=\"_blank\" style=\"color: #2b6cb0; text-decoration: none;\">{{ $('AI Agent').item.json.output.topArticles[2].title }}</a>\n                                        </h3>\n                                        <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin-top: 10px;\">\n                                            <tr>\n                                                <td style=\"font-size: 13px; color: #718096; text-align: right;\">{{ $('AI Agent').item.json.output.topArticles[2].source }} | {{ $('AI Agent').item.json.output.topArticles[2].date }}</td>\n                                                <td style=\"text-align: left;\">\n                                                    <div style=\"display: inline-block; padding: 3px 10px; border-radius: 30px; font-weight: 500; font-size: 12px; background-color: rgba(247, 185, 85, 0.1); color: #f7b955;\">\n                                                        {{ $('AI Agent').item.json.output.topArticles[2].sentimentHebrew }}\n                                                    </div>\n                                                </td>\n                                            </tr>\n                                        </table>\n                                    </td>\n                                </tr>\n                            </table>\n                        </td>\n                    </tr>\n                    \n                    <!-- מאמר 4 -->\n                    <tr>\n                        <td style=\"padding-bottom: 16px;\">\n                            <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); overflow: hidden;\">\n                                <tr>\n                                    <td width=\"4\" style=\"background-color: #f7b955;\"></td>\n                                    <td style=\"padding: 18px 22px;\">\n                                        <h3 style=\"margin: 0 0 8px 0; font-size: 16px; font-weight: 600; line-height: 1.4; text-align: right;\">\n                                            <a href=\"{{ $('AI Agent').item.json.output.topArticles[3].url }}\" target=\"_blank\" style=\"color: #2b6cb0; text-decoration: none;\">{{ $('AI Agent').item.json.output.topArticles[3].title }}</a>\n                                        </h3>\n                                        <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin-top: 10px;\">\n                                            <tr>\n                                                <td style=\"font-size: 13px; color: #718096; text-align: right;\">{{ $('AI Agent').item.json.output.topArticles[3].source }} | {{ $('AI Agent').item.json.output.topArticles[3].date }}</td>\n                                                <td style=\"text-align: left;\">\n                                                    <div style=\"display: inline-block; padding: 3px 10px; border-radius: 30px; font-weight: 500; font-size: 12px; background-color: rgba(247, 185, 85, 0.1); color: #f7b955;\">\n                                                        {{ $('AI Agent').item.json.output.topArticles[3].sentimentHebrew }}\n                                                    </div>\n                                                </td>\n                                            </tr>\n                                        </table>\n                                    </td>\n                                </tr>\n                            </table>\n                        </td>\n                    </tr>\n                    \n                    <!-- מאמר 5 -->\n                    <tr>\n                        <td style=\"padding-bottom: 16px;\">\n                            <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); overflow: hidden;\">\n                                <tr>\n                                    <td width=\"4\" style=\"background-color: #f7b955;\"></td>\n                                    <td style=\"padding: 18px 22px;\">\n                                        <h3 style=\"margin: 0 0 8px 0; font-size: 16px; font-weight: 600; line-height: 1.4; text-align: right;\">\n                                            <a href=\"{{ $('AI Agent').item.json.output.topArticles[4].url }}\" target=\"_blank\" style=\"color: #2b6cb0; text-decoration: none;\">{{ $('AI Agent').item.json.output.topArticles[4].title }}</a>\n                                        </h3>\n                                        <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin-top: 10px;\">\n                                            <tr>\n                                                <td style=\"font-size: 13px; color: #718096; text-align: right;\">{{ $('AI Agent').item.json.output.topArticles[4].source }} | {{ $('AI Agent').item.json.output.topArticles[4].date }}</td>\n                                                <td style=\"text-align: left;\">\n                                                    <div style=\"display: inline-block; padding: 3px 10px; border-radius: 30px; font-weight: 500; font-size: 12px; background-color: rgba(247, 185, 85, 0.1); color: #f7b955;\">\n                                                        {{ $('AI Agent').item.json.output.topArticles[4].sentimentHebrew }}\n                                                    </div>\n                                                </td>\n                                            </tr>\n                                        </table>\n                                    </td>\n                                </tr>\n                            </table>\n                        </td>\n                    </tr>\n                </table>\n            </div>\n              \n    <!-- נושאים חמים - גרסה משופרת למובייל -->\n    <div style=\"margin-bottom: 30px; text-align: right;\">\n        <h2 style=\"font-size: 20px; color: #1a202c; margin: 0 0 20px 0; padding-bottom: 12px; border-bottom: 1px solid #edf2f7; font-weight: 700; text-align: right;\">נושאים חמים</h2>\n        \n        <div style=\"background-color: #f8fafc; border-radius: 12px; padding: 20px 25px; text-align: right;\">\n            <p style=\"margin: 0 0 15px 0; font-size: 15px; color: #4a5568; text-align: right;\">הנושאים המרכזיים שמופיעים בחדשות על {{ $('AI Agent').item.json.output.stockSymbol }}:</p>\n            \n            <!-- נושא 1 -->\n            <div style=\"margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #edf2f7;\">\n                <div style=\"display: table; width: 100%; margin-bottom: 8px;\">\n                    <div style=\"display: table-cell; vertical-align: middle; text-align: right; font-weight: 600; font-size: 15px;\">\n                        {{ $('AI Agent').item.json.output.hotTopics[0].topic }}\n                    </div>\n                    <div style=\"display: table-cell; vertical-align: middle; text-align: left; white-space: nowrap;\">\n                        <div style=\"display: inline-block; background-color: #edf2f7; border-radius: 30px; padding: 4px 12px; font-size: 13px; color: #4a5568; text-align: center;\">\n                            <strong>{{ $('AI Agent').item.json.output.hotTopics[0].article_count }}</strong> מאמרים\n                        </div>\n                    </div>\n                </div>\n                <div style=\"background-color: #e2e8f0; height: 4px; width: 100%; border-radius: 2px; overflow: hidden;\">\n                    <div style=\"background-color: #4299e1; height: 100%; width: calc({{ $('AI Agent').item.json.output.hotTopics[0].average_relevance }} * 100%);\"></div>\n                </div>\n                <div style=\"text-align: left; font-size: 12px; color: #718096; margin-top: 4px;\">רלוונטיות: {{ $('AI Agent').item.json.output.hotTopics[0].average_relevance }}</div>\n            </div>\n            \n            <!-- נושא 2 -->\n            <div style=\"margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #edf2f7;\">\n                <div style=\"display: table; width: 100%; margin-bottom: 8px;\">\n                    <div style=\"display: table-cell; vertical-align: middle; text-align: right; font-weight: 600; font-size: 15px;\">\n                        {{ $('AI Agent').item.json.output.hotTopics[1].topic }}\n                    </div>\n                    <div style=\"display: table-cell; vertical-align: middle; text-align: left; white-space: nowrap;\">\n                        <div style=\"display: inline-block; background-color: #edf2f7; border-radius: 30px; padding: 4px 12px; font-size: 13px; color: #4a5568; text-align: center;\">\n                            <strong>{{ $('AI Agent').item.json.output.hotTopics[1].article_count }}</strong> מאמרים\n                        </div>\n                    </div>\n                </div>\n                <div style=\"background-color: #e2e8f0; height: 4px; width: 100%; border-radius: 2px; overflow: hidden;\">\n                    <div style=\"background-color: #4299e1; height: 100%; width: calc({{ $('AI Agent').item.json.output.hotTopics[1].average_relevance }} * 100%);\"></div>\n                </div>\n                <div style=\"text-align: left; font-size: 12px; color: #718096; margin-top: 4px;\">רלוונטיות: {{ $('AI Agent').item.json.output.hotTopics[1].average_relevance }}</div>\n            </div>\n            \n            <!-- נושא 3 -->\n            <div style=\"margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #edf2f7;\">\n                <div style=\"display: table; width: 100%; margin-bottom: 8px;\">\n                    <div style=\"display: table-cell; vertical-align: middle; text-align: right; font-weight: 600; font-size: 15px;\">\n                        {{ $('AI Agent').item.json.output.hotTopics[2].topic }}\n                    </div>\n                    <div style=\"display: table-cell; vertical-align: middle; text-align: left; white-space: nowrap;\">\n                        <div style=\"display: inline-block; background-color: #edf2f7; border-radius: 30px; padding: 4px 12px; font-size: 13px; color: #4a5568; text-align: center;\">\n                            <strong>{{ $('AI Agent').item.json.output.hotTopics[2].article_count }}</strong> מאמרים\n                        </div>\n                    </div>\n                </div>\n                <div style=\"background-color: #e2e8f0; height: 4px; width: 100%; border-radius: 2px; overflow: hidden;\">\n                    <div style=\"background-color: #4299e1; height: 100%; width: calc({{ $('AI Agent').item.json.output.hotTopics[2].average_relevance }} * 100%);\"></div>\n                </div>\n                <div style=\"text-align: left; font-size: 12px; color: #718096; margin-top: 4px;\">רלוונטיות: {{ $('AI Agent').item.json.output.hotTopics[2].average_relevance }}</div>\n            </div>\n            \n            <!-- נושא 4 -->\n            <div style=\"margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #edf2f7;\">\n                <div style=\"display: table; width: 100%; margin-bottom: 8px;\">\n                    <div style=\"display: table-cell; vertical-align: middle; text-align: right; font-weight: 600; font-size: 15px;\">\n                        {{ $('AI Agent').item.json.output.hotTopics[3].topic }}\n                    </div>\n                    <div style=\"display: table-cell; vertical-align: middle; text-align: left; white-space: nowrap;\">\n                        <div style=\"display: inline-block; background-color: #edf2f7; border-radius: 30px; padding: 4px 12px; font-size: 13px; color: #4a5568; text-align: center;\">\n                            <strong>{{ $('AI Agent').item.json.output.hotTopics[3].article_count }}</strong> מאמרים\n                        </div>\n                    </div>\n                </div>\n                <div style=\"background-color: #e2e8f0; height: 4px; width: 100%; border-radius: 2px; overflow: hidden;\">\n                    <div style=\"background-color: #4299e1; height: 100%; width: calc({{ $('AI Agent').item.json.output.hotTopics[3].average_relevance }} * 100%);\"></div>\n                </div>\n                <div style=\"text-align: left; font-size: 12px; color: #718096; margin-top: 4px;\">רלוונטיות: {{ $('AI Agent').item.json.output.hotTopics[3].average_relevance }}</div>\n            </div>\n            \n            <!-- נושא 5 -->\n            <div style=\"margin-bottom: 0;\">\n                <div style=\"display: table; width: 100%; margin-bottom: 8px;\">\n                    <div style=\"display: table-cell; vertical-align: middle; text-align: right; font-weight: 600; font-size: 15px;\">\n                        {{ $('AI Agent').item.json.output.hotTopics[4].topic }}\n                    </div>\n                    <div style=\"display: table-cell; vertical-align: middle; text-align: left; white-space: nowrap;\">\n                        <div style=\"display: inline-block; background-color: #edf2f7; border-radius: 30px; padding: 4px 12px; font-size: 13px; color: #4a5568; text-align: center;\">\n                            <strong>{{ $('AI Agent').item.json.output.hotTopics[4].article_count }}</strong> מאמרים\n                        </div>\n                    </div>\n                </div>\n                <div style=\"background-color: #e2e8f0; height: 4px; width: 100%; border-radius: 2px; overflow: hidden;\">\n                    <div style=\"background-color: #4299e1; height: 100%; width: calc({{ $('AI Agent').item.json.output.hotTopics[4].average_relevance }} * 100%);\"></div>\n                </div>\n                <div style=\"text-align: left; font-size: 12px; color: #718096; margin-top: 4px;\">רלוונטיות: {{ $('AI Agent').item.json.output.hotTopics[4].average_relevance }}</div>\n            </div>\n        </div>\n    </div>\n\t        \n        <!-- פוטר -->\n        <div style=\"background-color: #f8fafc; padding: 25px 40px; text-align: center; border-top: 1px solid #edf2f7;\">\n            <div style=\"font-size: 13px; color: #718096; line-height: 1.6;\">\n                <p style=\"margin: 0 0 8px 0;\">דוח זה נוצר באופן אוטומטי ואינו מהווה המלצת השקעה.</p>\n                <p style=\"margin: 0;\">יש להתייעץ עם יועץ השקעות מורשה לפני קבלת החלטות השקעה.</p>\n            </div>\n            <div style=\"margin-top: 20px;\">\n                נבנה ב-❤️ ע\"י <a href=\"https://www.linkedin.com/in/elay-g\" style=\"display: inline-block; text-decoration: none;\">עילי גז</a>\n            </div>\n        </div>\n        \n    </div>\n\n</body>\n</html>"},"typeVersion":1.2},{"id":"84a2fe62-e936-49ca-83d6-a02371e02166","name":"Send Stock Analysis","type":"n8n-nodes-base.emailSend","position":[2280,-100],"webhookId":"0de4d8cd-3519-4a4a-a05b-a9c973b64141","parameters":{"html":"={{ $json.html }}","options":{},"subject":"=הסקירה היומית של מניית {{ $('AI Agent').item.json.output.stockSymbol }}: {{ $('AI Agent').item.json.output.analysisDate }}","toEmail":"={{ $('On form submission').item.json[\"Email:\"] }}","fromEmail":"Elay's AI Assistant <elayguez@gmail.com>"},"credentials":{"smtp":{"id":"583PMpoYf46gbncd","name":"SMTP account"}},"executeOnce":false,"typeVersion":2.1},{"id":"36943e20-b0fc-40b0-b695-e0bdbd9182d1","name":"Adjust HTML Colors","type":"n8n-nodes-base.code","position":[2080,-100],"parameters":{"jsCode":"// New function to remove topics with only one article - ultra-simple approach\nfunction removeSingleArticleTopics(html) {\n  // First, see if there are any topics with exactly 1 article\n  if (!html.includes('<strong>1</strong> מאמרים')) {\n    console.log('No topics with 1 article found');\n    return html;\n  }\n\n  // Find each line that contains the \"נושא\" comment\n  // and check if it has exactly 1 article mentioned\n  const lines = html.split('\\n');\n  const linesToRemove = [];\n\n  // For each line containing \"1 מאמרים\", find the topic it belongs to\n  for (let i = 0; i < lines.length; i++) {\n    if (lines[i].includes('<strong>1</strong> מאמרים')) {\n      console.log(`Found line ${i} with 1 article mention`);\n      \n      // Go back to find the start of this topic\n      let startLine = -1;\n      for (let j = i; j >= 0; j--) {\n        if (lines[j].includes('<!-- נושא') || \n            lines[j].includes('<div style=\"margin-bottom: 15px; padding-bottom: 15px; border-bottom:')) {\n          startLine = j;\n          break;\n        }\n      }\n      \n      if (startLine === -1) {\n        console.log(`Couldn't find start of topic for line ${i}`);\n        continue;\n      }\n      \n      // Go forward to find the end of this topic\n      let endLine = -1;\n      let divCount = 0;\n      for (let j = startLine; j < lines.length; j++) {\n        // Count opening divs\n        const openMatches = lines[j].match(/<div/g);\n        if (openMatches) {\n          divCount += openMatches.length;\n        }\n        \n        // Count closing divs\n        const closeMatches = lines[j].match(/<\\/div>/g);\n        if (closeMatches) {\n          divCount -= closeMatches.length;\n        }\n        \n        // When divCount returns to 0, we've found the end\n        if (divCount === 0 && j > startLine) {\n          endLine = j;\n          break;\n        }\n      }\n      \n      if (endLine === -1) {\n        console.log(`Couldn't find end of topic for line ${i}`);\n        continue;\n      }\n      \n      // Now we have the start and end lines of the topic\n      console.log(`Found topic from line ${startLine} to ${endLine}`);\n      \n      // Mark these lines for removal\n      for (let j = startLine; j <= endLine; j++) {\n        linesToRemove.push(j);\n      }\n    }\n  }\n  \n  // Remove the marked lines\n  const newLines = [];\n  for (let i = 0; i < lines.length; i++) {\n    if (!linesToRemove.includes(i)) {\n      newLines.push(lines[i]);\n    }\n  }\n  \n  console.log(`Removed ${linesToRemove.length} lines in total`);\n  return newLines.join('\\n');\n}// Code for updating colors according to sentiment - for n8n\n\n// Define colors by sentiment type\nconst colors = {\n  positive: {\n    main: '#29cc7a',          // Green\n    background: 'rgba(41, 204, 122, 0.15)',\n    gradient: 'rgba(41, 204, 122, 0.07)',\n    accent: 'rgba(41, 204, 122, 0.1)'\n  },\n  neutral: {\n    main: '#f7b955',          // Orange\n    background: 'rgba(247, 185, 85, 0.15)',\n    gradient: 'rgba(247, 185, 85, 0.07)',\n    accent: 'rgba(247, 185, 85, 0.1)'\n  },\n  negative: {\n    main: '#f55e5e',          // Red\n    background: 'rgba(245, 94, 94, 0.15)',\n    gradient: 'rgba(245, 94, 94, 0.07)',\n    accent: 'rgba(245, 94, 94, 0.1)'\n  }\n};\n\n// Function to identify sentiment type from text\nfunction getSentimentType(text) {\n  if (!text) return 'neutral';\n  \n  const lowerText = text.toLowerCase();\n  \n  // Negative keywords - check first because there are expressions with both \"positive\" and \"negative\" together\n  if (lowerText.includes('שלילי') || lowerText.includes('negative') || \n      lowerText.includes('bearish') || lowerText.includes('ירידה') || \n      lowerText.includes('דובי') || lowerText.includes('מכירה') || \n      lowerText.includes('שלילי-חזק') || lowerText.includes('שלילי-חלש') ||\n      lowerText.includes('שלילית')) {\n    return 'negative';\n  }\n  \n  // Positive keywords\n  if (lowerText.includes('חיובי') || lowerText.includes('positive') || \n      lowerText.includes('bullish') || lowerText.includes('עלייה') || \n      lowerText.includes('שורי') || lowerText.includes('קנייה') || \n      lowerText.includes('חיובי-חזק') || lowerText.includes('חיובי-חלש') ||\n      lowerText.includes('חיובית')) {\n    return 'positive';\n  }\n  \n  // Additional check for expressions containing only \"strong\" or \"weak\"\n  if (lowerText.includes('חזק')) {\n    // If no negative word, assume it's positive\n    return 'positive';\n  }\n  \n  // Default - neutral\n  return 'neutral';\n}\n\n// Function to check if a specific text belongs to a sentiment - used for bug fixing\nfunction debugSentiment(text) {\n  console.log(`Sentiment check: \"${text}\" => ${getSentimentType(text)}`);\n}\n\n// New function to remove undefined articles from HTML\nfunction removeUndefinedArticles(html) {\n  // Find all article blocks\n  const articleBlocksRegex = /<tr>\\s*<td style=\"padding-bottom: 16px;\">\\s*<table[^>]*>[\\s\\S]*?<\\/table>\\s*<\\/td>\\s*<\\/tr>/g;\n  const articleBlocks = Array.from(html.matchAll(articleBlocksRegex));\n  \n  // No articles found\n  if (!articleBlocks || articleBlocks.length === 0) {\n    console.log(\"No article blocks found\");\n    return html;\n  }\n  \n  // Function to check if an article is fully undefined\n  function isFullyUndefinedArticle(articleHtml) {\n    // An article is considered fully undefined if:\n    // 1. It has href=\"undefined\"\n    // 2. It has link text that is \"undefined\"\n    // 3. It has \"undefined | undefined\" (source and date)\n    return articleHtml.includes('href=\"undefined\"') && \n           articleHtml.includes('>undefined</a>') &&\n           articleHtml.includes('undefined | undefined');\n  }\n  \n  // Identify blocks to remove\n  const blocksToRemove = [];\n  for (const match of articleBlocks) {\n    const block = match[0];\n    if (isFullyUndefinedArticle(block)) {\n      console.log(\"Found undefined article, will remove\");\n      blocksToRemove.push(match);\n    } else {\n      console.log(\"Found valid article, keeping it\");\n    }\n  }\n  \n  // If no blocks to remove, return original HTML\n  if (blocksToRemove.length === 0) {\n    console.log(\"No undefined articles found to remove\");\n    return html;\n  }\n  \n  console.log(`Found ${blocksToRemove.length} undefined articles to remove`);\n  \n  // Create a new string by removing the matches from end to start (to avoid index shifting)\n  let cleanedHtml = html;\n  for (let i = blocksToRemove.length - 1; i >= 0; i--) {\n    const match = blocksToRemove[i];\n    cleanedHtml = cleanedHtml.slice(0, match.index) + cleanedHtml.slice(match.index + match[0].length);\n  }\n  \n  return cleanedHtml;\n}\n\n// Get the HTML from the specified parameter\nconst html = $input.first().json.html;\nlet updatedHtml = html;\n\n// Bug checks - check several keywords\ndebugSentiment(\"חיובי\");\ndebugSentiment(\"שלילי\");\ndebugSentiment(\"נייטרלי\");\ndebugSentiment(\"חיובי-חזק\");\ndebugSentiment(\"שלילי-חזק\");\ndebugSentiment(\"חיובי-חלש\");\ndebugSentiment(\"שלילי-חלש\");\n\n// 1. Update colors in the recommendation title\nconst titleMatch = html.match(/<h2 style=\"[^\"]*color: #[a-f0-9]+;[^\"]*\">([^<]+)<\\/h2>/i);\nif (titleMatch) {\n  const titleText = titleMatch[1].trim();\n  const titleSentiment = getSentimentType(titleText);\n  \n  // Update title color\n  updatedHtml = updatedHtml.replace(\n    /(<h2 style=\"[^\"]*color: )#[a-f0-9]+(;[^\"]*\">)/i,\n    `$1${colors[titleSentiment].main}$2`\n  );\n  \n  // Update side bar color\n  updatedHtml = updatedHtml.replace(\n    /(<div style=\"position: absolute; right: 0; top: 0; bottom: 0; width: 6px; background-color: )#[a-f0-9]+(;\"><\\/div>)/i,\n    `$1${colors[titleSentiment].main}$2`\n  );\n  \n  // Update gradient color\n  updatedHtml = updatedHtml.replace(\n    /(<div style=\"position: absolute; right: 0; top: 0; width: 100%; height: 100%; background: linear-gradient\\(90deg, )rgba\\([^)]+\\)( 0%, )rgba\\([^)]+\\)( 50%\\);\"><\\/div>)/i,\n    `$1${colors[titleSentiment].gradient}$2${colors[titleSentiment].gradient.replace('0.07', '0')}$3`\n  );\n  \n  // Update icon background color\n  updatedHtml = updatedHtml.replace(\n    /(<div style=\"display: inline-block; width: 40px; height: 40px; border-radius: 50%; margin-bottom: 10px; background-color: )rgba\\([^)]+\\)(; text-align: center;\">)/i,\n    `$1${colors[titleSentiment].background}$2`\n  );\n}\n\n// 2. Update overall sentiment color\nconst sentimentMatch = updatedHtml.match(/<span style=\"[^\"]*font-weight: 600; color: #[a-f0-9]+;[^\"]*\">([^<]+)<\\/span>/i);\nif (sentimentMatch) {\n  const sentimentText = sentimentMatch[1].trim();\n  const sentimentType = getSentimentType(sentimentText);\n  \n  updatedHtml = updatedHtml.replace(\n    /(<span style=\"[^\"]*font-weight: 600; color: )#[a-f0-9]+(;[^\"]*\">)/i,\n    `$1${colors[sentimentType].main}$2`\n  );\n}\n\n// 3. Update article colors\nconst articleBlocks = updatedHtml.match(/<tr>\\s*<td style=\"padding-bottom: 16px;\">\\s*<table[^>]*>[\\s\\S]*?<\\/table>\\s*<\\/td>\\s*<\\/tr>/g);\nif (articleBlocks) {\n  for (const block of articleBlocks) {\n    // Check if this is a fully undefined article before skipping\n    const isUndefined = block.includes('href=\"undefined\"') && \n                        block.includes('>undefined</a>') && \n                        block.includes('undefined | undefined');\n    \n    // Skip if this is a completely undefined article\n    if (isUndefined) {\n      console.log(\"Skipping color update for undefined article\");\n      continue;\n    }\n    \n    // Find sentiment within the block\n    const articleSentimentMatch = block.match(/<div style=\"[^\"]*padding: 3px 10px;[^\"]*\">([^<]+)<\\/div>/i);\n    if (articleSentimentMatch) {\n      const articleSentimentText = articleSentimentMatch[1].trim();\n      const articleSentimentType = getSentimentType(articleSentimentText);\n      \n      // Debug check - log the identified sentiment\n      debugSentiment(articleSentimentText);\n      \n      // Create updated block\n      let updatedBlock = block;\n      \n      // Update side line color\n      updatedBlock = updatedBlock.replace(\n        /(<td width=\"4\" style=\"background-color: )#[a-f0-9]+(;\"><\\/td>)/i,\n        `$1${colors[articleSentimentType].main}$2`\n      );\n      \n      // Update sentiment tag colors (background and text color)\n      updatedBlock = updatedBlock.replace(\n        /(<div style=\"[^\"]*background-color: )rgba\\([^)]+\\)(; color: )#[a-f0-9]+(;[^\"]*\">)/i,\n        `$1${colors[articleSentimentType].accent}$2${colors[articleSentimentType].main}$3`\n      );\n      \n      // Replace the block with its updated version\n      updatedHtml = updatedHtml.replace(block, updatedBlock);\n    }\n  }\n}\n\n// 4. Update recommendation button color\nconst buttonMatch = updatedHtml.match(/<a style=\"[^\"]*background-color: #[a-f0-9]+;[^\"]*\">([^<]+)<\\/a>/i);\nif (buttonMatch) {\n  const buttonText = buttonMatch[1].trim();\n  let buttonSentiment = 'neutral'; // Default\n  \n  // Determine sentiment based on button text\n  if (buttonText.includes(\"ממליץ לקנות\")) {\n    buttonSentiment = 'positive';\n  } else if (buttonText.includes(\"ממליץ למכור\")) {\n    buttonSentiment = 'negative';\n  } else if (buttonText.includes(\"ממליץ לחכות\")) {\n    buttonSentiment = 'neutral';\n  }\n  \n  // Update button background color\n  updatedHtml = updatedHtml.replace(\n    /(<a style=\"[^\"]*background-color: )#[a-f0-9]+(;[^\"]*\">)/i,\n    `$1${colors[buttonSentiment].main}$2`\n  );\n  \n  // Update box-shadow color\n  const boxShadowRgba = `rgba(${parseInt(colors[buttonSentiment].main.substring(1, 3), 16)}, ${parseInt(colors[buttonSentiment].main.substring(3, 5), 16)}, ${parseInt(colors[buttonSentiment].main.substring(5, 7), 16)}, 0.25)`;\n  updatedHtml = updatedHtml.replace(\n    /(box-shadow: 0 4px 6px )rgba\\([^)]+\\)(;[^\"]*\">)/i,\n    `$1${boxShadowRgba}$2`\n  );\n}\n\n// 5. Remove undefined articles\nupdatedHtml = removeUndefinedArticles(updatedHtml);\n\n// 6. Remove topics with only one article\nupdatedHtml = removeSingleArticleTopics(updatedHtml);\n\n// Return updated HTML\nreturn { html: updatedHtml };"},"typeVersion":2},{"id":"d9174ea1-e42b-4533-98ab-9dc8f94055db","name":"Think","type":"@n8n/n8n-nodes-langchain.toolThink","position":[1680,140],"parameters":{},"typeVersion":1},{"id":"ca2820e9-553d-477b-9084-74b2fab92cc9","name":"Sticky Note8","type":"n8n-nodes-base.stickyNote","position":[940,260],"parameters":{"color":3,"width":160,"height":80,"content":"### Replace OpenAI Credentials"},"typeVersion":1},{"id":"2e0013ca-2dda-425c-b6d8-bdd3b3cd262d","name":"Generate Variables For API","type":"n8n-nodes-base.code","position":[2760,-20],"parameters":{"jsCode":"// Function to generate yesterday's date in the required format\nfunction getYesterdayDateFormat() {\n  // Create a current date object\n  const today = new Date();\n  \n  // Set the date to the previous day (yesterday)\n  today.setDate(today.getDate() - 1);\n  \n  // Reset hours, minutes, seconds and milliseconds to 00:00:00.000\n  today.setHours(0, 0, 0, 0);\n  \n  // Extract components\n  const year = today.getFullYear();\n  const month = String(today.getMonth() + 1).padStart(2, '0'); // Months in JS start from 0\n  const day = String(today.getDate()).padStart(2, '0');\n  const hours = String(today.getHours()).padStart(2, '0');\n  const minutes = String(today.getMinutes()).padStart(2, '0');\n  \n  // Build the string in the required format\n  return `${year}${month}${day}T${hours}${minutes}`;\n}\n// Calculate the date\nconst yesterdayDate = getYesterdayDateFormat();\n// Return the result in the format required by n8n - array of objects\nreturn [\n  {\n    json: {\n      wanted_date: yesterdayDate\n    }\n  }\n];"},"typeVersion":2},{"id":"a3d8d689-7b9a-4d45-9a9b-ffb9597606a1","name":"Set Variables","type":"n8n-nodes-base.set","position":[2920,-20],"parameters":{"values":{"number":[{"name":"wantedDate","value":"={{ $json.wanted_date }}"}],"string":[{"name":"stockSymbol","value":"={{ $('Workflow Input Trigger').item.json.ticker }}"},{"name":"apikey"}]},"options":{}},"typeVersion":2},{"id":"f4eeb758-ba3e-4fb2-882f-7422bdcdc30b","name":"Get News Data","type":"n8n-nodes-base.httpRequest","position":[3100,-20],"parameters":{"url":"=https://www.alphavantage.co/query?function=NEWS_SENTIMENT&tickers={{ $json.stockSymbol }}&sort=RELEVANCE&time_from={{ $json.wantedDate }}&apikey={{ $json.apikey }}","options":{}},"typeVersion":4.1},{"id":"87a2eb36-36e2-427d-8db2-2de3a280e404","name":"Analyse API Input","type":"n8n-nodes-base.code","position":[3280,-20],"parameters":{"jsCode":"/**\n * Custom code for n8n Code node to analyze news data from Alpha Vantage\n * \n * - The code receives API data from the previous node\n * - Analyzes sentiment, leading articles, and hot topics\n * - Returns a structured JSON object for further processing\n */\n\nconst stockSymbol = $('Set Variables').first().json.stockSymbol\nconst allNews = $input.first().json.feed\n  ;\nconst today = new Date().toISOString().split('T')[0]; // Current date in YYYY-MM-DD format\n\n// Filter articles relevant to the stock\nconst relevantArticles = allNews.filter(article => {\n  return article.ticker_sentiment && article.ticker_sentiment.some(ticker => \n    ticker.ticker === stockSymbol\n  );\n});\n\n// Sentiment analysis\nlet sentimentCounts = {\n  \"Bullish\": 0,\n  \"Somewhat-Bullish\": 0,\n  \"Neutral\": 0,\n  \"Somewhat-Bearish\": 0,\n  \"Bearish\": 0\n};\n\nlet totalSentimentScore = 0;\nlet totalRelevanceScore = 0;\n\nrelevantArticles.forEach(article => {\n  const stockTicker = article.ticker_sentiment.find(ticker => ticker.ticker === stockSymbol);\n  if (stockTicker) {\n    sentimentCounts[stockTicker.ticker_sentiment_label]++;\n    totalSentimentScore += parseFloat(stockTicker.ticker_sentiment_score) * parseFloat(stockTicker.relevance_score);\n    totalRelevanceScore += parseFloat(stockTicker.relevance_score);\n  }\n});\n\nconst avgSentimentScore = totalRelevanceScore > 0 ? totalSentimentScore / totalRelevanceScore : 0;\n\n// Determining overall sentiment\nlet overallSentiment;\nif (avgSentimentScore >= 0.35) {\n  overallSentiment = \"חיובי מאוד\";\n} else if (avgSentimentScore >= 0.15) {\n  overallSentiment = \"חיובי\";\n} else if (avgSentimentScore > -0.15) {\n  overallSentiment = \"נייטרלי\";\n} else if (avgSentimentScore > -0.35) {\n  overallSentiment = \"שלילי\";\n} else {\n  overallSentiment = \"שלילי מאוד\";\n}\n\n// Most influential articles\nconst topArticles = relevantArticles\n  .map(article => {\n    const stockTicker = article.ticker_sentiment.find(ticker => ticker.ticker === stockSymbol);\n    return {\n      title: article.title,\n      url: article.url,\n      source: article.source,\n      date: formatDate(article.time_published),\n      sentiment_label: stockTicker ? stockTicker.ticker_sentiment_label : \"N/A\",\n      sentiment_score: stockTicker ? parseFloat(stockTicker.ticker_sentiment_score) : 0,\n      relevance_score: stockTicker ? parseFloat(stockTicker.relevance_score) : 0,\n      impact_score: stockTicker ? Math.abs(parseFloat(stockTicker.ticker_sentiment_score) * parseFloat(stockTicker.relevance_score)) : 0\n    };\n  })\n  .sort((a, b) => b.impact_score - a.impact_score)\n  .slice(0, 5);\n\n// Analysis of main topics\nconst topicsMap = {};\n\nrelevantArticles.forEach(article => {\n  if (article.topics) {\n    article.topics.forEach(topic => {\n      if (!topicsMap[topic.topic]) {\n        topicsMap[topic.topic] = {\n          count: 0,\n          relevance: 0\n        };\n      }\n      topicsMap[topic.topic].count++;\n      topicsMap[topic.topic].relevance += parseFloat(topic.relevance_score);\n    });\n  }\n});\n\nconst hotTopics = Object.entries(topicsMap)\n  .map(([topic, data]) => ({\n    topic,\n    article_count: data.count,\n    average_relevance: (data.relevance / data.count).toFixed(2)\n  }))\n  .sort((a, b) => b.article_count - a.article_count)\n  .slice(0, 5);\n\n// Creating result object\nconst analysisResult = {\n  stock_symbol: stockSymbol,\n  analysis_date: today,\n  sentiment_analysis: {\n    overall_sentiment: overallSentiment,\n    sentiment_score: parseFloat(avgSentimentScore.toFixed(4)),\n    sentiment_distribution: sentimentCounts\n  },\n  top_articles: topArticles.map(article => ({\n    title: article.title,\n    source: article.source,\n    url: article.url,\n    date: article.date,\n    sentiment: article.sentiment_label,\n    impact_score: article.impact_score.toFixed(4)\n  })),\n  hot_topics: hotTopics,\n  recent_trends: {\n    description: getTrendDescription(overallSentiment, hotTopics),\n    market_outlook: getMarketOutlook(overallSentiment)\n  }\n};\n\n// Helper functions\nfunction formatDate(dateStr) {\n  if (!dateStr) return \"N/A\";\n  \n  try {\n    // Format: 20250418T152049 -> 2025-04-18\n    const year = dateStr.substring(0, 4);\n    const month = dateStr.substring(4, 6);\n    const day = dateStr.substring(6, 8);\n    return `${year}-${month}-${day}`;\n  } catch (e) {\n    return dateStr;\n  }\n}\n\nfunction getTrendDescription(sentiment, topics) {\n  let description = \"\";\n  \n  if (sentiment === \"חיובי מאוד\" || sentiment === \"חיובי\") {\n    description = \"מגמה חיובית כאשר משקיעים מתמקדים בעיקר ב\";\n  } else if (sentiment === \"שלילי מאוד\" || sentiment === \"שלילי\") {\n    description = \"מגמה שלילית כאשר החששות העיקריים מתמקדים ב\";\n  } else {\n    description = \"מגמה מעורבת עם התמקדות ב\";\n  }\n  \n  if (topics.length > 0) {\n    const topThreeTopics = topics.slice(0, Math.min(3, topics.length));\n    description += topThreeTopics.map(t => t.topic).join(\", \");\n  } else {\n    description += \"מגוון נושאים\";\n  }\n  \n  return description + \".\";\n}\n\nfunction getMarketOutlook(sentiment) {\n  if (sentiment === \"חיובי מאוד\") {\n    return \"תחזית שוק חיובית מאוד. הסנטימנט הכללי מצביע על אמון משקיעים גבוה ופוטנציאל לעלייה בטווח הקצר.\";\n  } else if (sentiment === \"חיובי\") {\n    return \"תחזית שוק חיובית. ישנן אינדיקציות לאופטימיות זהירה בקרב משקיעים.\";\n  } else if (sentiment === \"נייטרלי\") {\n    return \"תחזית שוק מעורבת. קיימים כוחות מאזנים של אופטימיות ופסימיות בשוק.\";\n  } else if (sentiment === \"שלילי\") {\n    return \"תחזית שוק שלילית. ישנן דאגות בקרב משקיעים שעשויות להשפיע על המניה בטווח הקצר.\";\n  } else {\n    return \"תחזית שוק שלילית מאוד. קיימת אווירת זהירות משמעותית ונטייה למכירות.\";\n  }\n}\n\n// Return the object for further flow in n8n\nreturn {\n  json: analysisResult\n};"},"typeVersion":2},{"id":"8a108780-d2e3-4cc7-bf7f-49da726f37fd","name":"Workflow Input Trigger","type":"n8n-nodes-base.executeWorkflowTrigger","position":[740,800],"parameters":{"workflowInputs":{"values":[{"name":"ticker"},{"name":"chart_style"}]}},"typeVersion":1.1},{"id":"8491ca61-2367-401e-9ff2-4f1d90f3ce59","name":"Download Chart","type":"n8n-nodes-base.httpRequest","position":[1540,520],"parameters":{"url":"={{ $json.url }}","options":{}},"typeVersion":4.2},{"id":"1250dca0-d4d6-4890-9aa4-110cd0f0fbb6","name":"Get Chart URL","type":"n8n-nodes-base.httpRequest","position":[1320,520],"parameters":{"url":"https://api.chart-img.com/v2/tradingview/advanced-chart/storage","method":"POST","options":{"response":{"response":{"responseFormat":"json"}}},"jsonBody":"={\n  \"style\": \"candle\",\n  \"theme\": \"light\",\n  \"interval\": \"1W\",\n  \"symbol\": \"NASDAQ:{{ $json.ticker }}\",\n  \"override\": {\n    \"showStudyLastValue\": false\n  },\n  \"studies\": [\n    {\n      \"name\": \"Volume\",\n      \"forceOverlay\": true\n    },\n    {\n      \"name\": \"Moving Average Exponential\",\n      \"inputs\": {\n        \"length\": 200\n      }\n    },\n    {\n      \"name\": \"Relative Strength Index\"\n    }\n  ]\n}","sendBody":true,"sendHeaders":true,"specifyBody":"json","authentication":"genericCredentialType","genericAuthType":"httpHeaderAuth","headerParameters":{"parameters":[{"name":"Content-Type","value":"application/json"}]}},"credentials":{"httpHeaderAuth":{"id":"cnQIlBI286n0AZiU","name":"Header Auth account"}},"typeVersion":4.2},{"id":"cfc9c470-c284-4d58-b6be-260f36d3d2b7","name":"Get Price History","type":"n8n-nodes-base.httpRequest","position":[1320,800],"parameters":{"url":"=https://api.twelvedata.com/time_series","options":{"response":{"response":{"responseFormat":"json"}}},"sendQuery":true,"queryParameters":{"parameters":[{"name":"symbol","value":"={{ $json.ticker }}"},{"name":"interval","value":"1day"},{"name":"outputsize","value":"180"},{"name":"apikey","value":"={{ $json.TwelveData_API_Key }}"}]}},"typeVersion":4.1},{"id":"3974f7f8-99b3-43b2-83f3-05819cdde7b2","name":"Get Bollinger Bands","type":"n8n-nodes-base.httpRequest","position":[1320,960],"parameters":{"url":"=https://api.twelvedata.com/bbands","options":{"response":{"response":{"responseFormat":"json"}}},"sendQuery":true,"queryParameters":{"parameters":[{"name":"symbol","value":"={{ $json.ticker }}"},{"name":"interval","value":"1day"},{"name":"outputsize","value":"1"},{"name":"apikey","value":"={{ $json.TwelveData_API_Key }}"}]}},"typeVersion":4.1},{"id":"0d333729-3fe5-4253-9989-16adaf1166b8","name":"Get MACD","type":"n8n-nodes-base.httpRequest","position":[1320,1120],"parameters":{"url":"=https://api.twelvedata.com/macd","options":{"response":{"response":{"responseFormat":"json"}}},"sendQuery":true,"queryParameters":{"parameters":[{"name":"symbol","value":"={{ $json.ticker }}"},{"name":"interval","value":"1day"},{"name":"outputsize","value":"1"},{"name":"apikey","value":"={{ $json.TwelveData_API_Key }}"}]}},"typeVersion":4.1},{"id":"4bd10a52-082b-4842-91ee-ef748c6ba695","name":"Merge","type":"n8n-nodes-base.merge","position":[1820,960],"parameters":{"numberInputs":3},"typeVersion":3.1},{"id":"8061fbcf-d138-44b5-b6d7-14c7a4e5904e","name":"Calculate Support Resistance","type":"n8n-nodes-base.code","position":[1540,800],"parameters":{"jsCode":"// Get historical price data\nconst data = $input.item.json;\n\n// Check if data exists\nif (!data.values || data.values.length === 0) {\n  return { json: { error: \"No price data available\", ticker: data.meta?.symbol } };\n}\n\n// Convert prices to numbers\nconst prices = data.values.map(v => parseFloat(v.close)).reverse();\n\n// Function to calculate Fibonacci levels\nfunction calculateFibonacciLevels() {\n  // Find min and max prices\n  const max = Math.max(...prices);\n  const min = Math.min(...prices);\n  const diff = max - min;\n  \n  return {\n    level_0: min.toFixed(2),\n    level_0_236: (min + diff * 0.236).toFixed(2),\n    level_0_382: (min + diff * 0.382).toFixed(2),\n    level_0_5: (min + diff * 0.5).toFixed(2),\n    level_0_618: (min + diff * 0.618).toFixed(2),\n    level_0_786: (min + diff * 0.786).toFixed(2),\n    level_1: max.toFixed(2)\n  };\n}\n// Function to identify support and resistance levels\nfunction findSupportResistanceLevels() {\n  // We need at least 30 data points\n  if (prices.length < 30) {\n    return { support: [], resistance: [] };\n  }\n  \n  const supportLevels = [];\n  const resistanceLevels = [];\n  \n  // Check each point (except edges) if it's a local minimum or maximum\n  const lookback = 5; // how many points to check in each direction\n  \n  for (let i = lookback; i < prices.length - lookback; i++) {\n    // Check for local minimum (support)\n    let isMinimum = true;\n    for (let j = i - lookback; j < i; j++) {\n      if (prices[j] <= prices[i]) {\n        isMinimum = false;\n        break;\n      }\n    }\n    \n    for (let j = i + 1; j <= i + lookback; j++) {\n      if (prices[j] <= prices[i]) {\n        isMinimum = false;\n        break;\n      }\n    }\n    \n    if (isMinimum) {\n      supportLevels.push(prices[i]);\n    }\n    \n    // Check for local maximum (resistance)\n    let isMaximum = true;\n    for (let j = i - lookback; j < i; j++) {\n      if (prices[j] >= prices[i]) {\n        isMaximum = false;\n        break;\n      }\n    }\n    \n    for (let j = i + 1; j <= i + lookback; j++) {\n      if (prices[j] >= prices[i]) {\n        isMaximum = false;\n        break;\n      }\n    }\n    \n    if (isMaximum) {\n      resistanceLevels.push(prices[i]);\n    }\n  }\n  \n  // Sort and remove duplicates\n  const uniqueSupports = [...new Set(supportLevels)];\n  const uniqueResistances = [...new Set(resistanceLevels)];\n  \n  // Return only significant levels (up to 5 of each)\n  return {\n    support: uniqueSupports.sort((a, b) => b - a).slice(0, 5).map(p => p.toFixed(2)),\n    resistance: uniqueResistances.sort((a, b) => a - b).slice(0, 5).map(p => p.toFixed(2))\n  };\n}\n\n// Calculate levels\nconst fibonacciLevels = calculateFibonacciLevels();\nconst supportResistanceLevels = findSupportResistanceLevels();\n\n// Return information with additional stock data\nreturn {\n  json: {\n    ticker: data.meta.symbol,\n    currentPrice: parseFloat(data.values[0].close).toFixed(2),\n    fibonacci: fibonacciLevels,\n    supportResistance: supportResistanceLevels,\n    dataPoints: prices.length\n  }\n};"},"typeVersion":2},{"id":"c692581d-a41d-48ef-9a7a-0a20fbceea81","name":"Organizing Data","type":"n8n-nodes-base.code","position":[2040,960],"parameters":{"jsCode":"// Getting data from different sources\n// Checking existence of objects before trying to access them\nconst items = $input.all();\nconst fibData = $input.first().json;\n\n// Trying to locate bband and MACD data if they exist\nlet bbandsData = null;\nlet macdData = null;\n\n// Trying to check if there is data in additional items\nif (items.length > 1 && items[1] && items[1].json) {\n  bbandsData = items[1].json;\n}\n\nif (items.length > 2 && items[2] && items[2].json) {\n  macdData = items[2].json;\n}\n\n// Creating data structure for the response - ensure all fields exist\nconst result = {\n  ticker: fibData.ticker || \"לא ידוע\",\n  currentPrice: fibData.currentPrice || \"0\",\n  timestamp: new Date().toISOString(),\n  technicalAnalysis: {\n    fibonacci: fibData.fibonacci || {},\n    supportResistance: fibData.supportResistance || { support: [], resistance: [] },\n    bollingerBands: {},\n    macd: {}\n  }\n};\n\n// Adding Bollinger Bands data - only if they exist\nif (bbandsData && bbandsData.values && bbandsData.values.length > 0) {\n  const bbands = bbandsData.values[0];\n  result.technicalAnalysis.bollingerBands = {\n    upperBand: parseFloat(bbands.upper_band).toFixed(2),\n    middleBand: parseFloat(bbands.middle_band).toFixed(2),\n    lowerBand: parseFloat(bbands.lower_band).toFixed(2)\n  };\n} else if (bbandsData && bbandsData.status === \"ok\") {\n  // If returning another data format\n  result.technicalAnalysis.bollingerBands = {\n    upperBand: bbandsData.upperBand || bbandsData.upper_band || \"0\",\n    middleBand: bbandsData.middleBand || bbandsData.middle_band || \"0\",\n    lowerBand: bbandsData.lowerBand || bbandsData.lower_band || \"0\"\n  };\n}\n\n// Adding MACD data - only if they exist\nif (macdData && macdData.values && macdData.values.length > 0) {\n  const macd = macdData.values[0];\n  result.technicalAnalysis.macd = {\n    macd: parseFloat(macd.macd).toFixed(2),\n    signal: parseFloat(macd.signal).toFixed(2),\n    histogram: parseFloat(macd.hist).toFixed(2)\n  };\n} else if (macdData && macdData.status === \"ok\") {\n  // If returning another data format\n  result.technicalAnalysis.macd = {\n    macd: macdData.macd || \"0\",\n    signal: macdData.signal || \"0\",\n    histogram: macdData.histogram || macdData.hist || \"0\"\n  };\n}\n\n// Creating summary and recommendation\nlet bullishFactors = [];\nlet bearishFactors = [];\n\n// Analyzing Bollinger Bands - only if data exists\nconst bbands = result.technicalAnalysis.bollingerBands;\nif (bbands.upperBand && bbands.lowerBand) {\n  const currentPrice = parseFloat(result.currentPrice);\n  const upperBand = parseFloat(bbands.upperBand);\n  const lowerBand = parseFloat(bbands.lowerBand);\n  \n  if (!isNaN(currentPrice) && !isNaN(upperBand) && !isNaN(lowerBand)) {\n    if (currentPrice > upperBand) {\n      bearishFactors.push(\"מחיר מעל רצועת בולינגר העליונה - אפשרות לקנייה יתר\");\n    } else if (currentPrice < lowerBand) {\n      bullishFactors.push(\"מחיר מתחת לרצועת בולינגר התחתונה - אפשרות למכירה יתר\");\n    }\n  }\n}\n\n// Analyzing MACD - only if data exists\nconst macdInfo = result.technicalAnalysis.macd;\nif (macdInfo.macd && macdInfo.signal) {\n  const macd = parseFloat(macdInfo.macd);\n  const signal = parseFloat(macdInfo.signal);\n  \n  if (!isNaN(macd) && !isNaN(signal)) {\n    if (macd > signal) {\n      bullishFactors.push(\"MACD מעל קו האיתות - אינדיקציה חיובית\");\n    } else {\n      bearishFactors.push(\"MACD מתחת לקו האיתות - אינדיקציה שלילית\");\n    }\n  }\n}\n\n// Analyzing support and resistance levels - only if data exists\nconst supportResistance = result.technicalAnalysis.supportResistance;\nif (supportResistance.support && supportResistance.resistance) {\n  const currentPrice = parseFloat(result.currentPrice);\n  \n  if (!isNaN(currentPrice)) {\n    const supports = supportResistance.support.map(s => parseFloat(s)).filter(s => !isNaN(s));\n    const resistances = supportResistance.resistance.map(r => parseFloat(r)).filter(r => !isNaN(r));\n    \n    // Finding the closest support level\n    let closestSupport = null;\n    let minSupportDist = Infinity;\n    for (const support of supports) {\n      if (support < currentPrice) {\n        const dist = currentPrice - support;\n        if (dist < minSupportDist) {\n          minSupportDist = dist;\n          closestSupport = support;\n        }\n      }\n    }\n    \n    // Finding the closest resistance level\n    let closestResistance = null;\n    let minResistanceDist = Infinity;\n    for (const resistance of resistances) {\n      if (resistance > currentPrice) {\n        const dist = resistance - currentPrice;\n        if (dist < minResistanceDist) {\n          minResistanceDist = dist;\n          closestResistance = resistance;\n        }\n      }\n    }\n    \n    // Adding support/resistance analysis\n    if (closestSupport !== null) {\n      const supportPercentage = ((currentPrice - closestSupport) / currentPrice * 100).toFixed(2);\n      if (supportPercentage < 5) {\n        bullishFactors.push(`המחיר קרוב לרמת תמיכה (${supportPercentage}%) - אפשרות להיפוך כלפי מעלה`);\n      }\n    }\n    \n    if (closestResistance !== null) {\n      const resistancePercentage = ((closestResistance - currentPrice) / currentPrice * 100).toFixed(2);\n      if (resistancePercentage < 5) {\n        bearishFactors.push(`המחיר קרוב לרמת התנגדות (${resistancePercentage}%) - אפשרות להיפוך כלפי מטה`);\n      }\n    }\n  }\n}\n\n// Analyzing Fibonacci - only if data exists\nconst fibonacci = result.technicalAnalysis.fibonacci;\nif (fibonacci && Object.keys(fibonacci).length > 0) {\n  const currentPrice = parseFloat(result.currentPrice);\n  \n  if (!isNaN(currentPrice)) {\n    const fibLevels = Object.values(fibonacci).map(level => parseFloat(level)).filter(level => !isNaN(level));\n    fibLevels.sort((a, b) => a - b);\n    \n    // Checking which Fibonacci level the price is at\n    for (let i = 0; i < fibLevels.length - 1; i++) {\n      if (currentPrice >= fibLevels[i] && currentPrice <= fibLevels[i+1]) {\n        // If the price is close to a Fibonacci resistance level\n        if (Math.abs(currentPrice - fibLevels[i+1]) / currentPrice * 100 < 2) {\n          bearishFactors.push(`המחיר קרוב לרמת פיבונאצ'י ${[0, 23.6, 38.2, 50, 61.8, 78.6, 100][Math.min(i+1, 6)]}% - אפשרות להתנגדות`);\n        }\n        // If the price is close to a Fibonacci support level\n        if (Math.abs(currentPrice - fibLevels[i]) / currentPrice * 100 < 2) {\n          bullishFactors.push(`המחיר קרוב לרמת פיבונאצ'י ${[0, 23.6, 38.2, 50, 61.8, 78.6, 100][Math.min(i, 6)]}% - אפשרות לתמיכה`);\n        }\n        break;\n      }\n    }\n  }\n}\n\n// Adding general recommendation based on factors\nlet recommendation = \"\";\nif (bullishFactors.length > bearishFactors.length) {\n  recommendation = \"חיובית\";\n} else if (bearishFactors.length > bullishFactors.length) {\n  recommendation = \"שלילית\";\n} else {\n  recommendation = \"נייטרלית\";\n}\n\n// Adding summary to the result\nresult.summary = {\n  recommendation: recommendation,\n  bullishFactors: bullishFactors,\n  bearishFactors: bearishFactors\n};\n\nreturn { json: result };"},"typeVersion":2},{"id":"d00b380b-43a4-478e-8265-d79299278867","name":"Merge-2","type":"n8n-nodes-base.merge","position":[2440,800],"parameters":{"mode":"combine","options":{},"combineBy":"combineByPosition"},"typeVersion":3.1},{"id":"4ca4c3aa-9c91-4a6c-973f-bb0ed3754a82","name":"ChatGPT 4o","type":"@n8n/n8n-nodes-langchain.openAi","position":[2900,800],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-4o","cachedResultName":"GPT-4O"},"options":{},"messages":{"values":[{"role":"system","content":"# Role\nYou are a senior technical analyst who merges visual insights with quantitative indicators.\n\n# Inputs\n1. Visual JSON from Agent 1:\n   {\n     \"ai_agent_visual_analysis\": \"...\"\n   }\n2. Technical-indicator JSON in the format:\n   {\n     \"ticker\": \"...\",\n     \"currentPrice\": \"...\",\n     \"timestamp\": \"...\",\n     \"technicalAnalysis\": {\n       \"fibonacci\": { ... },\n       \"supportResistance\": { ... },\n       \"bollingerBands\": { ... },\n       \"macd\": { ... }\n     },\n     \"summary\": { ... }\n   }\n\n# Expected Sections\nWrite five titled sections exactly in this order:\n\n1. Quick Stats  \n   - Ticker, current price, timestamp  \n   - Overall recommendation from technical JSON, if present\n\n2. Candles and EMA  \n   - Use Agent 1 data: trendDirection, candlestickPatterns, emaRelation, volumeNotes\n\n3. RSI  \n   - Report rsiNumeric and rsiState from Agent 1  \n   - Mention rsiDivergence and its implication\n\n4. Indicator Synthesis  \n   - Fibonacci – cite closest level above and below price  \n   - Bollinger Bands – quote upper, middle, lower and note price position  \n   - MACD – quote macd, signal, histogram, note cross or momentum if numbers are valid  \n   - Support-Resistance – use technicalAnalysis plus priceZones from Agent 1 to highlight the nearest levels\n\n5. Actionable Takeaway  \n   - One sentence bias (bullish, bearish, neutral)  \n   - Clear next step such as watch for break above X or pullback to Y\n\n# Style Rules\n- Be concise and strictly data driven  \n- Every statement must reference either a value from the inputs or a specific visual observation from Agent 1  \n- No speculation beyond supplied data  \n- End after the Takeaway section – output nothing else"},{"content":"={{ $json.textPayload }}"}]}},"credentials":{"openAiApi":{"id":"2m1HH5crgPAhTJlv","name":"OpenAi account"}},"typeVersion":1.8},{"id":"d46f175b-0737-4e33-b8fc-4937f0c6456e","name":"Set Variable","type":"n8n-nodes-base.set","position":[2040,520],"parameters":{"options":{},"assignments":{"assignments":[{"id":"fdf7e016-7082-4146-9038-454139023990","name":"ai_agent_visual_analysis","type":"string","value":"={{ $('First Technical Analysis').item.json.choices[0].message.content }}"}]}},"typeVersion":3.4},{"id":"186b6551-ac08-43f3-b000-9be984a4eb13","name":"Warp as JSON for GPT","type":"n8n-nodes-base.code","position":[2660,800],"parameters":{"jsCode":"/**\n * INPUT: items[0].json (Original JSON)\n * OUTPUT: { textPayload: \"```json\\n{ ... }\\n```\" }\n */\n\nconst pretty = JSON.stringify(items[0].json, null, 2);\nconst wrapped = `\\`\\`\\`json\\n${pretty}\\n\\`\\`\\``;\n\nreturn [\n  {\n    json: {\n      textPayload: wrapped\n    }\n  }\n];\n"},"typeVersion":2},{"id":"b42da048-4e0a-4e45-b6a4-9272473844cb","name":"Set Final Response","type":"n8n-nodes-base.set","position":[3280,800],"parameters":{"options":{},"assignments":{"assignments":[{"id":"fdf7e016-7082-4146-9038-454139023990","name":"response","type":"string","value":"={{ $json.message.content }}"},{"id":"4e5afd49-67c2-40ab-bc8c-565dea3850ed","name":"image","type":"string","value":"={{ $('Download Chart').item.json.url }}"}]}},"typeVersion":3.4},{"id":"c718d0a7-2689-42da-985f-17030583e51c","name":"Set Stock Symbol and API Key","type":"n8n-nodes-base.set","position":[960,800],"parameters":{"options":{},"assignments":{"assignments":[{"id":"cf5f7210-5b54-4f4a-abf7-87873be82df4","name":"ticker","type":"string","value":"={{ $json.ticker }}"},{"id":"9f008c4b-60e2-4d99-a119-b0170ec28358","name":"TwelveData_API_Key","type":"string","value":""}]}},"typeVersion":3.4},{"id":"884a7e3b-3e8f-4c69-8950-5b0f1dd25f29","name":"First Technical Analysis","type":"@n8n/n8n-nodes-langchain.openAi","position":[1820,520],"parameters":{"text":"=# Role\nYou are a visual chart analyst.  \nYour only input is a weekly candlestick chart image that shows:\n- Price candles and volume bars  \n- One short-term EMA line  \n- An RSI panel with its live value  \n\n# Task\nInspect the image and produce a **structured JSON** object with the following keys:\n\n{\n  \"rsiNumeric\": number,          // exact RSI value from the chart\n  \"rsiState\": \"overbought\" | \"oversold\" | \"neutral\",\n  \"rsiDivergence\": \"bullish\" | \"bearish\" | \"none\",\n  \"trendDirection\": \"up\" | \"down\" | \"sideways\",\n  \"candlestickPatterns\": [ \"pattern1\", \"pattern2\", ... ],   // max 3\n  \"emaRelation\": \"aboveEMA\" | \"belowEMA\" | \"testingEMA\",\n  \"volumeNotes\": \"string\",        // brief comment on recent volume behavior\n  \"priceZones\": {                 // visually inferred areas\n      \"potentialSupport\": [number, ...],  // up to 2 levels taken from visible lows\n      \"potentialResistance\": [number, ...]// up to 2 levels taken from visible highs\n  }\n}\n\n# Style Rules\n- Derive every value only from what is visible in the chart\n- Do not mention any external data or speculation\n- Return the JSON object only, nothing else\n","modelId":{"__rl":true,"mode":"list","value":"gpt-4o","cachedResultName":"GPT-4O"},"options":{"detail":"auto"},"resource":"image","simplify":false,"inputType":"base64","operation":"analyze"},"credentials":{"openAiApi":{"id":"2m1HH5crgPAhTJlv","name":"OpenAi account"}},"typeVersion":1.8},{"id":"ba7aca23-5d4c-4bfd-8d7f-af45aaa8d8a0","name":"Schedule Trigger1","type":"n8n-nodes-base.scheduleTrigger","position":[2560,-20],"parameters":{"rule":{"interval":[{}]}},"typeVersion":1.2},{"id":"4b93b4ba-3369-4051-a92d-d3ff811fb566","name":"Technical Analysis Tool","type":"@n8n/n8n-nodes-langchain.toolWorkflow","position":[1360,140],"parameters":{"name":"technical_analysis","workflowId":{"__rl":true,"mode":"list","value":"GDXsoM9kWq3cz53Y","cachedResultName":"technical_analysis"},"description":"Call this tool to get an analysis of a requested stock. It'll be obligatory to pass ticker.","workflowInputs":{"value":{"ticker":"={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('ticker', ``, 'string') }}"},"schema":[{"id":"ticker","type":"string","display":true,"required":false,"displayName":"ticker","defaultMatch":false,"canBeUsedToMatch":true},{"id":"chart_style","type":"string","display":true,"removed":true,"required":false,"displayName":"chart_style","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false}},"typeVersion":2},{"id":"c5d70195-f90b-42b4-b056-52794c75c20f","name":"Trends Analysis Tool","type":"@n8n/n8n-nodes-langchain.toolWorkflow","position":[1520,140],"parameters":{"name":"trends_analysis","workflowId":{"__rl":true,"mode":"list","value":"jnlklBcNkky9yFoc","cachedResultName":"trends_analysis"},"description":"Call this tool to get an analysis of a requested stock. It'll be obligatory to pass ticker.","workflowInputs":{"value":{"ticker":"={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('ticker', ``, 'string') }}"},"schema":[{"id":"ticker","type":"string","display":true,"required":false,"displayName":"ticker","defaultMatch":false,"canBeUsedToMatch":true},{"id":"chart_style","type":"string","display":true,"removed":true,"required":false,"displayName":"chart_style","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false}},"typeVersion":2},{"id":"de8355ea-8f6b-4733-b5ac-06e080e42e30","name":"Sticky Note16","type":"n8n-nodes-base.stickyNote","position":[2900,120],"parameters":{"color":3,"width":160,"height":80,"content":"### Replace Alphavantage API Key"},"typeVersion":1},{"id":"9c25c2ab-796f-4156-8267-efe6d0182e9d","name":"Sticky Note14","type":"n8n-nodes-base.stickyNote","position":[1800,680],"parameters":{"color":3,"width":160,"height":80,"content":"### Replace OpenAI Credentials"},"typeVersion":1},{"id":"e78394cb-809e-4731-b74b-e8378f8e2bc9","name":"Sticky Note15","type":"n8n-nodes-base.stickyNote","position":[2900,920],"parameters":{"color":3,"width":160,"height":80,"content":"### Replace OpenAI Credentials"},"typeVersion":1},{"id":"dcf46ba4-b309-4d41-8041-717d564c0690","name":"Sticky Note17","type":"n8n-nodes-base.stickyNote","position":[1300,680],"parameters":{"color":3,"width":160,"height":80,"content":"### Replace Chart-img API Key"},"typeVersion":1},{"id":"727d5d8b-c537-41d2-a757-479813ecb733","name":"Sticky Note19","type":"n8n-nodes-base.stickyNote","position":[920,980],"parameters":{"color":3,"width":160,"height":80,"content":"### Replace TwelveData API Key"},"typeVersion":1},{"id":"580f36fb-2d85-4a74-825b-540f328b860f","name":"Sticky Note13","type":"n8n-nodes-base.stickyNote","position":[2280,60],"parameters":{"color":3,"width":160,"height":80,"content":"### Replace SMTP Credentials"},"typeVersion":1},{"id":"e40b611a-12c3-4975-b692-f338d0477c8b","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[220,-220],"parameters":{"color":7,"width":440,"height":300,"content":"# Advance Stock Analysis (both Technical and Trends) Using GPT4o Powered AI Agent\n\n## Built by  [Elay Guez](https://www.linkedin.com/in/elay-g)"},"typeVersion":1},{"id":"ab83de34-3083-4882-b329-c368d5818917","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[220,100],"parameters":{"color":7,"width":440,"height":1200,"content":"### Overview ###\n\nGet comprehensive stock analysis with this AI-powered workflow that provides actionable insights for your investment decisions. On a weekly basis, this workflow:\n\n- Analyzes stock data from multiple sources (Chart-img, Twelve Data API, Alphavantage)\n- Performs technical analysis using advanced indicators (RSI, MACD, Bollinger Bands, Resistance and Support Levels)\n- Scans financial news from Alpha Vantage to capture market sentiment\n- Uses OpenAI's GPT-4o to identify patterns, trends, and trading opportunities\n- Generates a fully styled, responsive HTML email (with proper RTL layout) in Hebrew\n- Sends detailed recommendations directly to your inbox\n\n**Perfect for investors, traders, and financial analysts who want data-driven stock insights - combining technical indicators with news sentiment for more informed decisions.**\n\n### Setup Instructions ###\n\n**Estimated setup time:**\n- 15 minutes\n\n**Required credentials:**\n- OpenAI API Key\n- Chart-img API Key (free tier)\n- Twelve Data API Key (free tier)\n- Alpha Vantage API Key (free tier)\n- SMTP credentials (for email delivery)\n\n**Steps:**\n\n1. Import this template into your n8n instance.\n2. Add your API keys under credentials.\n3. Configure the SMTP Email node with: Host (e.g., smtp.gmail.com), Port (465 or 587), Username (your email), Password (app-specific password or login).\n4. Activate the workflow.\n5. Fill in the Form.\n6. **Enjoy!** (Check your Spam mailbox)\n\n### Important Note: ###\nThis report is being generated automatically and does not constitute an investment recommendation. **Please consult a licensed investment advisor before making any investment decisions.**"},"typeVersion":1},{"id":"28e1e96f-e27c-4933-afee-35931241060c","name":"On form submission","type":"n8n-nodes-base.formTrigger","position":[720,-100],"webhookId":"79520027-d875-4ca3-a533-614bfca5e5b1","parameters":{"options":{"appendAttribution":true,"respondWithOptions":{"values":{"formSubmittedText":"Success! Check your inbox (or spam folder) for your analysis report."}}},"formTitle":"Advance Stock Analysis","formFields":{"values":[{"fieldLabel":"Ticker symbol:","placeholder":"TSLA","requiredField":true},{"fieldType":"email","fieldLabel":"Email:","placeholder":"youremail@gmail.com","requiredField":true}]},"responseMode":"lastNode","formDescription":"Please enter the company’s NASDAQ ticker symbol (e.g. AAPL) to get a weekly email with combined technical-and-news sentiment analysis from our AI agent"},"typeVersion":2.2},{"id":"4d9939ec-4a8e-40cb-95d2-24bbdbc27c8e","name":"Refine Text","type":"@n8n/n8n-nodes-langchain.openAi","position":[1520,-100],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-4o","cachedResultName":"GPT-4O"},"options":{},"messages":{"values":[{"content":"=\"technicalAnalysis\": {{ $json.output.technicalAnalysis }}"},{"content":"=\"recommendationText\":  {{ $json.output.recommendationText }}"},{"role":"system","content":"Ensure that the text in the \"recommendationText\" \"technicalAnalysis\" values is written in proper Hebrew, like a professional analyst.\nReturn the same JSON format, but rewrite \"recommendationText\" \"technicalAnalysis\" values better.\nשים לב שכותבים \"רצועות בולינגר\" ולא \"חגורות בולינגר\""}]},"jsonOutput":true},"credentials":{"openAiApi":{"id":"2m1HH5crgPAhTJlv","name":"OpenAi account"}},"typeVersion":1.8}],"pinData":{},"connections":{"Merge":{"main":[[{"node":"Organizing Data","type":"main","index":0}]]},"Think":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]},"GPT 4o":{"ai_languageModel":[[{"node":"AI Agent","type":"ai_languageModel","index":0}]]},"Merge-2":{"main":[[{"node":"Warp as JSON for GPT","type":"main","index":0}]]},"AI Agent":{"main":[[{"node":"Refine Text","type":"main","index":0}]]},"Get MACD":{"main":[[{"node":"Merge","type":"main","index":2}]]},"ChatGPT 4o":{"main":[[{"node":"Set Final Response","type":"main","index":0}]]},"Refine Text":{"main":[[{"node":"Generate HTML","type":"main","index":0}]]},"Set Variable":{"main":[[{"node":"Merge-2","type":"main","index":0}]]},"Generate HTML":{"main":[[{"node":"Adjust HTML Colors","type":"main","index":0}]]},"Get Chart URL":{"main":[[{"node":"Download Chart","type":"main","index":0}]]},"Get News Data":{"main":[[{"node":"Analyse API Input","type":"main","index":0}]]},"Set Variables":{"main":[[{"node":"Get News Data","type":"main","index":0}]]},"Download Chart":{"main":[[{"node":"First Technical Analysis","type":"main","index":0}]]},"Organizing Data":{"main":[[{"node":"Merge-2","type":"main","index":1}]]},"Get Price History":{"main":[[{"node":"Calculate Support Resistance","type":"main","index":0}]]},"Schedule Trigger1":{"main":[[{"node":"Generate Variables For API","type":"main","index":0}]]},"Adjust HTML Colors":{"main":[[{"node":"Send Stock Analysis","type":"main","index":0}]]},"On form submission":{"main":[[{"node":"AI Agent","type":"main","index":0}]]},"Get Bollinger Bands":{"main":[[{"node":"Merge","type":"main","index":1}]]},"Trends Analysis Tool":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]},"Warp as JSON for GPT":{"main":[[{"node":"ChatGPT 4o","type":"main","index":0}]]},"Window Buffer Memory":{"ai_memory":[[{"node":"AI Agent","type":"ai_memory","index":0}]]},"Workflow Input Trigger":{"main":[[{"node":"Set Stock Symbol and API Key","type":"main","index":0}]]},"Technical Analysis Tool":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]},"First Technical Analysis":{"main":[[{"node":"Set Variable","type":"main","index":0}]]},"Structured Output Parser":{"ai_outputParser":[[{"node":"AI Agent","type":"ai_outputParser","index":0}]]},"Generate Variables For API":{"main":[[{"node":"Set Variables","type":"main","index":0}]]},"Calculate Support Resistance":{"main":[[{"node":"Merge","type":"main","index":0}]]},"Set Stock Symbol and API Key":{"main":[[{"node":"Get Chart URL","type":"main","index":0},{"node":"Get Price History","type":"main","index":0},{"node":"Get Bollinger Bands","type":"main","index":0},{"node":"Get MACD","type":"main","index":0}]]}}}


name: Generate & Enrich LinkedIn Leads with Apollo.io, LinkedIn API, Mail.so & GPT-3.5
author_name: Joseph
author_username: mjomba
categories: []
created_at: 2025-04-29T19:44:52.069Z
updated_at: 2025-04-29T19:44:52.069Z
url: https://n8n.io/workflows/3791
scraped_at: 2025-06-19 22:07:56
description:
  Note: Now includes an Apify alternative for Rapid API (Some users can't create new accounts on Rapid API, so I have added an alternative for you. But immediately you are able to get access to Rapid API, please use that option, it returns more detailed data). *Scroll to bottom for APify setup guide*

  This n8n workflow automates LinkedIn lead generation, enrichment, and activity analysis using Apollo.io, RapidAPI, Google Sheets and Mail.so.

  Perfect for sales teams, founders, B2B marketers, and cold outreach pros who want personalized lead insights to drive better conversion rates.

  ⚙️ How This Workflow Works

  The workflow is broken down into several key steps, each designed to help you build and enrich a valuable list of LinkedIn leads:

  1. 🔑 Lead Discovery (Keyword Search via Apollo)
  Pulls leads using Apollo.io's API based on keywords, industries, or job titles.
  Saves lead name, title, company, and LinkedIn URL to your Google Sheet.
  You can replace the trigger node from the form node to a webhook, whatsapp, telegram, etc, any way for you to send over your query variables over to initiate the workflow.

  2. 🧠 Username Extraction (from LinkedIn URL)
  Extracts the LinkedIn username from profile URLs using a simple script node.
  This is required for further enrichment via RapidAPI.

  3. ✉️ Email Lookup (via Apollo User ID)
  Uses the Apollo User ID to retrieve the lead’s verified work email.
  Ensures high-quality leads with reliable contact info.
  To double check that the email is currently valid, we use the mail.so api and filter out emails that fail deliverability and mx-record check. We don't wanna risk sending emails to no longer existent addresses, right?

  4. 🧾 Profile Summary Enrichment (via RapidAPI)
  Queries the LinkedIn Data API to fetch a lead’s profile summary/bio.
  Gives you a deeper understanding of their background and expertise.

  5. 📰 Recent Activity Collection (Posts & Reposts)
  Retrieves recent posts or reposts from each lead’s profile.
  Great for tailoring outreach with reference to what they’re currently talking about.

  6. 🗂️ Leads Database Update
  All enriched data is written to the same Google Sheet.
  New columns are filled in without overwriting existing data.

  ✅ Smart Retry & Row Status Logic

  Every subworkflow includes a fail-safe mechanism to ensure:

  ✅ Each row has status columns (e.g., done, failed, pending).
  🕒 A scheduled retry workflow resets failed rows to pending after 2 weeks (customizable).
  💬 This gives failed enrichments another chance to be processed later, reducing data loss.

  📋 Google Sheets Setup

  Template 1: Apollo Leads Scraping & Enrichment  

  Template 2: Enriched Leads Database  

  Make a copy to your Drive and use.

  Columns will be filled as each subworkflow runs (email, summary, interests, etc.)

  🔐 Required API Keys

  To use this workflow, you’ll need the following credentials:

  🧩 Apollo.io
  Sign up and get your key here: Apollo.io API Keys
  ⚠️ Important: Toggle the “Master API Key” option to ON when generating your key.  
  This ensures the same key can be used for all Apollo endpoints in this workflow.

  🌐 RapidAPI (LinkedIn Data API)
  Subscribe to the API here: LinkedIn Data API on RapidAPI
  Use the key in the x-rapidapi-key header in the relevant nodes.

  ✉️ Mail.so
  Sign up and get your key here: Mail.so API


  &gt; 💡 For both APIs, set up the credentials in n8n as “Generic Credential” types.  
  This way, you won’t need to reconfigure the headers in each node.

  🛠️ Customization Options

  Modify the Apollo filters (location, industry, seniority) to target your ideal customers.
  Change retry interval in the scheduler (e.g., weekly instead of 2 weeks).
  Connect the database to your email campaign tool like Mailchimp or Instantly.ai.
  Replace the AI nodes with your desired AI agents and customize the system messages further to get desired results.

  🆕 Apify Update Guide

  To use this workflow, you’ll need the following credentials:

  Login to Apify, then open this link; https://console.apify.com/actors/2SyF0bVxmgGr8IVCZ/

  Click on integrations and scroll down to API Solutions and select "Use API endpoints". Scroll to "Run Actor synchronously and get dataset items" and copy the actor endpoint url then paste it in the placeholder inside the http node of Apify alternative flow "apify-actor-endpoint". That's it, you are set to go.


  I am available for custom n8n workflows, if you like my work, please get in touch with me on email at joseph@uppfy.com

nodes_used:
  ["n8n-nodes-base.googleSheets","n8n-nodes-base.httpRequest","n8n-nodes-base.code","@n8n/n8n-nodes-langchain.openAi"]

workflow_json:
  {"id":"7wwY8wfZdNpL83QQ","meta":{"instanceId":"b3c467df4053d13fe31cc98f3c66fa1d16300ba750506bfd019a0913cec71ea3","templateCredsSetupCompleted":true},"name":"LinkedIn Leads Scraping & Enrichment (Main)","tags":[],"nodes":[{"id":"5d07dfa4-6e6a-41a6-a2ea-8a8787331735","name":"On form submission","type":"n8n-nodes-base.formTrigger","position":[-80,-80],"webhookId":"28ff6927-5d05-4182-910b-be2381e3b2c4","parameters":{"options":{},"formTitle":"Leads Search","formFields":{"values":[{"fieldLabel":"Job Title","requiredField":true},{"fieldLabel":"Location","requiredField":true},{"fieldType":"number","fieldLabel":"Number of Leads","requiredField":true}]}},"typeVersion":2.2},{"id":"76ade63a-7b44-4fd1-85d8-6dbacd83aca7","name":"Split Out","type":"n8n-nodes-base.splitOut","position":[360,-80],"parameters":{"options":{},"fieldToSplitOut":"people"},"typeVersion":1,"alwaysOutputData":false},{"id":"3c7b7179-a8b9-41d6-9617-8eb7af38f571","name":"Google Sheets Trigger","type":"n8n-nodes-base.googleSheetsTrigger","position":[-100,280],"parameters":{"event":"rowAdded","options":{},"pollTimes":{"item":[{"mode":"everyMinute"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsTriggerOAuth2Api":{"id":"DKYpQQpUt3ceJiG4","name":"Google Sheets Trigger account"}},"typeVersion":1},{"id":"9a55977c-638f-49ae-a51f-420f71c15454","name":"OpenAI1","type":"@n8n/n8n-nodes-langchain.openAi","position":[360,280],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-3.5-turbo","cachedResultName":"GPT-3.5-TURBO"},"options":{},"messages":{"values":[{"content":"=remove the http or https://www.linkedin.com/in/ from this  {{ $json.linkedin_url }}"}]}},"credentials":{"openAiApi":{"id":"DO9F6MAeTGLeqgoF","name":"OpenAi account"}},"typeVersion":1.8},{"id":"ca4d6b69-d4cb-4aa1-89bc-f0b5555f31cd","name":"Google Sheets Trigger2","type":"n8n-nodes-base.googleSheetsTrigger","position":[-100,680],"parameters":{"event":"rowAdded","options":{},"pollTimes":{"item":[{"mode":"everyMinute"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsTriggerOAuth2Api":{"id":"DKYpQQpUt3ceJiG4","name":"Google Sheets Trigger account"}},"typeVersion":1},{"id":"15422703-fa1f-42e4-8387-7d2d4f154938","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-200,200],"parameters":{"color":3,"width":1260,"height":300,"content":"## Extract Linkedin Username \n"},"typeVersion":1},{"id":"f8ffe414-9ca9-463a-9b98-22a65a86d4c4","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[-200,-160],"parameters":{"width":1260,"height":300,"content":"## Scrape Leads from Apollo\n"},"typeVersion":1},{"id":"a09d3993-99d6-488e-8bc0-76542de7b53c","name":"Clean Data","type":"n8n-nodes-base.set","position":[600,-80],"parameters":{"options":{},"assignments":{"assignments":[{"id":"e3bfe30e-9136-4ac9-b3da-c26eb678153b","name":"id","type":"string","value":"={{ $json.id }}"},{"id":"d45c81fb-1461-45fd-be95-d5d9901d72d7","name":"name","type":"string","value":"={{ $json.name }}"},{"id":"b4b8f660-7758-4a5f-a8f6-dc8ab6355132","name":"linkedin_url","type":"string","value":"={{ $json.linkedin_url }}"},{"id":"399f533a-6e6b-4f40-8ed8-aa5dd39017cd","name":"title","type":"string","value":"={{ $json.title }}"},{"id":"227d34c5-17db-4436-b0c2-f74e5ae453f2","name":"organization","type":"string","value":"={{ $json.employment_history[0].organization_name }}"}]}},"typeVersion":3.4},{"id":"92d03853-644e-4c73-8ecf-1e216238b37f","name":"Add Leads to Google Sheet","type":"n8n-nodes-base.googleSheets","position":[840,-80],"parameters":{"columns":{"value":{"name":"={{ $json.name }}","title":"={{ $json.title }}","apollo_id":"={{ $json.id }}","linkedin_url":"={{ $json.linkedin_url }}","organization":"={{ $json.organization }}","posts_scrape_status":"unscraped","contacts_scrape_status":"pending","profile_summary_scrape":"pending","extract_username_status":"pending"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"organization","type":"string","display":true,"removed":false,"required":false,"displayName":"organization","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":false,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"removed":false,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"removed":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"removed":false,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":false,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"0275f8ba-78d7-4464-bc80-ee939e9575e7","name":"Add Linkedin Username","type":"n8n-nodes-base.googleSheets","position":[720,280],"parameters":{"columns":{"value":{"apollo_id":"={{ $('Get Pending Username Row').item.json.apollo_id }}","linkedin_username":"={{ $json.message.content }}","extract_username_status":"finished"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":false,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":false,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"phone_number","type":"string","display":true,"removed":true,"required":false,"displayName":"phone_number","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"removed":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"removed":true,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"42827be7-8727-46e6-8f12-5d517c3ce6f3","name":"Get Pending Username Row","type":"n8n-nodes-base.googleSheets","position":[140,280],"parameters":{"options":{"returnFirstMatch":true},"filtersUI":{"values":[{"lookupValue":"pending","lookupColumn":"extract_username_status"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"executeOnce":true,"typeVersion":4.5},{"id":"ffbb16a6-020e-4825-9690-3e89a8bbbbf7","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[-200,560],"parameters":{"color":5,"width":1700,"height":360,"content":"## Get Email Address, Validate Deliverability & Update Column Status"},"typeVersion":1},{"id":"d2a1b54a-bc1b-4f63-a1a4-b9bd2017f82d","name":"Add Email Address","type":"n8n-nodes-base.googleSheets","position":[1240,600],"parameters":{"columns":{"value":{"apollo_id":"={{ $('Get Pending Email Statuses').item.json.apollo_id }}","email_address":"={{ $json.data.email }}","contacts_scrape_status":"finished"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":true,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":false,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"removed":false,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"removed":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"removed":true,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"executeOnce":true,"typeVersion":4.5},{"id":"7abbce6e-90fa-4ac5-9a0b-0ba2669aa77a","name":"Mark Invalid Email","type":"n8n-nodes-base.googleSheets","position":[1240,760],"parameters":{"columns":{"value":{"apollo_id":"={{ $('Get Pending Email Statuses').item.json.apollo_id }}","contacts_scrape_status":"invalid_email"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":true,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"removed":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"removed":true,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"2e1b9e78-8953-4059-9bbd-117c78742e4b","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[1560,560],"parameters":{"color":5,"width":800,"height":360,"content":"## Update Contact Scrape Status from Invalid back to Pending"},"typeVersion":1},{"id":"dc603dc3-148c-4609-a698-eaae32832423","name":"Schedule Trigger","type":"n8n-nodes-base.scheduleTrigger","position":[1640,680],"parameters":{"rule":{"interval":[{"field":"weeks","triggerAtDay":[2],"triggerAtHour":8,"weeksInterval":4}]}},"typeVersion":1.2},{"id":"a23feccf-f6b5-4440-b12a-ea10b41f4037","name":"Get Email from Apollo","type":"n8n-nodes-base.httpRequest","position":[380,680],"parameters":{"url":"https://api.apollo.io/api/v1/people/match","method":"POST","options":{},"sendBody":true,"sendQuery":true,"sendHeaders":true,"bodyParameters":{"parameters":[{"name":"id","value":"={{ $json.apollo_id }}"}]},"queryParameters":{"parameters":[{"name":"reveal_personal_emails","value":"true"},{"name":"reveal_phone_number","value":"false"}]},"headerParameters":{"parameters":[{"name":"Cache-Control","value":"no-cache"},{"name":"accept","value":"application/json"},{"name":"x-api-key","value":"\"your_api_key\""}]}},"typeVersion":4.2},{"id":"dfd5224d-fdb6-41f2-a219-a4a8f94930cd","name":"Confirm Email Validity","type":"n8n-nodes-base.httpRequest","position":[620,680],"parameters":{"url":"=https://api.mails.so/v1/validate?email={{ $json.person.email }}","options":{},"sendHeaders":true,"headerParameters":{"parameters":[{"name":"x-mails-api-key","value":"\"your_api_key\""}]}},"typeVersion":4.2},{"id":"14fab17d-b38b-43de-a38e-c8d49afcf1d0","name":"Get Pending Email Statuses","type":"n8n-nodes-base.googleSheets","position":[140,680],"parameters":{"options":{"returnFirstMatch":true},"filtersUI":{"values":[{"lookupValue":"pending","lookupColumn":"contacts_scrape_status"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"executeOnce":true,"typeVersion":4.5},{"id":"09a0c5eb-d098-47f1-a85c-13c93d8b5296","name":"Google Sheets Trigger3","type":"n8n-nodes-base.googleSheetsTrigger","position":[-100,1160],"parameters":{"event":"rowAdded","options":{},"pollTimes":{"item":[{"mode":"everyMinute"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsTriggerOAuth2Api":{"id":"DKYpQQpUt3ceJiG4","name":"Google Sheets Trigger account"}},"typeVersion":1},{"id":"3f592a9c-f692-44c0-b5be-9c5ff1506f24","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[-200,1040],"parameters":{"color":6,"width":1920,"height":360,"content":"## Get Profile Summary & Update Status (Rapid API✅ - Primary Option)"},"typeVersion":1},{"id":"6bc025b4-b150-493b-ae38-33772cb51a76","name":"Get Profile Posts","type":"n8n-nodes-base.httpRequest","onError":"continueRegularOutput","maxTries":2,"position":[340,1640],"parameters":{"url":"https://linkedin-data-api.p.rapidapi.com/get-profile-posts","options":{},"sendQuery":true,"sendHeaders":true,"queryParameters":{"parameters":[{"name":"username","value":"={{ $json.linkedin_username }}"},{"name":"start","value":"0"}]},"headerParameters":{"parameters":[{"name":"x-rapidapi-host","value":"linkedin-data-api.p.rapidapi.com"},{"name":"x-rapidapi-key","value":"\"your_api_key\""}]}},"retryOnFail":true,"typeVersion":4.2},{"id":"758da5bf-91e9-434d-9de5-eb3e907524e1","name":"Get About Profile","type":"n8n-nodes-base.httpRequest","onError":"continueRegularOutput","maxTries":2,"position":[380,1160],"parameters":{"url":"https://linkedin-data-api.p.rapidapi.com","options":{},"sendQuery":true,"sendHeaders":true,"queryParameters":{"parameters":[{"name":"username","value":"={{ $json.linkedin_username }}"}]},"headerParameters":{"parameters":[{"name":"x-rapidapi-host","value":"linkedin-data-api.p.rapidapi.com"},{"name":"x-rapidapi-key","value":"\"your_api_key\""}]}},"retryOnFail":true,"typeVersion":4.2},{"id":"9f88649f-3c41-4624-b94f-242d94118b0b","name":"Get Pending About and Posts Rows","type":"n8n-nodes-base.googleSheets","position":[140,1160],"parameters":{"options":{"returnFirstMatch":true},"filtersUI":{"values":[{"lookupValue":"pending","lookupColumn":"profile_summary_scrape"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"executeOnce":true,"typeVersion":4.5},{"id":"2e8bb4a1-5fa9-446a-8b55-bffd6ceb5648","name":"Clean Profile Data","type":"n8n-nodes-base.code","onError":"continueErrorOutput","maxTries":2,"position":[620,1160],"parameters":{"jsCode":"return [{\n  summary: $input.first().json.summary,\n  headline: $input.first().json.headline,\n  nationality: $input.first().json.geo.country,\n  languaage: $input.first().json.languages[0].name,\n  education: $input.first().json.educations[0].schoolName,\n  fieldOfStudy: $input.first().json.educations[0].fieldOfStudy,\n  employment_company: $input.first().json.position[0].companyName,\n  company_industry: $input.first().json.position[0].companyIndustry,\n  position: $input.first().json.position[0].title,\n  company_location: $input.first().json.position[0].location,\n  employment_description_1: $input.first().json.position[0].description,\n}];\n"},"retryOnFail":true,"typeVersion":2},{"id":"87aa2108-44df-4b7f-8090-f2e86cbf5bf9","name":"Google Sheets Trigger4","type":"n8n-nodes-base.googleSheetsTrigger","position":[-120,1640],"parameters":{"event":"rowAdded","options":{},"pollTimes":{"item":[{"mode":"everyMinute"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsTriggerOAuth2Api":{"id":"DKYpQQpUt3ceJiG4","name":"Google Sheets Trigger account"}},"typeVersion":1},{"id":"e3318dd3-4b6a-4994-92bc-2e715f398397","name":"Sticky Note5","type":"n8n-nodes-base.stickyNote","position":[-200,1520],"parameters":{"color":7,"width":1920,"height":360,"content":"## Get Summary of Latest Linkedin Profile Posts (Rapid API✅ - Primary Option)"},"typeVersion":1},{"id":"b1c117e8-493a-443f-912b-dbf4718c3348","name":"Get Pending About and Posts Rows1","type":"n8n-nodes-base.googleSheets","position":[100,1640],"parameters":{"options":{"returnFirstMatch":true},"filtersUI":{"values":[{"lookupValue":"unscraped","lookupColumn":"posts_scrape_status"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"executeOnce":true,"typeVersion":4.5},{"id":"c480f3ed-f316-4dd8-877b-49191d5aa694","name":"Clean Posts Data","type":"n8n-nodes-base.code","onError":"continueErrorOutput","maxTries":2,"position":[580,1640],"parameters":{"jsCode":"const data = $input.first().json.data || [];\n\nfunction getText(post, reshared = false) {\n  if (!post) return \"\";\n  return reshared ? (post.resharedPost?.text || \"\") : (post.text || \"\");\n}\n\nfunction getDate(post) {\n  if (!post) return \"\";\n  return post.postedDate || post.postedDateTimestamp || \"\";\n}\n\nreturn [{\n  json: {\n    post_1: getText(data[0]),\n    post_1_date: getDate(data[0]),\n\n    post_2: getText(data[1]),\n    post_2_date: getDate(data[1]),\n\n    post_3: getText(data[2]),\n    post_3_date: getDate(data[2]),\n\n    post_4: getText(data[3], true),\n    post_4_date: getDate(data[3]),\n\n    post_5: getText(data[4], true),\n    post_5_date: getDate(data[4]),\n  }\n}];\n"},"retryOnFail":true,"typeVersion":2,"waitBetweenTries":3000},{"id":"88d093e7-0180-4585-b338-11f4974fa0a7","name":"Sticky Note6","type":"n8n-nodes-base.stickyNote","position":[-200,1980],"parameters":{"color":4,"width":800,"height":360,"content":"## Update Completely Enriched Profile to Final Database"},"typeVersion":1},{"id":"a9a96a05-f135-4a19-827f-85a750bc1551","name":"Google Sheets Trigger5","type":"n8n-nodes-base.googleSheetsTrigger","position":[-120,2100],"parameters":{"event":"rowAdded","options":{},"pollTimes":{"item":[{"mode":"everyMinute"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsTriggerOAuth2Api":{"id":"DKYpQQpUt3ceJiG4","name":"Google Sheets Trigger account"}},"typeVersion":1},{"id":"ef709a3f-7e75-4904-9d3e-299db8db5d60","name":"Sticky Note8","type":"n8n-nodes-base.stickyNote","position":[1780,1040],"parameters":{"color":6,"width":800,"height":360,"content":"## Update profile summary status from failed back to pending"},"typeVersion":1},{"id":"91a1eaed-8645-4eeb-8558-c28a6d55f689","name":"Schedule Trigger2","type":"n8n-nodes-base.scheduleTrigger","position":[1860,1160],"parameters":{"rule":{"interval":[{"field":"weeks","triggerAtDay":[2],"triggerAtHour":8,"weeksInterval":4}]}},"typeVersion":1.2},{"id":"f7658227-39b3-4efb-971c-f17860d20fb8","name":"get invalid email rows","type":"n8n-nodes-base.googleSheets","position":[1880,680],"parameters":{"options":{"returnFirstMatch":false},"filtersUI":{"values":[{"lookupValue":"invalid_email","lookupColumn":"contacts_scrape_status"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"executeOnce":true,"typeVersion":4.5},{"id":"fce87e8a-9eb3-4c86-a4e3-f93534333206","name":"update_to_pending","type":"n8n-nodes-base.googleSheets","position":[2100,680],"parameters":{"columns":{"value":{"apollo_id":"={{ $json.apollo_id }}","contacts_scrape_status":"pending"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":true,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"removed":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"removed":true,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"9583cab2-6373-4e44-b9f2-ba0e8c8871e7","name":"get_failed_profile_summary_rows","type":"n8n-nodes-base.googleSheets","position":[2100,1160],"parameters":{"options":{"returnFirstMatch":false},"filtersUI":{"values":[{"lookupValue":"failed","lookupColumn":"profile_summary_scrape"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"executeOnce":true,"typeVersion":4.5},{"id":"a09d1560-2213-46ff-9668-5e98d5051e1f","name":"update_to_pending1","type":"n8n-nodes-base.googleSheets","position":[2320,1160],"parameters":{"columns":{"value":{"apollo_id":"={{ $json.apollo_id }}","profile_summary_scrape":"pending"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":true,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"removed":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"removed":false,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"809d74c0-bb21-4651-bf52-ccb7e8fac5a6","name":"Sticky Note9","type":"n8n-nodes-base.stickyNote","position":[1760,1520],"parameters":{"color":7,"width":800,"height":360,"content":"## Update posts summary status from failed back to pending"},"typeVersion":1},{"id":"ee1a5073-87a9-4395-9c79-b56165a5325d","name":"Schedule Trigger3","type":"n8n-nodes-base.scheduleTrigger","position":[1840,1640],"parameters":{"rule":{"interval":[{"field":"weeks","triggerAtDay":[2],"triggerAtHour":8,"weeksInterval":4}]}},"typeVersion":1.2},{"id":"4a89e593-adb3-4999-bb0a-cf8cc751a76a","name":"get_failed_posts_summary_rows1","type":"n8n-nodes-base.googleSheets","position":[2080,1640],"parameters":{"options":{"returnFirstMatch":false},"filtersUI":{"values":[{"lookupValue":"failed","lookupColumn":"posts_scrape_status"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"executeOnce":true,"typeVersion":4.5},{"id":"369715fe-67c5-41f1-9070-af5063f80fe0","name":"Posts AI Summarizer","type":"@n8n/n8n-nodes-langchain.openAi","position":[1060,1560],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-3.5-turbo","cachedResultName":"GPT-3.5-TURBO"},"options":{},"messages":{"values":[{"content":"=Below are the most recent posts and reposts from a LinkedIn user. Summarize them collectively in no more than two short paragraphs. Focus on capturing the main themes, tone, and any recurring interests or professional concerns.\n\nAvoid listing each post separately — instead, synthesize the information into a narrative that gives a clear idea of what this person is currently focused on or passionate about.\n\nPosts: {{ $json.postsString }}\n\nKeep it insightful but brief — no more than 2 concise paragraphs."}]},"simplify":false},"credentials":{"openAiApi":{"id":"DO9F6MAeTGLeqgoF","name":"OpenAi account"}},"executeOnce":true,"typeVersion":1.8},{"id":"a571b341-1a4b-42d7-a4fc-5dd8333e3dab","name":"AI Profile Summarizer","type":"@n8n/n8n-nodes-langchain.openAi","position":[1100,1080],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-3.5-turbo","cachedResultName":"GPT-3.5-TURBO"},"options":{},"messages":{"values":[{"content":"=Please summarize the following linkedin profile data {{ $json.profileString }} for a lead I want to cold email. I want to combine this summary with another information about them to send personalied emails, so please make sure you include relevant bits in the summary"}]},"simplify":false},"credentials":{"openAiApi":{"id":"DO9F6MAeTGLeqgoF","name":"OpenAi account"}},"executeOnce":true,"typeVersion":1.8},{"id":"8a81b29d-0ffa-4418-9aec-21eb94df7d26","name":"Update Profile Summary","type":"n8n-nodes-base.googleSheets","position":[1500,1080],"parameters":{"columns":{"value":{"apollo_id":"={{ $('Get Pending About and Posts Rows').item.json.apollo_id }}","about_linkedin_profile":"={{ $json.choices[0].message.content }}","profile_summary_scrape":"completed"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":true,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"4803c443-be3c-4578-90fc-ab78403a0913","name":"Update Posts Summary","type":"n8n-nodes-base.googleSheets","position":[1440,1560],"parameters":{"columns":{"value":{"apollo_id":"={{ $('Get Pending About and Posts Rows1').item.json.apollo_id }}","posts_scrape_status":"scraped","recent_posts_summary":"={{ $json.choices[0].message.content }}"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"organization","type":"string","display":true,"removed":true,"required":false,"displayName":"organization","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":true,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"removed":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"removed":true,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":false,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":false,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"ccf8b76d-1aba-4c5a-b19e-30aac3ab682a","name":"Get Completely Enriched Profiles","type":"n8n-nodes-base.googleSheets","position":[120,2100],"parameters":{"options":{"returnFirstMatch":true},"filtersUI":{"values":[{"lookupValue":"finished","lookupColumn":"contacts_scrape_status"},{"lookupValue":"completed","lookupColumn":"profile_summary_scrape"},{"lookupValue":"scraped","lookupColumn":"posts_scrape_status"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"executeOnce":true,"typeVersion":4.5},{"id":"bfe41bd1-3edc-456a-9bf4-8e88009060e4","name":"update_to_unscraped","type":"n8n-nodes-base.googleSheets","position":[2300,1640],"parameters":{"columns":{"value":{"apollo_id":"={{ $json.apollo_id }}","posts_scrape_status":"unscraped"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":true,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"removed":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"removed":true,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":false,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"0d6e42a6-8b7e-493e-b920-7cb4d3994151","name":"Append to Enriched Leads Database","type":"n8n-nodes-base.googleSheets","position":[340,2100],"parameters":{"columns":{"value":{"Position":"={{ $json.title }}","Lead Name":"={{ $json.name }}","Email Address":"={{ $json.email_address }}","Company/Organization":"={{ $json.organization }}","Recent Posts Summary":"={{ $json.recent_posts_summary }}","Linkedin Profile Summary":"={{ $json.about_linkedin_profile }}"},"schema":[{"id":"Lead Name","type":"string","display":true,"removed":false,"required":false,"displayName":"Lead Name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Email Address","type":"string","display":true,"removed":false,"required":false,"displayName":"Email Address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Company/Organization","type":"string","display":true,"removed":false,"required":false,"displayName":"Company/Organization","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Position","type":"string","display":true,"required":false,"displayName":"Position","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Linkedin Profile Summary","type":"string","display":true,"required":false,"displayName":"Linkedin Profile Summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Recent Posts Summary","type":"string","display":true,"required":false,"displayName":"Recent Posts Summary","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["Email Address"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"appendOrUpdate","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1c5USULUPS-2_RdNf29cyDguuHH7A7JNwzFCjQQUJTvE/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1c5USULUPS-2_RdNf29cyDguuHH7A7JNwzFCjQQUJTvE","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1c5USULUPS-2_RdNf29cyDguuHH7A7JNwzFCjQQUJTvE/edit?usp=drivesdk","cachedResultName":"Enriched Leads Database"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"dfc1a7e4-6348-4a84-b6b9-d9f53184948e","name":"update status to failed","type":"n8n-nodes-base.googleSheets","position":[880,1240],"parameters":{"columns":{"value":{"apollo_id":"={{ $('Get Pending About and Posts Rows').item.json.apollo_id }}","profile_summary_scrape":"failed"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"organization","type":"string","display":true,"removed":true,"required":false,"displayName":"organization","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":true,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"removed":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"a14fce10-9a03-4005-b440-f2bb5e4c4eed","name":"Google Sheets","type":"n8n-nodes-base.googleSheets","position":[840,1720],"parameters":{"columns":{"value":{"apollo_id":"={{ $('Get Pending About and Posts Rows1').item.json.apollo_id }}","posts_scrape_status":"failed"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"organization","type":"string","display":true,"removed":true,"required":false,"displayName":"organization","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":true,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"removed":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"removed":true,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"77a48254-5972-4a41-90e9-3903219e78cc","name":"Stringify Profile Data1","type":"n8n-nodes-base.code","position":[880,1080],"parameters":{"jsCode":"const profile = items[0].json;\n\nreturn [{\n  json: {\n    profileString: JSON.stringify(profile, null, 2) // Pretty print with 2-space indentation\n  }\n}];"},"typeVersion":2},{"id":"fb8cf143-7a62-4fdc-8888-b60841354352","name":"Stringify Posts Data","type":"n8n-nodes-base.code","position":[840,1560],"parameters":{"jsCode":"const profile = items[0].json;\n\nreturn [{\n  json: {\n    postsString: JSON.stringify(profile, null, 2) // Pretty print with 2-space indentation\n  }\n}];"},"typeVersion":2},{"id":"c440d713-3371-41a5-9093-8084ecb0eabd","name":"Generate Leads with Apollo.io","type":"n8n-nodes-base.httpRequest","position":[140,-80],"parameters":{"url":"https://api.apollo.io/api/v1/mixed_people/search","method":"POST","options":{"response":{"response":{}}},"jsonBody":"={\n  \"person_locations\": [\"{{ $json.Location }}\"],\n  \"person_titles\": [\"{{ $json['Job Title'] }}\"],\n  \"page\": 1,\n  \"per_page\": {{$json['Number of Leads']}},\n  \"projection\": [\"id\", \"name\", \"linkedin_url\", \"title\"]\n}","sendBody":true,"sendHeaders":true,"specifyBody":"json","headerParameters":{"parameters":[{"name":"Cache-Control","value":"no-cache"},{"name":"accept","value":"application/json"},{"name":"x-api-key","value":"\"your_api_key\""}]}},"typeVersion":4.2},{"id":"81edf4b0-cc78-4d62-ba0d-5e655c01c824","name":"Google Sheets Trigger6","type":"n8n-nodes-base.googleSheetsTrigger","position":[2920,1380],"parameters":{"event":"rowAdded","options":{},"pollTimes":{"item":[{"mode":"everyMinute"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsTriggerOAuth2Api":{"id":"DKYpQQpUt3ceJiG4","name":"Google Sheets Trigger account"}},"typeVersion":1},{"id":"7227fad7-9799-4159-b522-d552e0c0bd44","name":"Sticky Note7","type":"n8n-nodes-base.stickyNote","position":[2840,1060],"parameters":{"color":3,"width":2040,"height":820,"content":"## Get Profile and Latest Posts Summary & Update Status (Apify Alternative - Secondary, if rapid api option doesn't work )\nSome users that dont have an account on rapid API cant sign up hence are unable to get to the api and get an api key. This alternative is tailored for you. Immediatelty you get access to rapid API, please switch back to using that one. It is more reliable in scraping detailed data. See updated [documentation](https://docs.google.com/document/d/1U0Kk0HXO-9thjPcgFPu2Jk_yMtqLTv-hZobaKIGWOxM/edit?usp=sharing)"},"typeVersion":1},{"id":"c807c49c-ddea-4d63-8d06-070c13ae34f3","name":"Get Pending About and Posts Rows2","type":"n8n-nodes-base.googleSheets","position":[3160,1380],"parameters":{"options":{"returnFirstMatch":true},"filtersUI":{"values":[{"lookupValue":"pending","lookupColumn":"profile_summary_scrape"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"executeOnce":true,"typeVersion":4.5},{"id":"7d79491d-5ab8-4435-b655-97e81380428f","name":"Clean Profile Data1","type":"n8n-nodes-base.code","maxTries":2,"position":[3720,1280],"parameters":{"jsCode":"// Safe LinkedIn profile details scraper for n8n\nconst profileData = {};\nconst input = $input.first() && $input.first().json ? $input.first().json : {};\n\n// Basic profile information with fallbacks\nif (input.about) profileData.summary = input.about;\nif (input.headline) profileData.headline = input.headline;\nif (input.addressCountryOnly) profileData.nationality = input.addressCountryOnly;\n\n// Education information with fallbacks\nif (input.educations && Array.isArray(input.educations) && input.educations.length > 0) {\n  const education = input.educations[0];\n  if (education.title) profileData.education = education.title;\n  if (education.subtitle) profileData.fieldOfStudy = education.subtitle;\n}\n\n// Employment information with fallbacks\nif (input.experiences && Array.isArray(input.experiences) && input.experiences.length > 0) {\n  const experience = input.experiences[0];\n  if (experience.subtitle) profileData.employment_company = experience.subtitle;\n  if (experience.title) profileData.position = experience.title;\n  if (experience.metadata) profileData.company_location = experience.metadata;\n}\n\n// Return the profile data in n8n expected format\nreturn [{ json: profileData }];"},"retryOnFail":true,"typeVersion":2},{"id":"e13acd2c-5d4a-4683-9293-6520fc4da4a6","name":"Stringify Data2","type":"n8n-nodes-base.code","position":[3900,1280],"parameters":{"jsCode":"const profile = items[0].json;\n\nreturn [{\n  json: {\n    profileString: JSON.stringify(profile, null, 2) // Pretty print with 2-space indentation\n  }\n}];"},"typeVersion":2},{"id":"e774ffc5-2c4a-46fc-89a1-1ecb3ba2ef10","name":"AI Profile Summarizer1","type":"@n8n/n8n-nodes-langchain.openAi","position":[4100,1280],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-3.5-turbo","cachedResultName":"GPT-3.5-TURBO"},"options":{},"messages":{"values":[{"content":"=Please summarize the following linkedin profile data {{ $json.profileString }} for a lead I want to cold email. I want to combine this summary with another information about them to send personalied emails, so please make sure you include relevant bits in the summary"}]},"simplify":false},"credentials":{"openAiApi":{"id":"DO9F6MAeTGLeqgoF","name":"OpenAi account"}},"executeOnce":true,"typeVersion":1.8},{"id":"557a763a-8388-4f34-bd35-d91cd7018b97","name":"Update Profile Summary1","type":"n8n-nodes-base.googleSheets","position":[4640,1380],"parameters":{"columns":{"value":{"apollo_id":"={{ $('Get Pending About and Posts Rows2').item.json.apollo_id }}","posts_scrape_status":"scraped","about_linkedin_profile":"={{ $json.choices[0].message.content }}","profile_summary_scrape":"completed"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"organization","type":"string","display":true,"removed":true,"required":false,"displayName":"organization","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":true,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":false,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"867e28c2-e24f-419c-838f-d1ddc97cbd84","name":"Stringify Data3","type":"n8n-nodes-base.code","position":[3900,1520],"parameters":{"jsCode":"const profile = items[0].json;\n\nreturn [{\n  json: {\n    postsString: JSON.stringify(profile, null, 2) // Pretty print with 2-space indentation\n  }\n}];"},"typeVersion":2},{"id":"ba3ba6c9-803d-4f84-ad95-314fbe68e391","name":"Posts AI Summarizer1","type":"@n8n/n8n-nodes-langchain.openAi","position":[4100,1520],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-3.5-turbo","cachedResultName":"GPT-3.5-TURBO"},"options":{},"messages":{"values":[{"content":"=Below are the most recent posts and reposts from a LinkedIn user. Summarize them collectively in no more than two short paragraphs. Focus on capturing the main themes, tone, and any recurring interests or professional concerns.\n\nAvoid listing each post separately — instead, synthesize the information into a narrative that gives a clear idea of what this person is currently focused on or passionate about.\n\nPosts: {{ $json.postsString }}\n\nKeep it insightful but brief — no more than 2 concise paragraphs."}]},"simplify":false},"credentials":{"openAiApi":{"id":"DO9F6MAeTGLeqgoF","name":"OpenAi account"}},"executeOnce":true,"typeVersion":1.8},{"id":"8de0e703-1af4-491a-b915-391e94bc0b18","name":"Get About Profile2","type":"n8n-nodes-base.httpRequest","onError":"continueErrorOutput","position":[3400,1380],"parameters":{"url":"\"apify-actor-endpoint\"","options":{},"jsonBody":"={\n    \"profileUrls\": [\n        \"{{ $json.linkedin_url }}\"\n    ]\n}","sendBody":true,"specifyBody":"json"},"typeVersion":4.2},{"id":"2a082269-015e-4d01-850b-89db56135df1","name":"Clean Profile Data2","type":"n8n-nodes-base.code","maxTries":2,"position":[3720,1520],"parameters":{"jsCode":"// n8n function to extract LinkedIn posts\nconst items = [];\n\n// Check if input exists and has updates\nif (!$input.first() || !$input.first().json || !$input.first().json.updates || !Array.isArray($input.first().json.updates)) {\n  // Return a single item with \"no posts\" message\n  items.push({\n    json: {\n      message: \"No posts found\"\n    }\n  });\n  return items;\n}\n\nconst updates = $input.first().json.updates;\n\n// If no posts available, return the \"no posts\" message\nif (updates.length === 0) {\n  items.push({\n    json: {\n      message: \"No posts found\"\n    }\n  });\n  return items;\n}\n\n// Process available posts (up to 4)\nfor (let i = 0; i < Math.min(updates.length, 4); i++) {\n  if (updates[i] && updates[i].postText) {\n    // Create an item for each post with the required n8n structure\n    items.push({\n      json: {\n        postNumber: i + 1,\n        postText: updates[i].postText\n      }\n    });\n  }\n}\n\n// If no valid posts were processed\nif (items.length === 0) {\n  items.push({\n    json: {\n      message: \"No posts found\"\n    }\n  });\n}\n\nreturn items;"},"retryOnFail":true,"typeVersion":2},{"id":"966bf4ba-2680-4092-b62c-d37f77ffa2fc","name":"Merge","type":"n8n-nodes-base.merge","position":[4460,1380],"parameters":{"mode":"combine","options":{},"combineBy":"combineAll"},"typeVersion":3.1},{"id":"36682aa5-73bf-405b-ab10-43bc144b37af","name":"update status to failed1","type":"n8n-nodes-base.googleSheets","position":[3400,1640],"parameters":{"columns":{"value":{"apollo_id":"={{ $('Get Pending About and Posts Rows2').item.json.apollo_id }}","profile_summary_scrape":"failed"},"schema":[{"id":"apollo_id","type":"string","display":true,"removed":false,"required":false,"displayName":"apollo_id","defaultMatch":false,"canBeUsedToMatch":true},{"id":"name","type":"string","display":true,"removed":true,"required":false,"displayName":"name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"organization","type":"string","display":true,"removed":true,"required":false,"displayName":"organization","defaultMatch":false,"canBeUsedToMatch":true},{"id":"title","type":"string","display":true,"removed":true,"required":false,"displayName":"title","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_url","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_url","defaultMatch":false,"canBeUsedToMatch":true},{"id":"linkedin_username","type":"string","display":true,"removed":true,"required":false,"displayName":"linkedin_username","defaultMatch":false,"canBeUsedToMatch":true},{"id":"extract_username_status","type":"string","display":true,"removed":true,"required":false,"displayName":"extract_username_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"email_address","type":"string","display":true,"removed":true,"required":false,"displayName":"email_address","defaultMatch":false,"canBeUsedToMatch":true},{"id":"contacts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"contacts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"about_linkedin_profile","type":"string","display":true,"removed":true,"required":false,"displayName":"about_linkedin_profile","defaultMatch":false,"canBeUsedToMatch":true},{"id":"profile_summary_scrape","type":"string","display":true,"required":false,"displayName":"profile_summary_scrape","defaultMatch":false,"canBeUsedToMatch":true},{"id":"recent_posts_summary","type":"string","display":true,"removed":true,"required":false,"displayName":"recent_posts_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"posts_scrape_status","type":"string","display":true,"removed":true,"required":false,"displayName":"posts_scrape_status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["apollo_id"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1d99PlHkp9RPeSAtmATgQ4OC4Selcp8JSFLNuKx-n1EQ/edit?usp=drivesdk","cachedResultName":"apollo ai leads & enrichment"}},"credentials":{"googleSheetsOAuth2Api":{"id":"d0qeLhShx9sGXalR","name":"Google Sheets"}},"typeVersion":4.5},{"id":"a3d2a7e0-87ed-46ac-bb75-91c54e6e1f09","name":"If email is valid","type":"n8n-nodes-base.if","position":[880,680],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"loose"},"combinator":"or","conditions":[{"id":"bc3200af-7ae4-4944-b410-ebb459e2d927","operator":{"type":"string","operation":"notContains"},"leftValue":"={{ $json.data.result }}","rightValue":"undeliverable"},{"id":"b0c7d1c3-a733-4547-899e-ff76b66765ad","operator":{"type":"string","operation":"notEmpty","singleValue":true},"leftValue":"={{ $json.data.mx_record }}","rightValue":"[null]"}]},"looseTypeValidation":true},"typeVersion":2.2}],"active":false,"pinData":{},"settings":{"executionOrder":"v1"},"versionId":"eef068bb-348c-46c5-940b-d6042431aa01","connections":{"Merge":{"main":[[{"node":"Update Profile Summary1","type":"main","index":0}]]},"OpenAI1":{"main":[[{"node":"Add Linkedin Username","type":"main","index":0}]]},"Split Out":{"main":[[{"node":"Clean Data","type":"main","index":0}]]},"Clean Data":{"main":[[{"node":"Add Leads to Google Sheet","type":"main","index":0}]]},"Stringify Data2":{"main":[[{"node":"AI Profile Summarizer1","type":"main","index":0}]]},"Stringify Data3":{"main":[[{"node":"Posts AI Summarizer1","type":"main","index":0}]]},"Clean Posts Data":{"main":[[{"node":"Stringify Posts Data","type":"main","index":0}],[{"node":"Google Sheets","type":"main","index":0}]]},"Schedule Trigger":{"main":[[{"node":"get invalid email rows","type":"main","index":0}]]},"Get About Profile":{"main":[[{"node":"Clean Profile Data","type":"main","index":0}]]},"Get Profile Posts":{"main":[[{"node":"Clean Posts Data","type":"main","index":0}]]},"If email is valid":{"main":[[{"node":"Add Email Address","type":"main","index":0}],[{"node":"Mark Invalid Email","type":"main","index":0}]]},"Schedule Trigger2":{"main":[[{"node":"get_failed_profile_summary_rows","type":"main","index":0}]]},"Schedule Trigger3":{"main":[[{"node":"get_failed_posts_summary_rows1","type":"main","index":0}]]},"update_to_pending":{"main":[[]]},"Clean Profile Data":{"main":[[{"node":"Stringify Profile Data1","type":"main","index":0}],[{"node":"update status to failed","type":"main","index":0}]]},"Get About Profile2":{"main":[[{"node":"Clean Profile Data1","type":"main","index":0},{"node":"Clean Profile Data2","type":"main","index":0}],[{"node":"update status to failed1","type":"main","index":0}]]},"On form submission":{"main":[[{"node":"Generate Leads with Apollo.io","type":"main","index":0}]]},"Clean Profile Data1":{"main":[[{"node":"Stringify Data2","type":"main","index":0}]]},"Clean Profile Data2":{"main":[[{"node":"Stringify Data3","type":"main","index":0}]]},"Posts AI Summarizer":{"main":[[{"node":"Update Posts Summary","type":"main","index":0}]]},"Posts AI Summarizer1":{"main":[[{"node":"Merge","type":"main","index":1}]]},"Stringify Posts Data":{"main":[[{"node":"Posts AI Summarizer","type":"main","index":0}]]},"AI Profile Summarizer":{"main":[[{"node":"Update Profile Summary","type":"main","index":0}]]},"Get Email from Apollo":{"main":[[{"node":"Confirm Email Validity","type":"main","index":0}]]},"Google Sheets Trigger":{"main":[[{"node":"Get Pending Username Row","type":"main","index":0}]]},"AI Profile Summarizer1":{"main":[[{"node":"Merge","type":"main","index":0}]]},"Confirm Email Validity":{"main":[[{"node":"If email is valid","type":"main","index":0}]]},"Google Sheets Trigger2":{"main":[[{"node":"Get Pending Email Statuses","type":"main","index":0}]]},"Google Sheets Trigger3":{"main":[[{"node":"Get Pending About and Posts Rows","type":"main","index":0}]]},"Google Sheets Trigger4":{"main":[[{"node":"Get Pending About and Posts Rows1","type":"main","index":0}]]},"Google Sheets Trigger5":{"main":[[{"node":"Get Completely Enriched Profiles","type":"main","index":0}]]},"Google Sheets Trigger6":{"main":[[{"node":"Get Pending About and Posts Rows2","type":"main","index":0}]]},"get invalid email rows":{"main":[[{"node":"update_to_pending","type":"main","index":0}]]},"Stringify Profile Data1":{"main":[[{"node":"AI Profile Summarizer","type":"main","index":0}]]},"Get Pending Username Row":{"main":[[{"node":"OpenAI1","type":"main","index":0}]]},"Get Pending Email Statuses":{"main":[[{"node":"Get Email from Apollo","type":"main","index":0}]]},"Generate Leads with Apollo.io":{"main":[[{"node":"Split Out","type":"main","index":0}]]},"get_failed_posts_summary_rows1":{"main":[[{"node":"update_to_unscraped","type":"main","index":0}]]},"get_failed_profile_summary_rows":{"main":[[{"node":"update_to_pending1","type":"main","index":0}]]},"Get Completely Enriched Profiles":{"main":[[{"node":"Append to Enriched Leads Database","type":"main","index":0}]]},"Get Pending About and Posts Rows":{"main":[[{"node":"Get About Profile","type":"main","index":0}]]},"Get Pending About and Posts Rows1":{"main":[[{"node":"Get Profile Posts","type":"main","index":0}]]},"Get Pending About and Posts Rows2":{"main":[[{"node":"Get About Profile2","type":"main","index":0}]]}}}


name: Create a Session-Based Telegram Chatbot with GPT-4o-mini and Google Sheets
author_name: Davide
author_username: n3witalia
categories: []
created_at: 2025-04-30T08:31:26.629Z
updated_at: 2025-04-30T08:31:26.629Z
url: https://n8n.io/workflows/3798
scraped_at: 2025-06-19 22:07:56
description:
  How It Works  
  This workflow creates an AI-powered Telegram chatbot with session management, allowing users to:  
  Start new conversations** (/new).  
  Check current sessions** (/current).  
  Resume past sessions** (/resume).  
  Get summaries** (/summary).  
  Ask questions** (/question).  

  Key Components:  
  Session Management**:  
    Uses Google Sheets to track active/expired sessions (storing SESSION IDs and STATE).  
    /new creates a session; /resume reactivates past ones.  
  AI Processing**:  
    OpenAI GPT-4 generates responses with contextual memory (via Simple Memory node).  
    Summarization: Condenses past conversations when requested.  
  Data Logging**:  
    All interactions (prompts/responses) are saved to Google Sheets for audit and retrieval.  
  User Interaction**:  
    Telegram commands trigger specific actions (e.g., /question [query] fetches answers from session history).  

  Main Advantages

  1. Multi-session Handling  
  Each user can create, manage, and switch between multiple sessions independently, perfect for organizing different conversations without confusion.

  2. Persistent Memory  
  Conversations are stored in Google Sheets, ensuring that chat history and session states are preserved even if the server or n8n instance restarts.

  3. Commands for Full Control  
  With intuitive commands like /new, /current, /resume, /summary, and /question, users can manage sessions easily without needing a web interface.

  4. Smart Summarization and Q&A  
  Thanks to OpenAI models, the workflow can summarize entire conversations or answer specific questions about past discussions, saving time and improving the chatbot’s usability.

  5. Easy Setup and Scalability  
  By using Google Sheets instead of a database, the workflow is easy to clone, modify, and deploy — ideal for quick prototyping or lightweight production uses.

  6. Modular and Extensible  
  Each logical block (new session, get current session, resume, summarize, ask question) is modular, making it easy to extend the workflow with additional features like analytics, personalized greetings, or integrations with CRM systems.

  Setup Steps  

  Prerequisites:  
  Telegram Bot Token**: Create via BotFather.  
  Google Sheets**:  
    Duplicate this template.  
    Two sheets: Session (active/inactive sessions) and Database (chat logs).  
  OpenAI API Key**: For GPT-4 responses.  

  Configuration:  
  Telegram Integration:  
     Add your bot token to the Telegram Trigger and Telegram Send nodes.  

  Google Sheets Setup:  
     Authenticate the Google Sheets nodes with OAuth.  
     Ensure sheet names (Session, Database) and column mappings match the template.  

  OpenAI & Memory:  
     Add your API key to the OpenAI Chat Model nodes.  
     Adjust contextWindowLength in the Simple Memory node for conversation history length.  

  Testing:  
     Use Telegram commands to test:  
       /new: Starts a session.  
       /question [query]: Tests AI responses.  
       /summary: Checks summarization.  

  Deployment:  
     Activate the workflow; the bot will respond to Telegram messages in real-time.  

  Need help customizing?  
  Contact me for consulting and support or add me on Linkedin. 

nodes_used:
  ["n8n-nodes-base.googleSheets","n8n-nodes-base.telegram","n8n-nodes-base.code","@n8n/n8n-nodes-langchain.agent","@n8n/n8n-nodes-langchain.chainSummarization","@n8n/n8n-nodes-langchain.chainLlm","@n8n/n8n-nodes-langchain.lmChatOpenAi","@n8n/n8n-nodes-langchain.memoryBufferWindow"]

workflow_json:
  {"id":"A7dRnMf9WybO8O02","meta":{"instanceId":"a4bfc93e975ca233ac45ed7c9227d84cf5a2329310525917adaf3312e10d5462","templateCredsSetupCompleted":true},"name":"Telegram ChatBot with multiple sessions","tags":[],"nodes":[{"id":"d3104851-90ec-4f0c-ab4d-aee5a6faf81b","name":"OpenAI Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","position":[640,1180],"parameters":{"model":{"__rl":true,"mode":"list","value":"gpt-4o-mini"},"options":{}},"credentials":{"openAiApi":{"id":"4zwP0MSr8zkNvvV9","name":"OpenAi account"}},"typeVersion":1.2},{"id":"e25050a1-e49b-4b46-8452-ce7b3388c7f3","name":"Simple Memory","type":"@n8n/n8n-nodes-langchain.memoryBufferWindow","position":[780,1180],"parameters":{"sessionKey":"={{ $('Get session').item.json.SESSION }}","sessionIdType":"customKey","contextWindowLength":100},"typeVersion":1.3},{"id":"d50951ae-e9a2-492c-8ff5-a039ca6975e4","name":"Get message","type":"n8n-nodes-base.telegramTrigger","position":[-520,-60],"webhookId":"8d87dcf7-1608-4255-a1f1-03b700a42f0e","parameters":{"updates":["message"],"additionalFields":{}},"credentials":{"telegramApi":{"id":"rQ5q95W7uKesMDx4","name":"Telegram account Fastewb"}},"typeVersion":1.2},{"id":"1bc9aa0f-8af8-47b8-b7d7-6bfd9280b65f","name":"Command or text?","type":"n8n-nodes-base.switch","position":[-20,-60],"parameters":{"rules":{"values":[{"outputKey":"New session","conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"955f3c39-0732-4a63-b3f7-70ab0753a68a","operator":{"type":"string","operation":"startsWith"},"leftValue":"={{ $('Get message').item.json.message.text }}","rightValue":"/new"}]},"renameOutput":true},{"outputKey":"Current session","conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"790f64bc-f3cc-4ef4-9d43-80853742fee6","operator":{"type":"string","operation":"startsWith"},"leftValue":"={{ $('Get message').item.json.message.text }}","rightValue":"/current"}]},"renameOutput":true},{"outputKey":"Resume session","conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"39f35352-aee4-4a16-b82f-7f58ab8120f0","operator":{"type":"string","operation":"startsWith"},"leftValue":"={{ $('Get message').item.json.message.text }}","rightValue":"/resume"}]},"renameOutput":true},{"outputKey":"Summary ","conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"270d71bf-8ecf-46e7-a601-cc5d1dc58e72","operator":{"name":"filter.operator.equals","type":"string","operation":"equals"},"leftValue":"={{ $('Get message').item.json.message.text }}","rightValue":"/summary"}]},"renameOutput":true},{"outputKey":"Question","conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"55fe98fa-e39a-41b3-983f-359a8e730f21","operator":{"type":"string","operation":"startsWith"},"leftValue":"={{ $('Get message').item.json.message.text }}","rightValue":"/question"}]},"renameOutput":true}]},"options":{"fallbackOutput":"extra"}},"typeVersion":3.2},{"id":"4a405332-99a8-4ec9-84d5-89f9f5f1610f","name":"Get session","type":"n8n-nodes-base.googleSheets","position":[-260,-60],"parameters":{"options":{"returnFirstMatch":true},"filtersUI":{"values":[{"lookupValue":"current","lookupColumn":"STATE"}]},"sheetName":{"__rl":true,"mode":"list","value":207843712,"cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit#gid=207843712","cachedResultName":"Session"},"documentId":{"__rl":true,"mode":"list","value":"1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit?usp=drivesdk","cachedResultName":"Chatbot with session"}},"credentials":{"googleSheetsOAuth2Api":{"id":"JYR6a64Qecd6t8Hb","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"860fa8d5-6c9b-476b-857b-9c47a023d72f","name":"Disable previous session","type":"n8n-nodes-base.googleSheets","position":[640,-760],"parameters":{"columns":{"value":{"STATE":"expire","SESSION":"={{ $('Get session').item.json.SESSION }}"},"schema":[{"id":"SESSION","type":"string","display":true,"removed":false,"required":false,"displayName":"SESSION","defaultMatch":false,"canBeUsedToMatch":true},{"id":"STATE","type":"string","display":true,"removed":false,"required":false,"displayName":"STATE","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["SESSION"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":207843712,"cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit#gid=207843712","cachedResultName":"Session"},"documentId":{"__rl":true,"mode":"list","value":"1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit?usp=drivesdk","cachedResultName":"Chatbot with session"}},"credentials":{"googleSheetsOAuth2Api":{"id":"JYR6a64Qecd6t8Hb","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"6ff1cf54-5406-4c86-a77e-8424208d4728","name":"Set new session","type":"n8n-nodes-base.googleSheets","position":[960,-760],"parameters":{"columns":{"value":{"STATE":"current","SESSION":"={{ $('Get message').item.json.update_id }}"},"schema":[{"id":"SESSION","type":"string","display":true,"required":false,"displayName":"SESSION","defaultMatch":false,"canBeUsedToMatch":true},{"id":"STATE","type":"string","display":true,"removed":false,"required":false,"displayName":"STATE","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":207843712,"cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit#gid=207843712","cachedResultName":"Session"},"documentId":{"__rl":true,"mode":"list","value":"1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit?usp=drivesdk","cachedResultName":"Chatbot with session"}},"credentials":{"googleSheetsOAuth2Api":{"id":"JYR6a64Qecd6t8Hb","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"be9c755a-0c52-453c-b448-98975d4c6e87","name":"Session activated","type":"n8n-nodes-base.telegram","position":[1240,-760],"webhookId":"fa5e7641-068c-40cb-b490-a6809e74c629","parameters":{"text":"New session activated","chatId":"={{ $('Get message').item.json.message.from.id }}","additionalFields":{"appendAttribution":false}},"credentials":{"telegramApi":{"id":"rQ5q95W7uKesMDx4","name":"Telegram account Fastewb"}},"typeVersion":1.2},{"id":"31478ec5-c1de-43db-ac9d-9b34f68e951c","name":"Send response","type":"n8n-nodes-base.telegram","position":[1100,980],"webhookId":"fa5e7641-068c-40cb-b490-a6809e74c629","parameters":{"text":"={{ $json.output }}","chatId":"={{ $('Get message').item.json.message.from.id }}","additionalFields":{"appendAttribution":false}},"credentials":{"telegramApi":{"id":"rQ5q95W7uKesMDx4","name":"Telegram account Fastewb"}},"typeVersion":1.2},{"id":"be35b9a6-2914-409f-bc9c-6199ccb9e4ed","name":"Update database","type":"n8n-nodes-base.googleSheets","position":[1420,980],"parameters":{"columns":{"value":{"DATE":"={{$now}}","PROMPT":"={{ $('Get message').item.json.message.text }}","SESSION":"={{ $('Get session').item.json.SESSION }}","RESPONSE":"={{ $('Telegram Chatbot').item.json.output }}"},"schema":[{"id":"SESSION","type":"string","display":true,"required":false,"displayName":"SESSION","defaultMatch":false,"canBeUsedToMatch":true},{"id":"DATE","type":"string","display":true,"required":false,"displayName":"DATE","defaultMatch":false,"canBeUsedToMatch":true},{"id":"PROMPT","type":"string","display":true,"required":false,"displayName":"PROMPT","defaultMatch":false,"canBeUsedToMatch":true},{"id":"RESPONSE","type":"string","display":true,"required":false,"displayName":"RESPONSE","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit#gid=0","cachedResultName":"Database"},"documentId":{"__rl":true,"mode":"list","value":"1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit?usp=drivesdk","cachedResultName":"Chatbot with session"}},"credentials":{"googleSheetsOAuth2Api":{"id":"JYR6a64Qecd6t8Hb","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"dacc79e0-56ff-47d6-b09f-16ebfb31cbfa","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-540,-540],"parameters":{"color":3,"width":620,"height":360,"content":"# Telegram ChatBot with multiple sessions\n\nThis workflow creates an **AI-powered Telegram chatbot** with **session management**, allowing users to:  \n- **Start new conversations** (`/new`).  \n- **Check current sessions** (`/current`).  \n- **Resume past sessions** (`/resume`).  \n- **Get summaries** (`/summary`).  \n- **Ask questions** (`/question`). \n\n- Clone [this sheet](https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit?usp=sharing)"},"typeVersion":1},{"id":"55751f99-ee2e-429b-99cd-fef30eb56fb8","name":"Summarization Chain","type":"@n8n/n8n-nodes-langchain.chainSummarization","position":[1040,200],"parameters":{"options":{"summarizationMethodAndPrompts":{"values":{"prompt":"=Write a concise summary of the following:\n\n\n\"{{ $json.fullText }}\"\n\n\nCONCISE SUMMARY:","combineMapPrompt":"=Write a concise summary of the following:\n\n\n\"{{ $json.fullText }}\"\n\n\nCONCISE SUMMARY:"}}}},"typeVersion":2},{"id":"c7f0d136-4a77-4bc8-b2ef-b03fa41ad1ff","name":"OpenAI Chat Model1","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","position":[1020,380],"parameters":{"model":{"__rl":true,"mode":"list","value":"gpt-4o-mini"},"options":{}},"credentials":{"openAiApi":{"id":"4zwP0MSr8zkNvvV9","name":"OpenAi account"}},"typeVersion":1.2},{"id":"a455cf00-2f72-4377-ac2a-d65ab8c30e31","name":"OpenAI Chat Model2","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","position":[1660,800],"parameters":{"model":{"__rl":true,"mode":"list","value":"gpt-4o-mini"},"options":{}},"credentials":{"openAiApi":{"id":"4zwP0MSr8zkNvvV9","name":"OpenAi account"}},"typeVersion":1.2},{"id":"e55082e4-4e65-4fbb-8850-d3b64093eab6","name":"Basic LLM Chain","type":"@n8n/n8n-nodes-langchain.chainLlm","position":[1680,600],"parameters":{"text":"=Question:\n{{ $json.question }}","messages":{"messageValues":[{"message":"=You have to answer the questions that are asked by analyzing the following text:\n\n{{ $json.fullText }}"}]},"promptType":"define"},"typeVersion":1.6},{"id":"1e670eaa-fd9f-4e79-b2a8-bd43240455db","name":"Get message1","type":"n8n-nodes-base.set","position":[600,-100],"parameters":{"options":{},"assignments":{"assignments":[{"id":"56bf47bc-e84c-4cee-8aa0-927f3d2e31c5","name":"text","type":"string","value":"={{ $('Get message').item.json.message.text }}"}]}},"typeVersion":3.4},{"id":"65771b1e-47fc-439d-a1ea-9901bac047d8","name":"Set to expire","type":"n8n-nodes-base.googleSheets","position":[1040,-100],"parameters":{"columns":{"value":{"STATE":"expire","SESSION":"={{ $('Get session').item.json.SESSION }}"},"schema":[{"id":"SESSION","type":"string","display":true,"removed":false,"required":false,"displayName":"SESSION","defaultMatch":false,"canBeUsedToMatch":true},{"id":"STATE","type":"string","display":true,"removed":false,"required":false,"displayName":"STATE","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["SESSION"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":207843712,"cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit#gid=207843712","cachedResultName":"Session"},"documentId":{"__rl":true,"mode":"list","value":"1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit?usp=drivesdk","cachedResultName":"Chatbot with session"}},"credentials":{"googleSheetsOAuth2Api":{"id":"JYR6a64Qecd6t8Hb","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"d18268f6-8792-4a66-aa32-35b596e7fcad","name":"Exist?","type":"n8n-nodes-base.if","position":[1540,-100],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"1710e38c-80d1-49ba-9814-2b2c6b3c3b8d","operator":{"type":"object","operation":"empty","singleValue":true},"leftValue":"={{ $json }}","rightValue":""}]}},"typeVersion":2.2},{"id":"6eeb7e4d-8af0-4d71-981d-66a39f4870a3","name":"OK","type":"n8n-nodes-base.telegram","position":[1880,-40],"webhookId":"fa5e7641-068c-40cb-b490-a6809e74c629","parameters":{"text":"=The current session is {{ $json.SESSION }}","chatId":"={{ $('Get message').item.json.message.from.id }}","additionalFields":{"appendAttribution":false}},"credentials":{"telegramApi":{"id":"rQ5q95W7uKesMDx4","name":"Telegram account Fastewb"}},"typeVersion":1.2},{"id":"1a6b9065-154f-4c43-9406-110636884f3d","name":"KO","type":"n8n-nodes-base.telegram","position":[1880,-200],"webhookId":"fa5e7641-068c-40cb-b490-a6809e74c629","parameters":{"text":"=This session doesn't exist","chatId":"={{ $('Get message').item.json.message.from.id }}","additionalFields":{"appendAttribution":false}},"credentials":{"telegramApi":{"id":"rQ5q95W7uKesMDx4","name":"Telegram account Fastewb"}},"typeVersion":1.2},{"id":"a2463ecc-fc82-401d-bf1b-6104f4b379e6","name":"Trim resume","type":"n8n-nodes-base.code","position":[820,-100],"parameters":{"jsCode":"for (const item of $input.all()) {\n  const text = item.json.text || '';\n  const match = text.match(/\\/resume\\s+(.*)/);\n\n  if (match) {\n    item.json.resume = match[1].trim();\n  } else {\n    item.json.resume = null; \n  }\n}\n\nreturn $input.all();"},"typeVersion":2},{"id":"c24a0c1d-290f-4e0c-8da9-98de14a65e2b","name":"Get session1","type":"n8n-nodes-base.googleSheets","position":[600,200],"parameters":{"options":{},"filtersUI":{"values":[{"lookupValue":"={{ $('Get session').item.json.SESSION }}","lookupColumn":"SESSION"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit#gid=0","cachedResultName":"Database"},"documentId":{"__rl":true,"mode":"list","value":"1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit?usp=drivesdk","cachedResultName":"Chatbot with session"}},"credentials":{"googleSheetsOAuth2Api":{"id":"JYR6a64Qecd6t8Hb","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"84cc4b12-ab2b-4509-a2e3-1c4d952904c5","name":"Prompt + Resume","type":"n8n-nodes-base.code","position":[820,200],"parameters":{"jsCode":"let fullText = '';\n\nfor (const item of $input.all()) {\n  const prompt = item.json.PROMPT || '';\n  const response = item.json.RESPONSE || '';\n  fullText += `PROMPT: ${prompt}\\nRESPONSE: ${response}\\n`;\n}\nconst chat_id=$('Get message').first().json.message.from.id\n\nreturn [{ json: { fullText, chat_id } }];\n"},"typeVersion":2},{"id":"3ae34388-95a0-41d7-8f20-7fa74817dab8","name":"Send summary","type":"n8n-nodes-base.telegram","position":[1420,200],"webhookId":"fa5e7641-068c-40cb-b490-a6809e74c629","parameters":{"text":"={{ $json.response.text }}","chatId":"={{ $('Prompt + Resume').item.json.chat_id }}","additionalFields":{"appendAttribution":false}},"credentials":{"telegramApi":{"id":"rQ5q95W7uKesMDx4","name":"Telegram account Fastewb"}},"typeVersion":1.2},{"id":"d38eb058-29a3-4500-b6de-f700b8f501e9","name":"Get message2","type":"n8n-nodes-base.set","position":[620,600],"parameters":{"options":{},"assignments":{"assignments":[{"id":"56bf47bc-e84c-4cee-8aa0-927f3d2e31c5","name":"text","type":"string","value":"={{ $('Get message').item.json.message.text }}"}]}},"typeVersion":3.4},{"id":"ee6713b5-6264-43e8-86a4-f9584049f05b","name":"Trim question","type":"n8n-nodes-base.code","position":[860,600],"parameters":{"jsCode":"for (const item of $input.all()) {\n  const text = item.json.text || '';\n  const match = text.match(/\\/question\\s+(.*)/);\n\n  if (match) {\n    item.json.question = match[1].trim();\n  } else {\n    item.json.question = null; // oppure \"\" se preferisci\n  }\n}\n\nreturn $input.all();"},"typeVersion":2},{"id":"65482069-1c41-4977-8f18-75f192866fdd","name":"Set new current session","type":"n8n-nodes-base.googleSheets","position":[1280,-100],"parameters":{"columns":{"value":{"STATE":"current","SESSION":"={{ $('Trim resume').item.json.resume }}"},"schema":[{"id":"SESSION","type":"string","display":true,"removed":false,"required":false,"displayName":"SESSION","defaultMatch":false,"canBeUsedToMatch":true},{"id":"STATE","type":"string","display":true,"removed":false,"required":false,"displayName":"STATE","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["SESSION"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":207843712,"cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit#gid=207843712","cachedResultName":"Session"},"documentId":{"__rl":true,"mode":"list","value":"1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit?usp=drivesdk","cachedResultName":"Chatbot with session"}},"credentials":{"googleSheetsOAuth2Api":{"id":"JYR6a64Qecd6t8Hb","name":"Google Sheets account"}},"notesInFlow":false,"typeVersion":4.5,"alwaysOutputData":true},{"id":"91ef2595-acca-4efd-8331-3450fa466d33","name":"Response + Text","type":"n8n-nodes-base.googleSheets","position":[1140,600],"parameters":{"options":{},"filtersUI":{"values":[{"lookupValue":"={{ $('Get session').item.json.SESSION }}","lookupColumn":"SESSION"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit#gid=0","cachedResultName":"Database"},"documentId":{"__rl":true,"mode":"list","value":"1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1MCJLAqKP0Y7Qr68ZYoSSBeEVyKI1QgAAZnlEiyqkzXo/edit?usp=drivesdk","cachedResultName":"Chatbot with session"}},"credentials":{"googleSheetsOAuth2Api":{"id":"JYR6a64Qecd6t8Hb","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"2fbaf240-c01e-48dc-90bf-cc655eb7cd4d","name":"fullText","type":"n8n-nodes-base.code","position":[1440,600],"parameters":{"jsCode":"let fullText = '';\n\nfor (const item of $input.all()) {\n  const prompt = item.json.PROMPT || '';\n  const response = item.json.RESPONSE || '';\n  fullText += `PROMPT: ${prompt}\\nRESPONSE: ${response}\\n`;\n}\nconst chat_id=$('Get message').first().json.message.from.id;\nconst question=$('Trim question').first().json.question;\n\nreturn [{ json: { fullText, chat_id, question } }];\n"},"typeVersion":2},{"id":"30e29b4f-da86-4cee-b875-7985b5d82b7e","name":"Send answer","type":"n8n-nodes-base.telegram","position":[2080,600],"webhookId":"fa5e7641-068c-40cb-b490-a6809e74c629","parameters":{"text":"={{ $json.text }}","chatId":"={{ $('fullText').item.json.chat_id }}","additionalFields":{"appendAttribution":false}},"credentials":{"telegramApi":{"id":"rQ5q95W7uKesMDx4","name":"Telegram account Fastewb"}},"typeVersion":1.2},{"id":"41e2b5b3-25a2-411c-929a-ad4c22f45cd1","name":"Send current session","type":"n8n-nodes-base.telegram","position":[620,-440],"webhookId":"fa5e7641-068c-40cb-b490-a6809e74c629","parameters":{"text":"=The current session is {{ $('Get session').item.json.SESSION }}","chatId":"={{ $('Get message').item.json.message.from.id }}","additionalFields":{"appendAttribution":false}},"credentials":{"telegramApi":{"id":"rQ5q95W7uKesMDx4","name":"Telegram account Fastewb"}},"typeVersion":1.2},{"id":"e30bb12b-8e46-4fe4-81b7-72ce036d431f","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[640,-880],"parameters":{"width":580,"height":80,"content":"## NEW SESSION\n"},"typeVersion":1},{"id":"7e23ee2d-b4ee-4be8-969b-c713039dacdf","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[620,-560],"parameters":{"width":580,"height":80,"content":"## GET CURRENT SESSION\n"},"typeVersion":1},{"id":"5b10cf55-3730-4e0c-8db7-49c82e9b1350","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[600,-220],"parameters":{"width":580,"height":80,"content":"## RESUME SESSION\n"},"typeVersion":1},{"id":"f0932a2c-ebf2-4179-972a-073e5edfa50d","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[600,100],"parameters":{"width":580,"height":80,"content":"## GET SUMMARY\n"},"typeVersion":1},{"id":"9d9fdca4-870b-4ef4-9415-2fb750eb6466","name":"Sticky Note5","type":"n8n-nodes-base.stickyNote","position":[620,500],"parameters":{"width":580,"height":80,"content":"## SEND QUESTION\n"},"typeVersion":1},{"id":"aa55d92a-ec31-42c3-8b17-fa9d81d99c31","name":"Sticky Note6","type":"n8n-nodes-base.stickyNote","position":[640,860],"parameters":{"width":580,"height":80,"content":"## CHATBOT\n"},"typeVersion":1},{"id":"fa4dd49c-881b-41d2-84b4-82cbd3e62d0e","name":"Telegram Chatbot","type":"@n8n/n8n-nodes-langchain.agent","position":[640,980],"parameters":{"text":"={{ $('Get message').item.json.message.text }}","options":{"systemMessage":"=Sei un assitente virtuale:\n\nData e ora corrente: {{ $now }}"},"promptType":"define"},"typeVersion":1.9}],"active":false,"pinData":{},"settings":{"timezone":"Europe/Rome","callerPolicy":"workflowsFromSameOwner","executionOrder":"v1","executionTimeout":-1},"versionId":"c6d418c0-aec9-498a-b5c7-318a3e4efe0b","connections":{"Exist?":{"main":[[{"node":"KO","type":"main","index":0}],[{"node":"OK","type":"main","index":0}]]},"fullText":{"main":[[{"node":"Basic LLM Chain","type":"main","index":0}]]},"Get message":{"main":[[{"node":"Get session","type":"main","index":0}]]},"Get session":{"main":[[{"node":"Command or text?","type":"main","index":0}]]},"Trim resume":{"main":[[{"node":"Set to expire","type":"main","index":0}]]},"Get message1":{"main":[[{"node":"Trim resume","type":"main","index":0}]]},"Get message2":{"main":[[{"node":"Trim question","type":"main","index":0}]]},"Get session1":{"main":[[{"node":"Prompt + Resume","type":"main","index":0}]]},"Send response":{"main":[[{"node":"Update database","type":"main","index":0}]]},"Set to expire":{"main":[[{"node":"Set new current session","type":"main","index":0}]]},"Simple Memory":{"ai_memory":[[{"node":"Telegram Chatbot","type":"ai_memory","index":0}]]},"Trim question":{"main":[[{"node":"Response + Text","type":"main","index":0}]]},"Basic LLM Chain":{"main":[[{"node":"Send answer","type":"main","index":0}]]},"Prompt + Resume":{"main":[[{"node":"Summarization Chain","type":"main","index":0}]]},"Response + Text":{"main":[[{"node":"fullText","type":"main","index":0}]]},"Set new session":{"main":[[{"node":"Session activated","type":"main","index":0}]]},"Command or text?":{"main":[[{"node":"Disable previous session","type":"main","index":0}],[{"node":"Send current session","type":"main","index":0}],[{"node":"Get message1","type":"main","index":0}],[{"node":"Get session1","type":"main","index":0}],[{"node":"Get message2","type":"main","index":0}],[{"node":"Telegram Chatbot","type":"main","index":0}]]},"Telegram Chatbot":{"main":[[{"node":"Send response","type":"main","index":0}]]},"OpenAI Chat Model":{"ai_languageModel":[[{"node":"Telegram Chatbot","type":"ai_languageModel","index":0}]]},"OpenAI Chat Model1":{"ai_languageModel":[[{"node":"Summarization Chain","type":"ai_languageModel","index":0}]]},"OpenAI Chat Model2":{"ai_languageModel":[[{"node":"Basic LLM Chain","type":"ai_languageModel","index":0}]]},"Summarization Chain":{"main":[[{"node":"Send summary","type":"main","index":0}]]},"Set new current session":{"main":[[{"node":"Exist?","type":"main","index":0}]]},"Disable previous session":{"main":[[{"node":"Set new session","type":"main","index":0}]]}}}


name: Automated AI Content Creation & Instagram Publishing from Google Sheets
author_name: Onur
author_username: onurpolat05
categories: []
created_at: 2025-05-03T08:06:31.593Z
updated_at: 2025-05-03T08:06:31.593Z
url: https://n8n.io/workflows/3840
scraped_at: 2025-06-19 22:07:56
description:
  Automated AI Content Creation & Instagram Publishing from Google Sheets

  This n8n workflow automates the creation and publishing of social media content directly to Instagram, using ideas stored in a Google Sheet. It leverages AI (Google Gemini and Replicate Flux) to generate concepts, image prompts, captions, and the final image, turning your content plan into reality with minimal manual intervention.

  Think of this as the execution engine for your content strategy. It assumes you have a separate process (whether manual entry, another workflow, or a different tool) for populating the Google Sheet with initial post ideas (including Topic, Audience, Voice, and Platform). This workflow takes those ideas and handles the rest, from AI generation to final publication.

  What does this workflow do?

  This workflow streamlines the content execution process by:

  Automatically fetching** unprocessed content ideas from a designated Google Sheet based on a schedule.
  Using Google Gemini to generate a platform-specific content concept (specifically for a 'Single Image' format).
  Generating two distinct AI image prompt options based on the concept using Gemini.
  Writing an engaging, platform-tailored caption (including hashtags) using Gemini, based on the first prompt option.
  Generating a visual image using the first prompt option via the Replicate API (using the Flux model).
  Publishing* the generated image and caption directly to a connected *Instagram Business account**.
  Updating the status** in the Google Sheet to mark the idea as completed, preventing reprocessing.

  Who is this for?

  Social Media Managers & Agencies:** Automate the execution of your content calendar stored in Google Sheets.
  Marketing Teams:** Streamline content production from planned ideas and ensure consistent posting schedules.
  Content Creators & Solopreneurs:** Save significant time by automating the generation and publishing process based on your pre-defined ideas.
  Anyone** using Google Sheets to plan social media content and wanting to automate the creative generation and posting steps with AI.

  Benefits

  Full Automation:** From fetching planned ideas to Instagram publishing, automate the entire content execution pipeline.
  AI-Powered Generation:** Leverage Google Gemini for creative concepts, prompts, and captions, and Replicate for image generation based on your initial topic.
  Content Calendar Execution:** Directly turn your Google Sheet plan into published posts.
  Time Savings:** Drastically reduce the manual effort involved in creating visuals and text for each planned post.
  Consistency:** Maintain a regular posting schedule by automatically processing your queue of ideas.
  Platform-Specific Content:** AI prompts are designed to tailor concepts, prompts, and captions for the platform specified in your sheet (e.g., Instagram or LinkedIn).

  How it Works

  Scheduled Trigger: The workflow starts automatically based on the schedule you set (e.g., every hour, daily).
  Fetch Idea: Reads the next row from your Google Sheet where the 'Status' column indicates it's pending (e.g., '0'). It only fetches one idea per run.
  Prepare Inputs: Extracts Topic, Audience, Voice, and Platform from the sheet data.
  AI Concept Generation (Gemini): Creates a single content concept suitable for a 'Single Image' post on the target platform.
  AI Prompt Generation (Gemini): Develops two detailed, distinct image prompt options based on the concept.
  AI Caption Generation (Gemini): Writes a caption tailored to the platform, using the first image prompt and other context.
  Image Generation (Replicate): Sends the first prompt to the Replicate API (Flux model) to generate the image.
  Prepare for Instagram: Formats the generated image URL and caption.
  Publish to Instagram: Uses the Facebook Graph API in three steps:
      Creates a media container by uploading the image URL and caption.
      Waits for Instagram to process the container.
      Publishes the processed container to your feed.
  Update Sheet: Changes the 'Status' in the Google Sheet for the processed row (e.g., to '1') to mark it as complete.

  n8n Nodes Used

  Schedule Trigger
  Google Sheets (Read & Update operations)
  Set (Multiple instances for data preparation)
  Langchain Chain - LLM (Multiple instances for Gemini calls)
  Langchain Chat Model - Google Gemini (Multiple instances)
  Langchain Output Parser - Structured (Multiple instances)
  HTTP Request (for Replicate API call)
  Wait
  Facebook Graph API (Multiple instances for Instagram publishing steps)

  Prerequisites

  Active n8n instance (Cloud or Self-Hosted).
  Google Account** with access to Google Sheets.
  Google Sheets API Credentials (OAuth2):** Configured in n8n.
  A Google Sheet** structured with columns like Topic, Audience, Voice, Platform, Status (or similar). Ensure your 'pending' and 'completed' statuses are defined (e.g., '0' and '1').
  Google Cloud Project** with the Vertex AI API enabled.
  Google Gemini API Credentials:** Configured in n8n (usually via Google Vertex AI credentials).
  Replicate Account** and API Token.
  Replicate API Credentials (Header Auth):** Configured in n8n.
  Facebook Developer Account**.
  Instagram Business Account** connected to a Facebook Page.
  Facebook App** with necessary permissions: instagram_basic, instagram_content_publish, pages_read_engagement, pages_show_list.
  Facebook Graph API Credentials (OAuth2):** Configured in n8n with the required permissions.

  Setup

  Import the workflow JSON into your n8n instance.
  Configure Schedule Trigger: Set the desired frequency (e.g., every 30 minutes, every 4 hours) for checking new ideas in the sheet.
  Configure Google Sheets Nodes:
      Select your Google Sheets OAuth2 credentials for both Google Sheets nodes.
      In 1. Get Next Post Idea..., enter your Spreadsheet ID and Sheet Name. Verify the Status filter matches your 'pending' value (e.g., 0).
      In 7. Update Post Status..., enter the same Spreadsheet ID and Sheet Name. Ensure the Matching Columns (e.g., Topic) and the Status value to update match your 'completed' value (e.g., 1).
  Configure Google Gemini Nodes: Select your configured Google Vertex AI / Gemini credentials in all Google Gemini Chat Model nodes.
  Configure Replicate Node (4. Generate Image...): Select your Replicate Header Auth credentials. The workflow uses black-forest-labs/flux-1.1-pro-ultra by default; you can change this if needed.
  Configure Facebook Graph API Nodes (6a, 6c):
      Select your Facebook Graph API OAuth2 credentials.
      Crucially, update the Instagram Account ID in the Node parameter of both Facebook Graph API nodes (6a and 6c). The template uses a placeholder (17841473009917118); replace this with your actual Instagram Business Account ID.
  Adjust Wait Node (6b): The default wait time might be sufficient, but if you encounter errors during publishing (especially with larger images/videos in the future), you might need to increase the wait duration.
  Activate the workflow.
  Populate your Google Sheet: Ensure you have rows with your content ideas and the correct 'pending' status (e.g., '0'). The workflow will pick them up on its next scheduled run.

  This workflow transforms your Google Sheet content plan into a fully automated AI-powered Instagram publishing engine. Start automating your social media presence today!

   

nodes_used:
  ["n8n-nodes-base.googleSheets","n8n-nodes-base.httpRequest","n8n-nodes-base.facebookGraphApi","@n8n/n8n-nodes-langchain.chainLlm","@n8n/n8n-nodes-langchain.outputParserStructured","@n8n/n8n-nodes-langchain.lmChatGoogleGemini"]

workflow_json:
  {"meta":{"instanceId":"02e782574ebb30fbddb2c3fd832c946466d718819d25f6fe4b920124ff3fc2c1","templateCredsSetupCompleted":true},"nodes":[{"id":"c53bc70a-2e85-465a-b977-73cfd268ba41","name":"Scheduled Start: Check for New Posts","type":"n8n-nodes-base.scheduleTrigger","position":[-1240,-380],"parameters":{"rule":{"interval":[{}]}},"typeVersion":1.2},{"id":"aa73f2af-f311-465b-8270-b027b17eec0d","name":"1. Get Next Post Idea from Sheet","type":"n8n-nodes-base.googleSheets","position":[-920,-380],"parameters":{"options":{"returnFirstMatch":true},"filtersUI":{"values":[{"lookupValue":"0","lookupColumn":"Status"}]},"sheetName":{"__rl":true,"mode":"list","value":1510137257,"cachedResultUrl":"https://docs.google.com/spreadsheets/d/1hG2NMi-4fMa7D5qGonCN8bsYVya4L2TOB_8mI4XK-9k/edit#gid=1510137257","cachedResultName":"Postİ"},"documentId":{"__rl":true,"mode":"list","value":"1hG2NMi-4fMa7D5qGonCN8bsYVya4L2TOB_8mI4XK-9k","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1hG2NMi-4fMa7D5qGonCN8bsYVya4L2TOB_8mI4XK-9k/edit?usp=drivesdk","cachedResultName":"Medium Post Automation"}},"credentials":{"googleSheetsOAuth2Api":{"id":"VV5AyFvgYkc4TfC7","name":"Onur Drive "}},"typeVersion":4.5},{"id":"42e129d0-5e4f-4226-a86f-b7fd04c8c497","name":"2. Prepare Input Variables (Topic, Audience, etc.)","type":"n8n-nodes-base.set","position":[-620,-380],"parameters":{"options":{},"assignments":{"assignments":[{"id":"aa3b9a02-ac6a-4d7f-937f-a0e6e566a0c8","name":"Topic","type":"string","value":"={{ $json.Topic }}"},{"id":"e48783e8-5f6b-4c54-bf4f-c004414dc510","name":"TargetAudience","type":"string","value":"={{ $json.Audience }}"},{"id":"c499a954-b4c6-4702-ab86-3656aa2b1783","name":"BrandVoice","type":"string","value":"={{ $json.Voice }}"},{"id":"210f7103-4d6b-42e9-9168-fd99dff94b5a","name":"Platform","type":"string","value":"={{ $json.Platform }}"}]}},"typeVersion":3.4},{"id":"5036db9b-1a1b-48d7-bfaa-1839ba5331b5","name":"3a. Generate Content Concept (Gemini)","type":"@n8n/n8n-nodes-langchain.chainLlm","position":[-160,-380],"parameters":{"text":"=","messages":{"messageValues":[{"message":"=<prompt>     <role>         You are a **highly imaginative Social Media Strategist** specializing in generating **unique, platform-aware content CONCEPTS** for **Instagram and LinkedIn**. You think beyond basic formats and consider audience engagement.     </role>      <task>         Based *only* on the `Topic`, `Target Audience`, `Brand Voice`, AND **target `Platform` ('Instagram' or 'LinkedIn')**, generate **exactly 1 creative content CONCEPT**. Focus on the **core message, angle, or hook**. The suggested format **MUST be \"Single Image\"**.         1.  **Platform Optimization:** **Explicitly tailor** the *type* and *angle* of the concept to the specified `Platform`. Consider typical user expectations and content formats:             * **Instagram:** Often more visual, storytelling, personal, community-focused, impactful single images.             * **LinkedIn:** Can utilize impactful single images to convey data points, key takeaways, or thought-provoking visuals supporting a concise message.         2.  **Originality:** Avoid common tropes (like basic quotes) unless the input strongly suggests it. Explore diverse angles: striking visual representations of data, metaphorical imagery, thought-provoking questions presented visually, behind-the-scenes moments captured in a compelling image, key message highlighted graphically.         3.  **Format Suggestion:** The format **MUST be \"Single Image\"**. The **CONCEPT is primary, the format is fixed**.      </task>      <input_context>         <param name=\"Topic\">{{ $json.Topic }}</param>         <param name=\"TargetAudience\">{{ $json.TargetAudience }}</param>         <param name=\"BrandVoice\">{{ $json.BrandVoice }}</param>         <param name=\"Platform\">{{ $json.Platform }}</param>     </input_context>      <output_instructions>         Your response MUST be a single, valid JSON object containing exactly one key: `ideas`.         The value of `ideas` MUST be an array containing exactly 1 object.         The object in the array MUST have two keys: `concept` (string: the descriptive concept text) and `suggested_format` (string: **MUST be \"Single Image\"**).         Example: `{\"ideas\": [{\"concept\": \"Concept text...\", \"suggested_format\": \"Single Image\"}]}`         Do NOT include any other text, explanations, or formatting outside the JSON structure.     </output_instructions> </prompt>"}]},"promptType":"define","hasOutputParser":true},"typeVersion":1.5},{"id":"5a882770-53af-466c-a297-856b088eb2b9","name":"(LLM Model for Concept)","type":"@n8n/n8n-nodes-langchain.lmChatGoogleGemini","position":[-160,-220],"parameters":{"options":{},"modelName":"models/gemini-2.0-flash-001"},"credentials":{"googlePalmApi":{"id":"hAoasnlx3jlRA3Kf","name":"BCP Gemini"}},"typeVersion":1},{"id":"a4132a05-def7-4c4f-ad1d-092533f3b9f2","name":"(Parse Concept JSON)","type":"@n8n/n8n-nodes-langchain.outputParserStructured","position":[0,-220],"parameters":{"schemaType":"manual","inputSchema":"{\n  \"type\": \"object\",\n  \"properties\": {\n    \"ideas\": {\n      \"type\": \"array\",\n      \"description\": \"An array containing exactly 1 content concept.\",\n      \"minItems\": 1,\n      \"maxItems\": 1,\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"concept\": {\n            \"type\": \"string\",\n            \"description\": \"The detailed text describing the content concept for a Single Image.\"\n          },\n          \"suggested_format\": {\n            \"type\": \"string\",\n            \"description\": \"The post format, which MUST be 'Single Image'.\",\n            \"const\": \"Single Image\"\n          }\n        },\n        \"required\": [\n          \"concept\",\n          \"suggested_format\"\n        ]\n      }\n    }\n  },\n  \"required\": [\n    \"ideas\"\n  ]\n}"},"typeVersion":1.2},{"id":"46338aad-5f7d-464f-a7fe-e9ae00aacad5","name":"3b. Generate Image Prompt Options (Gemini)","type":"@n8n/n8n-nodes-langchain.chainLlm","position":[580,-380],"parameters":{"text":"=","messages":{"messageValues":[{"message":"=<prompt>\n    <role>\n        You are an **Expert Instagram/LinkedIn Content Strategist and AI Image Prompt Engineer**. You excel at elaborating concepts based on user feedback and crafting distinct, detailed, and visually consistent prompt options tailored for the target platform.\n    </role>\n\n    <task>\n        1.  **Analyze** the `Chosen Idea`, `User Visual Input` (if provided and relevant), and **target `Platform`** to determine the optimal post format (in this case, assumed to be Single Image based on the output) and elaborate this into a practical `expanded_post_concept`. **Justify format choice based on concept AND platform norms.**\n        2.  **Incorporate** the user's visual direction (if any) into the concept description. If no specific visual input was given, propose a clear visual direction that aligns with the concept and platform.\n        3.  Generate **TWO DISTINCT OPTIONS** for the image prompts based on the `expanded_post_concept`. **Tailor the visual style and content nuances** described in the prompts to the target `Platform`. (E.g., LinkedIn visuals might be cleaner, more data-oriented; Instagram more lifestyle or emotive).\n        4.  **Ensure Distinction:** The two options should offer meaningful variety (e.g., style, composition, focus) while remaining true to the core concept.\n        5.  **Detail:** Prompts should be highly detailed, suitable for advanced AI image generators (include subject, action, setting, style, mood, composition, lighting, color palette keywords).\n    </task>\n\n    <input_context>\n        <param name=\"ChosenIdea\">{{ $json.output.ideas[0].concept }}</param>\n        <param name=\"OriginalTopic\"> {{ $('2. Prepare Input Variables (Topic, Audience, etc.)').item.json.Topic }}</param>\n        <param name=\"TargetAudience\"> {{ $('2. Prepare Input Variables (Topic, Audience, etc.)').item.json.TargetAudience }}</param>\n        <param name=\"BrandVoice\"> {{ $('2. Prepare Input Variables (Topic, Audience, etc.)').item.json.BrandVoice }}</param>\n        <param name=\"Platform\"> {{ $('2. Prepare Input Variables (Topic, Audience, etc.)').item.json.Platform }}</param>\n        </input_context>\n\n    <output_instructions>\n        Your response MUST be a single, valid JSON object containing exactly two keys: `expanded_post_concept` and `prompt_options`.\n        - `expanded_post_concept` (string): The elaborated visual concept, stating Single Image format and incorporating user input/platform considerations.\n        - `prompt_options` (array): MUST contain exactly TWO objects.\n            - Each object represents one prompt option and MUST have two keys:\n                - `option_description` (string): Briefly describe the distinct angle/style of this option (e.g., \"Option 1: Hyperrealistic...\").\n                - `prompts` (array of strings): Contains ONE string representing the detailed prompt for the single image.\n       \n         \n        Do NOT include any text outside this JSON structure. Do NOT generate captions here.\n    </output_instructions>\n</prompt>"}]},"promptType":"define","hasOutputParser":true},"typeVersion":1.5},{"id":"950b7c2f-cebc-41ae-aad8-38b139c1b2ea","name":"(LLM Model for Prompts)","type":"@n8n/n8n-nodes-langchain.lmChatGoogleGemini","position":[580,-220],"parameters":{"options":{},"modelName":"models/gemini-2.0-flash-001"},"credentials":{"googlePalmApi":{"id":"hAoasnlx3jlRA3Kf","name":"BCP Gemini"}},"typeVersion":1},{"id":"bfda2858-3a3a-4997-b30c-ce64429bd255","name":"(Parse Prompts JSON)","type":"@n8n/n8n-nodes-langchain.outputParserStructured","position":[740,-220],"parameters":{"schemaType":"manual","inputSchema":"{\n  \"type\": \"object\",\n  \"properties\": {\n    \"expanded_post_concept\": {\n      \"type\": \"string\",\n      \"description\": \"The elaborated visual concept, stating Single Image format and incorporating user input/platform considerations.\"\n    },\n    \"prompt_options\": {\n      \"type\": \"array\",\n      \"description\": \"An array containing exactly TWO prompt options for the single image concept.\",\n      \"minItems\": 2,\n      \"maxItems\": 2,\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"option_description\": {\n            \"type\": \"string\",\n            \"description\": \"Briefly describes the distinct angle/style of this option (e.g., 'Option 1: Hyperrealistic...').\"\n          },\n          \"prompts\": {\n            \"type\": \"array\",\n            \"description\": \"Contains ONE detailed prompt string for the single image.\",\n            \"minItems\": 1,\n            \"maxItems\": 1,\n            \"items\": {\n              \"type\": \"string\",\n              \"description\": \"A detailed image generation prompt.\"\n            }\n          }\n        },\n        \"required\": [\n          \"option_description\",\n          \"prompts\"\n        ]\n      }\n    }\n  },\n  \"required\": [\n    \"expanded_post_concept\",\n    \"prompt_options\"\n  ]\n}"},"typeVersion":1.2},{"id":"0151450f-fca2-4628-a268-f90186a79b9e","name":"3c. Generate Post Caption (Gemini)","type":"@n8n/n8n-nodes-langchain.chainLlm","position":[1540,-380],"parameters":{"text":"=","messages":{"messageValues":[{"message":"=<prompt>\n    <role>\n        You are an AI Instagram/LinkedIn **Caption Writer**. You specialize in crafting concise, engaging, and contextually relevant captions based on a generated image (represented by its prompt) and the overall content strategy, specifically tailored for the target platform.\n    </role>\n\n    <task>\n        Write a short, effective social media caption **specifically tailored for the target `Platform` ('Instagram' or 'LinkedIn')**.\n        * The caption must complement the image described by `ImagePrompt` and align with all context parameters (`ChosenIdea`, `OriginalTopic`, `TargetAudience`, `BrandVoice`).\n        * **Platform Style:** Adapt tone, length, calls-to-action, and hashtag usage:\n            * **Instagram:** Can be more conversational, use more emojis, ask engaging questions, often benefits from slightly longer, more storytelling captions if relevant. Use a mix of popular and niche hashtags (3-7 recommended).\n            * **LinkedIn:** Generally more professional, concise, focused on insights or value proposition. Calls-to-action often relate to reading more, commenting with professional opinions, or business objectives. Use targeted, professional hashtags (1-3 recommended).\n        * Include 1-5 relevant, platform-appropriate hashtags.\n    </task>\n\n    <input_context>\n        <param name=\"ImagePrompt\">{{ $json.output.prompt_options[0].prompts[0] }}</param>\n        <param name=\"ChosenIdea\">{{ $('3a. Generate Content Concept (Gemini)').item.json.output.ideas[0].concept }} </param>\n        <param name=\"OriginalTopic\">{{ $('1. Get Next Post Idea from Sheet').item.json.Topic }} </param>\n        <param name=\"TargetAudience\">{{ $('1. Get Next Post Idea from Sheet').item.json.Audience }}</param>\n        <param name=\"BrandVoice\">{{ $('1. Get Next Post Idea from Sheet').item.json.Voice }} </param>\n        <param name=\"Platform\">{{ $('1. Get Next Post Idea from Sheet').item.json.Platform }} </param>\n    </input_context>\n\n    <output_instructions>\n        Your response MUST be a single, valid JSON object containing exactly one key: `Caption`.\n        The value of `Caption` MUST be a string containing the generated caption text, including hashtags.\n        Example: `{\"Caption\": \"Caption text tailored for LinkedIn goes here. #ProfessionalDevelopment #IndustryInsights\"}`\n        Do NOT include any other text, explanations, or formatting outside the JSON structure.\n    </output_instructions>\n</prompt>"}]},"promptType":"define","hasOutputParser":true},"typeVersion":1.5},{"id":"d4667681-a5aa-4b63-802c-2adf8c111bb9","name":"(LLM Model for Caption)","type":"@n8n/n8n-nodes-langchain.lmChatGoogleGemini","position":[1560,-220],"parameters":{"options":{},"modelName":"models/gemini-2.0-flash"},"credentials":{"googlePalmApi":{"id":"hAoasnlx3jlRA3Kf","name":"BCP Gemini"}},"typeVersion":1},{"id":"59da1f6c-ee55-4095-8c95-d60d1177c142","name":"(Parse Caption JSON)","type":"@n8n/n8n-nodes-langchain.outputParserStructured","position":[1700,-220],"parameters":{"jsonSchemaExample":"{\n\t\"Caption\": \"Thee future of call centers is here!\"\n}"},"typeVersion":1.2},{"id":"a8d38be7-85b5-4a39-bd39-7f765699d471","name":"4. Generate Image using Prompt 1 (Replicate Flux)","type":"n8n-nodes-base.httpRequest","position":[2360,-380],"parameters":{"url":"https://api.replicate.com/v1/models/black-forest-labs/flux-1.1-pro-ultra/predictions","method":"POST","options":{},"jsonBody":"={\n  \"input\": {\n    \"raw\": false,\n    \"prompt\": \"{{ $('3b. Generate Image Prompt Options (Gemini)').item.json.output.prompt_options[0].prompts[0] }}\",\n    \"aspect_ratio\": \"1:1\",\n    \"output_format\": \"jpg\",\n    \"safety_tolerance\": 6\n  }\n}","sendBody":true,"sendHeaders":true,"specifyBody":"json","authentication":"genericCredentialType","genericAuthType":"httpHeaderAuth","headerParameters":{"parameters":[{"name":"Prefer","value":"wait"}]}},"credentials":{"httpHeaderAuth":{"id":"iEtPzDTSlZBfz1RP","name":"Replicate"}},"typeVersion":4.2},{"id":"cc0f0035-0d63-4988-8ec8-89260261960a","name":"5. Prepare Data for Instagram API","type":"n8n-nodes-base.set","position":[2880,-380],"parameters":{"options":{},"assignments":{"assignments":[{"id":"8a4260ba-3bde-4444-8f42-8a8abd51ff0c","name":"ImageURL","type":"string","value":"={{ $json.output }}"},{"id":"1953ae03-6a86-4847-8686-5a928637be1d","name":"Caption","type":"string","value":"={{ $('3c. Generate Post Caption (Gemini)').item.json.output.Caption }}"}]}},"typeVersion":3.4},{"id":"f010c17f-1af3-4d04-a69c-8b03c70fc8f0","name":"6a. Create Instagram Media Container","type":"n8n-nodes-base.facebookGraphApi","position":[3180,-380],"parameters":{"edge":"media","node":"17841473009917118","options":{"queryParameters":{"parameter":[{"name":"caption","value":"={{ $json.Caption }}"},{"name":"image_url","value":"={{ $json.ImageURL }}"}]}},"graphApiVersion":"v22.0","httpRequestMethod":"POST"},"credentials":{"facebookGraphApi":{"id":"vF0cZWKe6sYiEaHo","name":"n8n Bot Access"}},"typeVersion":1},{"id":"c69f1856-efa2-48a5-9e41-71ab67bcaeca","name":"6b. Wait for Container Processing","type":"n8n-nodes-base.wait","position":[3480,-380],"webhookId":"1b14c8bf-151a-4054-8215-093dd5b6cbcc","parameters":{},"typeVersion":1.1},{"id":"516b61f0-825e-468b-8eb7-80f757171465","name":"6c. Publish Post to Instagram","type":"n8n-nodes-base.facebookGraphApi","position":[3780,-380],"parameters":{"edge":"media_publish","node":"17841473009917118","options":{"queryParameters":{"parameter":[{"name":"creation_id","value":"={{ $json.id }}"}]}},"graphApiVersion":"v22.0","httpRequestMethod":"POST"},"credentials":{"facebookGraphApi":{"id":"vF0cZWKe6sYiEaHo","name":"n8n Bot Access"}},"typeVersion":1},{"id":"1a94c8ba-7b59-4337-ae07-5c0b2b8349dd","name":"7. Update Post Status in Sheet","type":"n8n-nodes-base.googleSheets","position":[4440,-380],"parameters":{"columns":{"value":{"Topic":"={{ $('1. Get Next Post Idea from Sheet').item.json.Topic }}","Status":"1"},"schema":[{"id":"Topic","type":"string","display":true,"removed":false,"required":false,"displayName":"Topic","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Audience","type":"string","display":true,"removed":true,"required":false,"displayName":"Audience","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Voice","type":"string","display":true,"removed":true,"required":false,"displayName":"Voice","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Platform","type":"string","display":true,"removed":true,"required":false,"displayName":"Platform","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Status","type":"string","display":true,"required":false,"displayName":"Status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["Topic"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":1510137257,"cachedResultUrl":"https://docs.google.com/spreadsheets/d/1hG2NMi-4fMa7D5qGonCN8bsYVya4L2TOB_8mI4XK-9k/edit#gid=1510137257","cachedResultName":"Postİ"},"documentId":{"__rl":true,"mode":"list","value":"1hG2NMi-4fMa7D5qGonCN8bsYVya4L2TOB_8mI4XK-9k","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1hG2NMi-4fMa7D5qGonCN8bsYVya4L2TOB_8mI4XK-9k/edit?usp=drivesdk","cachedResultName":"Medium Post Automation"}},"credentials":{"googleSheetsOAuth2Api":{"id":"VV5AyFvgYkc4TfC7","name":"Onur Drive "}},"typeVersion":4.5},{"id":"68651653-07fb-435a-a82d-b8ee911b4161","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-240,-820],"parameters":{"width":460,"height":740,"content":"# 01. Content Concept Generation\n\n**Purpose:** This step uses Google Gemini to generate **one unique content concept** tailored for the specified platform (Instagram/LinkedIn), based on the input topic, audience, and brand voice. The format is fixed to \"Single Image\".\n\n**Input (from Node '2. Prepare Input Variables'):**\n*   `Topic` (string)\n*   `TargetAudience` (string)\n*   `BrandVoice` (string)\n*   `Platform` (string: 'Instagram' )\n\n**Output (JSON):**\n*   `{\"ideas\": [{\"concept\": \"Generated concept text...\", \"suggested_format\": \"Single Image\"}]}`"},"typeVersion":1},{"id":"c463e2b6-6b2b-498d-8672-a0a9b15c06b8","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[2220,-820],"parameters":{"color":4,"width":380,"height":740,"content":"# 03b. Image Generation\n\n**Purpose:** Generates the actual image using the **first detailed prompt** created in step 3b. It sends this prompt to the Replicate API, specifically using the 'Flux 1.1 Pro Ultra' model.\n\n**Input (from Node '3b. Generate Image Prompt Options'):**\n*   `prompt` (string: The *first* prompt string from `prompt_options[0].prompts[0]`)\n\n**Output (from Replicate API):**\n*   JSON containing the `output` URL of the generated image (e.g., `{\"output\": \"https://replicate.delivery/...\"}`)"},"typeVersion":1},{"id":"4636ef23-11e1-45dd-94c1-5774ad612811","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[400,-820],"parameters":{"color":2,"width":740,"height":740,"content":"# 02. Image Prompt Elaboration & Options\n\n**Purpose:** Takes the generated content concept and expands on it to create **two distinct, detailed image generation prompts**. These prompts are optimized for the target platform and suitable for AI image generators like Replicate Flux.\n\n**Input (from Nodes '2. Prepare Input Variables' & '3a. Generate Content Concept'):**\n*   `ChosenIdea` (string: Concept from step 3a)\n*   `OriginalTopic` (string)\n*   `TargetAudience` (string)\n*   `BrandVoice` (string)\n*   `Platform` (string: 'Instagram')\n\n**Output (JSON):**\n*   `{\"expanded_post_concept\": \"Elaborated concept description...\", \"prompt_options\": [{\"option_description\": \"Option 1: ...\", \"prompts\": [\"Detailed prompt 1...\"]}, {\"option_description\": \"Option 2: ...\", \"prompts\": [\"Detailed prompt 2...\"]}]}`\n"},"typeVersion":1},{"id":"1996f6d0-42bc-4b23-a1af-59c4bb1cb3ee","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[1380,-820],"parameters":{"color":3,"width":620,"height":740,"content":"# 03a. Caption Generation\n\n**Purpose:** Uses Google Gemini to write a short, engaging social media caption **specifically tailored for the target platform**. The caption complements the image (represented by the first generated prompt) and aligns with the overall content strategy. Includes relevant hashtags.\n\n**Input (from Nodes '1. Get Next Post Idea', '3a. Generate Content Concept', '3b. Generate Image Prompt Options'):**\n*   `ImagePrompt` (string: The *first* prompt from step 3b)\n*   `ChosenIdea` (string: Concept from step 3a)\n*   `OriginalTopic` (string)\n*   `TargetAudience` (string)\n*   `BrandVoice` (string)\n*   `Platform` (string: 'Instagram' or 'LinkedIn')\n\n**Output (JSON):**\n*   `{\"Caption\": \"Generated caption text with #hashtags...\"}`"},"typeVersion":1},{"id":"25e2ae24-c0c5-46fd-8c81-39a9fcafe472","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[2800,-820],"parameters":{"color":5,"width":1160,"height":740,"content":"# 04. Instagram Publishing\n\n**Purpose:** This block takes the final image URL and caption, prepares them for the Instagram Graph API, uploads the media to create a container, waits for Instagram to process it, and finally publishes the container as a post to the connected Instagram account.\n\n**Input (from Nodes '3c. Generate Post Caption (Gemini)' & '4. Generate Image using Prompt 1 (Replicate Flux)'):**\n*   `ImageURL` (string: URL of the generated image from Replicate)\n*   `Caption` (string: Generated post text with hashtags from Gemini)\n\n**Process:**\n1.  **Format Data (`5. Prepare Data...`):** Organizes the ImageURL and Caption into the required structure.\n2.  **Create Media Container (`6a. Create...`):** Sends the `image_url` and `caption` to the Instagram Graph API (`media` edge) to initiate the upload. Receives a container `id`.\n3.  **Wait for Processing (`6b. Wait...`):** Pauses the workflow to allow Instagram's servers time to process the uploaded media. *Note: Wait time might need adjustment depending on media size and API responsiveness.*\n4.  **Publish Media (`6c. Publish...`):** Sends the container `id` (as `creation_id`) to the Instagram Graph API (`media_publish` edge) to make the post live.\n\n**Output:** The content is published as a new post on the target Instagram profile. The final node (`6c. Publish Post...`) returns the `id` of the successfully published media object on Instagram."},"typeVersion":1},{"id":"d7082400-def2-40eb-9dbc-3e15d24c8f8f","name":"Sticky Note5","type":"n8n-nodes-base.stickyNote","position":[4640,-480],"parameters":{"color":6,"width":380,"height":300,"content":"# 05. Finalize: Update Sheet Status\n\n**Purpose:** Marks the processed post idea as completed in the Google Sheet.\n\n**Action:** Finds the corresponding row in the sheet (using the 'Topic' to match) and updates its 'Status' column to '1'. This prevents the same idea from being processed again by the workflow in future runs."},"typeVersion":1},{"id":"4e5433ff-5d04-424a-8165-6fd85cca1ad0","name":"Sticky Note6","type":"n8n-nodes-base.stickyNote","position":[-100,-300],"parameters":{},"typeVersion":1},{"id":"71bf2b4d-eaa1-423b-b798-8c0f737f4371","name":"Sticky Note7","type":"n8n-nodes-base.stickyNote","position":[-1760,-440],"parameters":{"width":420,"height":240,"content":"# 00. Scheduled Start & Input Preparation\n\n**Purpose:** Initiates the workflow automatically based on the user-defined schedule. Fetches the next available post idea (Status=0) from Google Sheets and prepares the necessary input variables (`Topic`, `Audience`, `Voice`, `Platform`) for the content generation steps."},"typeVersion":1}],"pinData":{"3c. Generate Post Caption (Gemini)":[{"output":{"Caption":"The future of call centers is here, and it's all about human + AI collaboration ✨. We believe AI should enhance, not replace, the human touch, leading to better customer experiences and happier agents. Ready to see how AI can transform your customer interactions? 🤔 \n\n#AIinCX #CallCenterAI #CustomerExperience #ArtificialIntelligence #TechInnovation #FutureofWork #HumanAI"}}],"3a. Generate Content Concept (Gemini)":[{"output":{"ideas":[{"concept":"Visually striking image of a human hand interacting with a glowing AI interface. Caption highlights how AI enhances, not replaces, human connection in call centers, leading to better customer experiences. Focus on the seamless collaboration between humans and AI.","suggested_format":"Single Image"}]}}],"3b. Generate Image Prompt Options (Gemini)":[{"output":{"prompt_options":[{"prompts":["Close-up shot of a diverse human hand gently touching a shimmering, holographic AI interface that displays abstract data visualizations. The background is blurred, showing a modern, bright call center environment. Soft, diffused lighting with hints of neon blue and teal. Ethereal, optimistic mood. Style: Digital art, futuristic, ethereal. Color palette: Pastel blues, teals, and whites with subtle glowing accents. Composition: Focus on the hand and interface, using shallow depth of field."],"option_description":"Option 1: Ethereal and futuristic. Focus on soft, diffused lighting and glowing elements to create a dreamlike, optimistic atmosphere."},{"prompts":["A slightly wider shot showcasing a human hand confidently interacting with a vibrant, glowing AI interface displaying customer profiles and real-time data. The background shows a bustling call center with diverse agents working collaboratively. Dramatic, focused lighting with strong shadows and vibrant color accents. Energetic, innovative mood. Style: High-resolution photograph, modern, bold. Color palette: Deep blues, purples, and oranges with bright yellow accents on the interface. Composition: Dynamic angle, leading lines towards the interface and agents in the background, creating depth."],"option_description":"Option 2: Bold and dynamic. Focus on contrast and vibrant colors to create a visually striking and energetic image."}],"expanded_post_concept":"Single Image post for Instagram showcasing a human hand interacting with a glowing AI interface. The image should convey a sense of collaboration and enhancement, not replacement, emphasizing the positive impact of AI on human connection in call centers. Given Instagram's visual nature, the image should be aesthetically pleasing, modern, and slightly stylized to attract attention and reflect an innovative brand voice."}}],"4. Generate Image using Prompt 1 (Replicate Flux)":[{"id":"be6ctjws49rma0cpjcy8ej169r","logs":"","urls":{"get":"https://api.replicate.com/v1/predictions/be6ctjws49rma0cpjcy8ej169r","cancel":"https://api.replicate.com/v1/predictions/be6ctjws49rma0cpjcy8ej169r/cancel","stream":"https://stream.replicate.com/v1/files/bcwr-kvewu3jt6ec4id3svfazffgcxpyifayvqjhxloix3oncveoazwya"},"error":null,"input":{"raw":false,"prompt":"Close-up shot of a diverse human hand gently touching a shimmering, holographic AI interface that displays abstract data visualizations. The background is blurred, showing a modern, bright call center environment. Soft, diffused lighting with hints of neon blue and teal. Ethereal, optimistic mood. Style: Digital art, futuristic, ethereal. Color palette: Pastel blues, teals, and whites with subtle glowing accents. Composition: Focus on the hand and interface, using shallow depth of field.","aspect_ratio":"1:1","output_format":"jpg","safety_tolerance":6},"model":"black-forest-labs/flux-1.1-pro-ultra","output":"https://replicate.delivery/xezq/LJHVcPdaiexmPyfQnkDBi6o2HpkFWKzeoktHmS5uEvgSIgRpA/tmptt54uqf7.jpg","status":"processing","version":"hidden","created_at":"2025-05-02T23:02:24.29Z","data_removed":false}],"2. Prepare Input Variables (Topic, Audience, etc.)":[{"Topic":"Call Center Transformation with Artificial Intelligence","Platform":"Instagram","BrandVoice":"Informative, reassuring, innovative","TargetAudience":"Enterprise customers, tech enthusiasts, customer experience professionals"}]},"connections":{"(Parse Caption JSON)":{"ai_outputParser":[[{"node":"3c. Generate Post Caption (Gemini)","type":"ai_outputParser","index":0}]]},"(Parse Concept JSON)":{"ai_outputParser":[[{"node":"3a. Generate Content Concept (Gemini)","type":"ai_outputParser","index":0}]]},"(Parse Prompts JSON)":{"ai_outputParser":[[{"node":"3b. Generate Image Prompt Options (Gemini)","type":"ai_outputParser","index":0}]]},"(LLM Model for Caption)":{"ai_languageModel":[[{"node":"3c. Generate Post Caption (Gemini)","type":"ai_languageModel","index":0}]]},"(LLM Model for Concept)":{"ai_languageModel":[[{"node":"3a. Generate Content Concept (Gemini)","type":"ai_languageModel","index":0}]]},"(LLM Model for Prompts)":{"ai_languageModel":[[{"node":"3b. Generate Image Prompt Options (Gemini)","type":"ai_languageModel","index":0}]]},"6c. Publish Post to Instagram":{"main":[[{"node":"7. Update Post Status in Sheet","type":"main","index":0}]]},"1. Get Next Post Idea from Sheet":{"main":[[{"node":"2. Prepare Input Variables (Topic, Audience, etc.)","type":"main","index":0}]]},"5. Prepare Data for Instagram API":{"main":[[{"node":"6a. Create Instagram Media Container","type":"main","index":0}]]},"6b. Wait for Container Processing":{"main":[[{"node":"6c. Publish Post to Instagram","type":"main","index":0}]]},"3c. Generate Post Caption (Gemini)":{"main":[[{"node":"4. Generate Image using Prompt 1 (Replicate Flux)","type":"main","index":0}]]},"6a. Create Instagram Media Container":{"main":[[{"node":"6b. Wait for Container Processing","type":"main","index":0}]]},"Scheduled Start: Check for New Posts":{"main":[[{"node":"1. Get Next Post Idea from Sheet","type":"main","index":0}]]},"3a. Generate Content Concept (Gemini)":{"main":[[{"node":"3b. Generate Image Prompt Options (Gemini)","type":"main","index":0}]]},"3b. Generate Image Prompt Options (Gemini)":{"main":[[{"node":"3c. Generate Post Caption (Gemini)","type":"main","index":0}]]},"4. Generate Image using Prompt 1 (Replicate Flux)":{"main":[[{"node":"5. Prepare Data for Instagram API","type":"main","index":0}]]},"2. Prepare Input Variables (Topic, Audience, etc.)":{"main":[[{"node":"3a. Generate Content Concept (Gemini)","type":"main","index":0}]]}}}


name: AI Customer Support Assistant · WhatsApp Ready · Works for Any Business
author_name: Matt F.
author_username: lemolex
categories: []
created_at: 2025-05-04T13:37:44.027Z
updated_at: 2025-05-04T13:37:44.027Z
url: https://n8n.io/workflows/3859
scraped_at: 2025-06-19 22:07:56
description:
  AI Customer-Support Assistant that auto-maps any business site, answers WhatsApp in real time, and lets you earn or save thousands by replacing pricey SaaS chat tools.

  ⚡ What the workflow does

  Live “AI employee”* - the bot crawls pages on demand (products, policies, FAQs) so you *never** upload documents or fine-tune a model.
  No-code setup** - Drop in API keys, paste your domain, publish the webhook—ready in \~15 min.
  Chat memory** - each conversation turn is written to Supabase/Postgres and automatically replayed into the next prompt, letting the assistant remember context so follow-up questions feel natural and coherent even across long sessions.
  WhatsApp ready** - Free-form replies inside the 24-hour service window, automatically switches to a template when required (recommended by Meta).

  🚀 Why you’ll love it

  | Benefit                   | Impact                                                                |
  | ------------------------- | --------------------------------------------------------------------- |
  | Zero content training | Point the AI Agent at any domain → go live.                            |
  | Save or earn money    | Replace pricey SaaS chat tools or sell white-label bots to clients. |
  | Channel-agnostic      | Ships with WhatsApp; swap one node for Telegram, Slack, or web chat.  |
  | Flexible voice        | Adjust tone & language by editing one prompt line.                    |

  🧰 Prerequisites (all free-tier friendly)

  OpenAI key
  Meta WhatsApp Cloud API number + permanent token (easy setup)
  Supabase (or Postgres) URL for chat memory (easy setup)

  🛠 5-step setup

  Import the template into n8n.
  Add credentials for OpenAI, WhatsApp, and Supabase.
  Enter your root domain in the root\_url variable.
  Point Meta’s Webhook to the n8n URL.
  Hit Execute Trigger and send “Hi” from WhatsApp—watch the bot answer with live data.

  🔄 Easy to extend

  Voice & language** – change wording in the System Prompt.
  Escalation** – add an “If fallback” branch → Zendesk, email, live agents.
  Extra channels** – duplicate the reply node for Telegram or Slack.
  Commerce API hooks** – plug in Shopify, Woo, Stripe for order status or payments.

  💡 Monetization ideas

  Replace costly SaaS seats.* Deploy the bot on your own server and *stop paying \$300–\$500 every month for third-party “AI support” platforms.
  Sell it as a service.* Spin up a branded instance for local shops, clinics, or e-commerce stores and *charge each client \$300–\$500 per month**—setup time is under 15 minutes.
  Upsell premium coverage (24/7 human hand-off) once the bot handles routine questions.
  Embed product links in answers and earn affiliate or upsell revenue automatically.

  Spin it up, connect a domain and a phone number, and you—or your customers—get enterprise-grade support without code, training, or ongoing licence fees.

nodes_used:
  ["n8n-nodes-base.whatsApp","n8n-nodes-base.code","@n8n/n8n-nodes-langchain.agent","@n8n/n8n-nodes-langchain.lmChatOpenAi","@n8n/n8n-nodes-langchain.memoryPostgresChat","@n8n/n8n-nodes-langchain.toolHttpRequest"]

workflow_json:
  {"id":"J2D0BssoDmn4BC6D","meta":{"instanceId":"143d2ab55c8bffb06f8b9c7ad30335764fdc48bbbacecbe2218dadb998a32213","templateCredsSetupCompleted":true},"name":"AI Customer-Support Assistant · WhatsApp Ready · Works for Any Business","tags":[],"nodes":[{"id":"fe395033-e36e-42d4-a0ce-8362b172be31","name":"AI Agent","type":"@n8n/n8n-nodes-langchain.agent","maxTries":5,"position":[120,140],"parameters":{"text":"={{ $json.messages[0].text.body }}","options":{"maxIterations":10,"systemMessage":"=You are [Company Name]’s real-time website assistant for https://www.your-company-url.com.\n\nAVAILABLE TOOLS\n• list_links(url) → { urls:[ … ] }  — returns up to 100 internal links from that page  \n• get_page(url)   → { text:\"…\" }    — returns the visible, tag-free text of the page (JavaScript rendered if needed)\n\nSEARCH STRATEGY\n1. Start with list_links on the root page.  \n2. Pick ≤ 5 links whose URL or anchor text best match the user’s question (producto, pago, envío, servicio, política, etc.).  \n3. For each chosen link call get_page once.  \n4. Read the returned text and look for the answer.  \n5. If the answer is still unknown, you may repeat steps 1-4 one level deeper.  \n6. Stop after two list_links rounds **or** eight get_page calls (whichever comes first).\n\nANSWER RULES\n• Reply in clear and friendly toon **as part of [Company Name]** (use “we”, “our”).  \n• Keep answers concise but complete.  \n• **No Markdown ni símbolos de formato. Nunca uses \\*, **, \\_, \\~, ni [texto](url).**  \n  Write urls like: Descriptive Text ␣URL   Ej.: Combos https://…  \n• Quote the exact wording for facts such as stock status, prices, envíos, métodos de pago, garantías o políticas.  \n• If the information is not on the site, reply exactly:  \n  “I can't find that information on our site right now. Do you want me to put you through to a human agent?”  \n• Stay on-domain; ignore mailto:, tel:, javascript:, or off-site links.\n• Finally, if any of the tools returns a status code 404, then reply:\n\"Non-subscribed user.\"","returnIntermediateSteps":true},"promptType":"define","hasOutputParser":true},"retryOnFail":false,"typeVersion":1.7,"alwaysOutputData":true,"waitBetweenTries":null},{"id":"3953a213-6140-4603-a069-93718e4d8982","name":"list_links","type":"@n8n/n8n-nodes-langchain.toolHttpRequest","position":[260,420],"parameters":{"url":"https://lemolex.app.n8n.cloud/webhook/list-links","method":"POST","sendBody":true,"parametersBody":{"values":[{"name":"url","value":"https://www.your-company-url.com","valueProvider":"fieldValue"},{"name":"auth-token","value":"your-auth-token (read setup guide)","valueProvider":"fieldValue"}]},"toolDescription":"Returns up to 100 unique, fully-qualified INTERNAL links for a given page.\n\nInput  (JSON body the model must supply)\n  {\n    \"url\": \"<absolute https://…>\"\n  }\n\nBehaviour\n  • Crawls only the domain of the input URL.\n  • Converts relative <a href> values to absolute URLs.\n  • Drops empty roots (\"/\"), mailto:, tel:, javascript:, and off-site links.\n  • De-duplicates the list.\n  • Responds with a JSON object:\n\n      {\n        \"urls\": [ \"<link-1>\", \"<link-2>\", … ]\n      }\n\nUse this tool when you need a navigation map of the current page.\nPass one of the returned URLs back into other tools (e.g. get_text) to read its content.\n"},"typeVersion":1.1},{"id":"21ceaf5e-d2d4-47c3-98cb-ee7c0ab0fcab","name":"OpenAI Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","position":[40,340],"parameters":{"model":{"__rl":true,"mode":"list","value":"gpt-4o-mini"},"options":{}},"credentials":{"openAiApi":{"id":"jh4eAOIykIxQWUI9","name":"OpenAi account"}},"typeVersion":1.2},{"id":"7e0e84c8-ad96-44d1-9de9-c639230418fd","name":"WhatsApp Trigger","type":"n8n-nodes-base.whatsAppTrigger","position":[-260,140],"webhookId":"857366e8-7b6f-45a7-bbd1-f876002620d7","parameters":{"updates":["messages"]},"credentials":{"whatsAppTriggerApi":{"id":"EB6eAVg9ZBZGYsyX","name":"WhatsApp OAuth account"}},"typeVersion":1},{"id":"c2a0ba34-4a23-4918-9be8-7b9d50279cde","name":"cleanAnswer","type":"n8n-nodes-base.code","position":[1040,120],"parameters":{"jsCode":"// cleanAnswer – run once per item\nlet txt = $('AI Agent').first().json.output || '';\n\n// 1. Remove bold / italic / strike markers\ntxt = txt.replace(/[*_~]+/g, '');\n\n// 2. Convert [Texto](https://url) → Texto https://url\ntxt = txt.replace(/\\[([^\\]]+)\\]\\((https?:\\/\\/[^\\s)]+)\\)/g, '$1 $2');\n\n// 3. Collapse 3+ blank lines\ntxt = txt.replace(/\\n{3,}/g, '\\n\\n').trim();\n\nreturn [{ json: { answer: txt } }];\n"},"typeVersion":2},{"id":"ef403af2-4543-4edb-80ae-afda1e98a2a9","name":"get_page","type":"@n8n/n8n-nodes-langchain.toolHttpRequest","position":[420,420],"parameters":{"url":"https://lemolex.app.n8n.cloud/webhook/get_text","method":"POST","sendBody":true,"parametersBody":{"values":[{"name":"url"},{"name":"auth-token","value":"your-auth-token (read setup guide)","valueProvider":"fieldValue"}]},"toolDescription":"Fetches the fully-rendered **plain text** of a single web.  \n• Input  : { \"url\": \"<absolute https://…>\" }  \n• Auth   : token is sent as HTTP basic-auth.  \n• Query  : url=<encoded url>  \n• Output : { \"text\": \"<visible text of the body>\", \"url\": \"<same url>\" }  \n• The \"text\" field already has **all HTML tags removed** .  \n• Use this tool whenever you need the actual words that appear on the page—product details, prices, stock lines, shipping terms, payment options, company policies, etc.  \n• Do **not** call it on off-site links or mailto:/tel:/javascript: pseudo-links.  \n"},"typeVersion":1.1},{"id":"46c1fd08-9b61-4ea9-bee3-9ad8b7e7ce4d","name":"24-hour window check","type":"n8n-nodes-base.code","position":[520,140],"parameters":{"jsCode":"// within24h?  – run once per item\n// Meta (WhatsApp) timestamp arrives as seconds since epoch\nconst lastTs = Number($('WhatsApp Trigger').first().json.messages[0].timestamp) * 1000;   // → ms\nconst withinWindow = Date.now() - lastTs < 24 * 60 * 60 * 1000;\n\nreturn [{ json: { withinWindow, answer: $json.answer, userId: $json.userId } }];"},"typeVersion":2},{"id":"0309e9fb-745e-46cd-a360-a6a4a96ffa36","name":"If","type":"n8n-nodes-base.if","position":[740,140],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"d33e218e-a49a-49ed-9c6b-55b9ea0b0dbb","operator":{"type":"boolean","operation":"true","singleValue":true},"leftValue":"={{ $json.withinWindow }}","rightValue":""}]}},"typeVersion":2.2},{"id":"e0f6e0b0-d2f8-4be5-85e4-74b351390369","name":"Send Pre-approved Template Message to Reopen the Conversation","type":"n8n-nodes-base.whatsApp","position":[1060,360],"parameters":{"template":"hello_world|en_US","phoneNumberId":"679436108574898","requestOptions":{},"recipientPhoneNumber":"={{ $('WhatsApp Trigger').item.json.contacts[0].wa_id }}"},"credentials":{"whatsAppApi":{"id":"zNN8ICsFZI5A7frT","name":"WhatsApp account"}},"typeVersion":1},{"id":"fd41fbf2-f471-4529-bb4d-358ace9cf639","name":"Send AI Agent's Answer","type":"n8n-nodes-base.whatsApp","position":[1260,120],"parameters":{"textBody":"={{ $json.answer }}","operation":"send","phoneNumberId":"679436108574898","requestOptions":{},"additionalFields":{},"recipientPhoneNumber":"={{ $('WhatsApp Trigger').item.json.contacts[0].wa_id }}"},"credentials":{"whatsAppApi":{"id":"zNN8ICsFZI5A7frT","name":"WhatsApp account"}},"typeVersion":1},{"id":"35e6c77f-56c5-4b93-a69a-e048b593cf40","name":"Postgres Users Memory","type":"@n8n/n8n-nodes-langchain.memoryPostgresChat","position":[120,500],"parameters":{"tableName":"message_history","sessionKey":"={{ $json.contacts[0].wa_id }}","sessionIdType":"customKey"},"credentials":{"postgres":{"id":"Bk7n11D1jU5zJ802","name":"Postgres account"}},"typeVersion":1.3},{"id":"67c3296e-8915-4857-a294-03c5bc8257c0","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-920,-320],"parameters":{"width":460,"height":1460,"content":"# Step by Step Setup Guide\n\n### **The technology that powers this AI Agent—continuously crawling, extracting, and generating answers—incurs real operating costs to stay online.**\n### **That’s why the workflow requires an active membership, priced at only **\\$29 per month**. Comparable AI-support platforms charge **\\$150 – \\$500 each month**, so with this template you either save a large chunk of that expense for your own business or earn the same amount by reselling the chatbot to clients—while paying just \\$29 yourself. *And because the bot pulls fresh information from the site in real time, you never have to “re-train” a model, saving you even more time and money.***\n\n **Activate your membership here:** [https://lemolex.gumroad.com/l/ejsnx](https://lemolex.gumroad.com/l/ejsnx)\n\n### Let's start setting this up step by step:\n*Total hands-on time: ≈ 15 minutes*\n\n1. Activate the tools with the membership generated key: \n- Go to the membership link to get your key.\n- Copy the key (e.g 6F0E4C97-B72A4E69-A11BF6C4-AF123456) and paste it in the body parameters of list_links and get_page (tools):\n*Name: auth-token*\n*Value:  6F0E4C97-B72A4E69-A11BF6C4-AF123456* **(example)**\n\n2. Customize for Your Company:\n- Copy the Root URL of your company's website (Home Page).\n- Open the AI Agent node and inside the `System Message`, change the following values:\n[Company Name] with your company name (e.g [Company Name] -> Facebook)\n[https://www.your-company-url.com] with your company Root URL that you copied before.\nCheck for these 2 values along the entire text.\n- Go back to the tools list_links and get_page and paste the Root URL inside the body parameters, specifically:\n*Name: url*\n*Value: https://www.your-company-url.com **(e.g https://www.facebook.com)**\n\n3. Connect your credentials:\n- Go to the OpenAI Chat Model node and connect your OpenAI credentials.\n- Go to the Postgres Users Memory node and connect your Supabase credentials. A tutorial for this: https://youtu.be/6w5f_jsPYSQ?si=MPdXYUjxv3fghQPj&t=105 (Minute 1:45 to 5:00)\n- Go to the WhatsApp nodes \"WhatsApp Trigger\", \"Send Pre-approved Template Message to Reopen the Conversation\" and \"Send AI Agent's Answer\" to connect your credentials. A tutorial for this: https://youtu.be/ZrhTQle55LQ?si=MO_leooogO9KchCV\n- Go to the \"Send Pre-approved Template Message to Reopen the Conversation\" and select the template message under the \"Template\" parameter.\n***If you don't want to use this feature (not recommended) delete the nodes \"24-hour window check\", \"If\" and \"Send Pre-approved Template Message to Reopen the Conversation\". Then connect the AI Agent node to the \"cleanAnswer\" node.***\n\n\n### **You are ready**"},"typeVersion":1}],"active":false,"pinData":{},"settings":{"executionOrder":"v1"},"versionId":"245c3695-7177-4a1d-a33d-7aedd0eccc44","connections":{"If":{"main":[[{"node":"cleanAnswer","type":"main","index":0}],[{"node":"Send Pre-approved Template Message to Reopen the Conversation","type":"main","index":0}]]},"AI Agent":{"main":[[{"node":"24-hour window check","type":"main","index":0}]]},"get_page":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]},"list_links":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]},"cleanAnswer":{"main":[[{"node":"Send AI Agent's Answer","type":"main","index":0}]]},"WhatsApp Trigger":{"main":[[{"node":"AI Agent","type":"main","index":0}]]},"OpenAI Chat Model":{"ai_languageModel":[[{"node":"AI Agent","type":"ai_languageModel","index":0}]]},"24-hour window check":{"main":[[{"node":"If","type":"main","index":0}]]},"Postgres Users Memory":{"ai_memory":[[{"node":"AI Agent","type":"ai_memory","index":0}]]}}}


name: Automated YouTube Video Scheduling & AI Metadata Generation 🎬
author_name: JPres
author_username: stardawnai
categories: []
created_at: 2025-05-06T14:51:43.081Z
updated_at: 2025-05-06T14:51:43.081Z
url: https://n8n.io/workflows/3900
scraped_at: 2025-06-19 22:07:56
description:
  👥 Who Is This For?
  Content creators, marketing teams, and channel managers who need to streamline video publishing with optimized metadata and scheduled releases across multiple videos.

  🛠 What Problem Does This Solve?
  Manual YouTube video publishing is time-consuming and often results in inconsistent descriptions, tags, and scheduling. This workflow fully automates:
  Extracting video transcripts via Apify for metadata generation
  Creating SEO-optimized descriptions and tags for each video
  Setting videos to private during initial upload (critical for scheduling)
  Implementing scheduled publishing at strategic times
  Maintaining consistent branding and formatting across all content

  🔄 Node-by-Node Breakdown
  | Step | Node Purpose |
  |------|--------------|
  | 1 | Every Day (Scheduler) | Trigger workflow on a regular schedule |
  | 2 | Get Videos to Harmonize | Retrieve videos requiring metadata enhancement |
  | 3 | Get Video IDs (Unpublished) | Filter for videos that need publishing |
  | 4 | Loop over Video IDs | Process each video individually |
  | 5 | Get Video Data | Retrieve metadata for the current video |
  | 6 | Loop over Videos with Parameter IS | Set parameters for processing |
  | 7 | Set Videos to Private | Ensure videos are private (required for scheduling) |
  | 8 | Apify: Get Transcript | Extract video transcript via Apify |
  | 9 | Fetch Latest Videos | Get most recent channel content |
  | 10 | Loop Over Items | Process each video item |
  | 11 | Generate Description, Tags, etc. | Create optimized metadata from transcript |
  | 12 | AP Clean ID | Format identifiers |
  | 13 | Retrieve Generated Data | Collect the enhanced metadata |
  | 14 | Adjust Transcript Format | Format transcript for better processing |
  | 15 | Update Video's Metadata | Apply generated description and tags to video |

  ⚙️ Pre-conditions / Requirements
  n8n with YouTube API credentials configured
  Apify account with API access for transcript extraction
  YouTube channel with upload permissions
  Master templates for description formatting
  Videos must be initially set to private for scheduling to work

  ⚙️ Setup Instructions
  Import this workflow into your n8n instance.
  Configure YouTube API credentials with proper channel access.
  Set up Apify integration with appropriate actor for transcript extraction.
  Define scheduling parameters in the Every Day node.
  Configure description templates with placeholders for dynamic content.
  Set default tags and customize tag generation rules.
  Test with a single video before batch processing.

  🎨 How to Customize
  Adjust prompt templates for description generation to match your brand voice.
  Modify tag selection algorithms based on your channel's SEO strategy.
  Create multiple publishing schedules for different content categories.
  Integrate with analytics tools to optimize publishing times.
  Add notification nodes to alert when videos are successfully scheduled.

  ⚠️ Important Notes
  Videos MUST be uploaded as private initially - the Publish At logic only works for private videos that haven't been published before.
  Publishing schedules require videos to remain private until their scheduled time.
  Transcript quality affects metadata generation results.
  Consider YouTube API quotas when scheduling large batches of videos.

  🔐 Security and Privacy
  API credentials are stored securely within n8n.
  Transcripts are processed temporarily and not stored permanently.
  Webhook URLs should be protected to prevent unauthorized triggering.
  Access to the workflow should be limited to authorized team members only.

nodes_used:
  ["n8n-nodes-base.httpRequest","n8n-nodes-base.youTube","n8n-nodes-base.code","@n8n/n8n-nodes-langchain.agent","@n8n/n8n-nodes-langchain.openAi","@n8n/n8n-nodes-langchain.lmChatGoogleGemini"]

workflow_json:
  {"id":"wLbJ7rE6vQzizCp2","meta":{"instanceId":"5ce52989094be90be3b3bdd9ed9cee1d7ce1fcecaa598afaec4a50646d32e291","templateCredsSetupCompleted":true},"name":"Youtube_Automation","tags":[{"id":"5eZb3e5PJspoJjVN","name":"Privat","createdAt":"2025-02-22T09:31:58.972Z","updatedAt":"2025-02-22T09:31:58.972Z"},{"id":"fSDcaaN3w5sV5e3S","name":"Templates","createdAt":"2025-02-23T15:20:47.262Z","updatedAt":"2025-02-23T15:20:47.262Z"}],"nodes":[{"id":"2001b9ca-f76b-437e-90a9-16c0d17accef","name":"Fetch Latest Videos","type":"n8n-nodes-base.youTube","position":[-60,40],"parameters":{"limit":1,"filters":{},"options":{"order":"date"},"resource":"video"},"credentials":{"youTubeOAuth2Api":{"id":"cpgVAMXp8iMLXwKW","name":"Private Pres"}},"typeVersion":1},{"id":"e8857adf-63ec-4612-aa49-cd77130a6728","name":"Loop Over Items","type":"n8n-nodes-base.splitInBatches","position":[160,40],"parameters":{"options":{}},"typeVersion":3},{"id":"1a8cf640-caf4-4163-a658-400714702314","name":"YT Title","type":"@n8n/n8n-nodes-langchain.openAi","disabled":true,"position":[2220,120],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-4.1-nano","cachedResultName":"GPT-4.1-NANO"},"options":{},"messages":{"values":[{"role":"system","content":"Du bist ein professioneller Texter für SEO-optimierte YouTube-Titel."},{"content":"=Schreib mir einen passenden SEO Youtube Titel für das Transkript folgendes Videotranskriptes. Gib mir nur den Titel sonst nichts. Maximal 100 Character also halte dich kurz.\n\n{{ $('Adjust Transcript Format').item.json.transcript }}"}]}},"credentials":{"openAiApi":{"id":"ftBgqCi1fD1fFEZq","name":"Midgard#1"}},"typeVersion":1.7},{"id":"ec6e5d83-d8c8-417e-8df0-86634feef3e6","name":"Create Description","type":"@n8n/n8n-nodes-langchain.openAi","position":[1920,80],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-4.1-nano","cachedResultName":"GPT-4.1-NANO"},"options":{},"messages":{"values":[{"role":"system","content":"Du bist ein professioneller Texteschreiber.\nDu erhältst das Transkript eines wirtschaftsbezogenen Videos und erstellst eine ausführlichere aber auch nicht zu lange  Zusammenfassung (mit Absätzen) darüber, worum es geht.\n\nSchreibe eine ausführlichere Zusammenfassung (mit Absätzen) über den Inhalt des Podcasts. \n\nDein Output wird für die Youtube Video Beschreibung verwendet. Also starte mit sowas wie: \"In diesem Video...\" oder \"In dieser Folge...\". \nSchreibe aus meiner Perspektive also Sachen wie \"meine Meinung\" oder \"meiner Ansicht nach\"...  aus der Ich- Perspektive aber niemals sowas wie \"In dieser Folge lerne ich...\" oder so ähnlich, denn ich erkläre stets den Inhalt bzw. diskutiere darüber. DU SCHREIBST NIEMALS SOWAS WIE \"DER SPRECHER SAGT\"!!! Immer aus meiner Position heraus.\n\nWichtig: Verwende klare und dominante Aussagen, wie sie im Transkript formuliert sind. Vermeide neutrale oder unsichere Formulierungen wie \"es könnte\", \"ich vermute, dass\", \"möglicherweise\" oder ähnliche Phrasen. Die Aussagen sollen selbstbewusst und eindeutig sein, um die Inhalte des Podcasts kraftvoll zu vermitteln.\nFüge einige wenige (2-4) Emojis an wo es sich anbietet. \n   \nEnde den Post mit 2-5 passenden Hashtags. Die Hashtags sollten grob sein also sowas wie #wirtschaft #geld #gold oder so ähnlich - je nachdem was passt.\n"},{"content":"=Hier ist das Transkript: \n\n{{ $json.transcript }}"}]}},"credentials":{"openAiApi":{"id":"ftBgqCi1fD1fFEZq","name":"Midgard#1"}},"typeVersion":1.7},{"id":"f59d950b-4e29-4a41-8756-85ea7814b3d3","name":"When clicking ‘Test workflow’","type":"n8n-nodes-base.manualTrigger","position":[-2260,300],"parameters":{},"typeVersion":1},{"id":"27ef7d44-7cca-417f-8177-b5b896a79aa0","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[2660,-20],"parameters":{"color":3,"width":220,"content":"### 🎥Title is kept from the upload, alternatively you can just add the YT Title module in the mix \n# 👇🏻\n"},"typeVersion":1},{"id":"8d763312-35f0-4d53-a210-1e0e22a06323","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[1920,-140],"parameters":{"color":3,"height":200,"content":"# Adjust the Prompts 👉🏻\n\n# 👇🏻"},"typeVersion":1},{"id":"9e7f22d5-7776-4bc4-a274-963cb8c8810c","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[-540,-560],"parameters":{"color":5,"width":620,"height":420,"content":"# Youtube Video Description/Tags/etc. Automation\n\n👉🏻 **Repos**: [github.com/JimPresting](https://github.com/JimPresting) 🛠️  \n👉🏻 **YouTube**: [youtube.com/@StardawnAI](https://www.youtube.com/@StardawnAI) 🎥  \n\nStay up to date for guides on Github repos and tutorials on YouTube! 🚀\n\n\n**Note:** By default, this takes only the latest video and adjusts the values. If you upload multiple videos within a day or even at once within one hour, you need to set that value higher, but bear in mind that if you set it to a high number, it will process older, already published videos. Using the *Publish After* option can't be recommended as it might lead to errors with scheduled videos.\nYou can also detach the *Remove Duplicates* node from the ongoing nodes and set the limit of the *Get All Videos* node to *Return all*. This way, everything that has already been uploaded will not be returned in the future. To undo this, you can select *Clear Database* in the *Remove Duplicates* node.\n"},"typeVersion":1},{"id":"4c5667cf-f8a9-45ab-876b-3a6b5730484c","name":"2.5FlashPrev","type":"@n8n/n8n-nodes-langchain.lmChatGoogleGemini","position":[2220,0],"parameters":{"options":{},"modelName":"models/gemini-2.5-flash-preview-04-17"},"credentials":{"googlePalmApi":{"id":"clmB8ZYJMHaHmnsu","name":"Stardawn#1"}},"typeVersion":1},{"id":"7c73cc89-ea8e-42b3-a1c9-2dc493026216","name":"YT Tags","type":"@n8n/n8n-nodes-langchain.agent","position":[2220,-120],"parameters":{"text":"=Nun folgt das eigentliche Thema/Transkript. Gib mir die Youtube Tags dafür:\n\n{{ $('Adjust Transcript Format').item.json.transcript }}","options":{"systemMessage":"You will get the transcript of a Youtube video for which you should generate matching tags (YOU NEED TO separate it by comma).\n\nBased on the topic/transcript of the video generate YouTube tags. These tags should be very general about the topics. Give multiple matching YouTube Tags that improve SEO for the video. \n\nExample:\nif the video is about why gold is a good investment you will for example not use gold investments as a tag but rather just gold     \n\nThe tags (if appropriate) should be in German as the channel content is in German.\n\nReturn just the tags one word by one separated via Comma. \n\n\nDieses Video handelt vom zukünftigen Goldpreis und davon, wie er die Renditen von performanten Vermögenswerten wie Aktien und Anleihen in ihrer angepassten Rendite beeinflusst.\n\nErwartetet output:\nGoldpreis, zukünftiger Goldpreis, Goldinvestitionen, Vermögensrenditen, Aktien und Anleihen, Investitionsrenditen, angepasste Rendite, Goldmarkt, Finanzmärkte, Goldpreisprognose, Wirtschaftstrends, Investieren in Gold, Aktienmarktanalyse, Anleihenmarkt, Anlagestrategien, Inflation und Gold, Gold vs. Aktien, Finanzanalyse, Edelmetalle, Portfoliomanagement, Marktausblick, Investmenttipps\n "},"promptType":"define"},"typeVersion":1.9},{"id":"e8782ac7-ca31-4a5f-a9f1-62548f56407d","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[-2340,-140],"parameters":{"color":4,"width":2000,"height":660,"content":"# 📅Scheduling Logic⏰\n\n"},"typeVersion":1},{"id":"33d289eb-c989-4c8d-b387-405f31ba11d6","name":"3s","type":"n8n-nodes-base.wait","position":[2920,140],"webhookId":"1e75fe1f-e553-4530-a8bc-5e64208a1184","parameters":{"amount":3},"typeVersion":1.1},{"id":"19337563-a349-485d-a064-32f58c8fde90","name":"gettitle","type":"n8n-nodes-base.youTube","position":[-780,200],"parameters":{"options":{},"videoId":"={{ $json.videoId }}","resource":"video","operation":"get"},"credentials":{"youTubeOAuth2Api":{"id":"cpgVAMXp8iMLXwKW","name":"Private Pres"}},"typeVersion":1},{"id":"da93ff18-8d19-45ab-b268-dbbebcb86719","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-1040,-140],"parameters":{"color":5,"width":180,"content":"## Code only returns the videos that are not listed"},"typeVersion":1},{"id":"71e9606c-4b6e-4b49-b3ec-bd9bd261f7a9","name":"Sticky Note5","type":"n8n-nodes-base.stickyNote","position":[-600,-80],"parameters":{"color":3,"width":220,"height":260,"content":"## Video needs to be set to private TOGETHER with the PublishAt parameter in order for it to work."},"typeVersion":1},{"id":"c5a240aa-b705-4219-b293-da4ae0168350","name":"Sticky Note6","type":"n8n-nodes-base.stickyNote","position":[1320,-140],"parameters":{"color":3,"width":280,"height":240,"content":"### Video needs to be Unlisted or Published in order for the scraper to be able to get the transcript\n\n### ADJUST YOUR APIFY API TOKEN HERE      \n# 👇🏻"},"typeVersion":1},{"id":"5fb12ed7-8992-424d-86e0-7c8cd0f0b9d3","name":"Sticky Note7","type":"n8n-nodes-base.stickyNote","position":[-80,-140],"parameters":{"color":4,"width":3200,"height":660,"content":"# Generate Description, Tags, etc. 🖌️📝 #️⃣"},"typeVersion":1},{"id":"33b865ef-ec2e-4349-bbba-d76d41345fe3","name":"Set Publish Date","type":"n8n-nodes-base.youTube","position":[-600,280],"parameters":{"title":"={{ $json.snippet.title }}","videoId":"={{ $json.id }}","resource":"video","operation":"update","categoryId":"25","regionCode":"DE","updateFields":{"publishAt":"={{ $('Loop over All Videos not Published').item.json.publishAt }}","privacyStatus":"private"}},"credentials":{"youTubeOAuth2Api":{"id":"cpgVAMXp8iMLXwKW","name":"Private Pres"}},"typeVersion":1},{"id":"9a228886-c91a-44f7-b894-e23095166efc","name":"Every Day","type":"n8n-nodes-base.scheduleTrigger","disabled":true,"position":[-2260,60],"parameters":{"rule":{"interval":[{"triggerAtHour":14,"triggerAtMinute":22}]}},"typeVersion":1.2},{"id":"c0277051-f146-43f7-a2cd-36739d933209","name":"Get Videos to reschedule","type":"n8n-nodes-base.youTube","position":[-1880,40],"parameters":{"limit":2,"filters":{},"options":{"order":"date"},"resource":"video"},"credentials":{"youTubeOAuth2Api":{"id":"cpgVAMXp8iMLXwKW","name":"Private Pres"}},"typeVersion":1},{"id":"5cf3813b-931b-4c0a-84fe-4edd3d55a99a","name":"Get video Ids seperated","type":"n8n-nodes-base.code","position":[-1660,40],"parameters":{"jsCode":"// Extract video IDs from YouTube search results\n// This function processes all input items and creates separate items for each videoId\n\n// Initialize empty array for our result items\nconst resultItems = [];\n\n// Process each input item\nfor (const item of items) {\n  // Check if the item has a valid structure\n  if (item.json && item.json.id && item.json.id.videoId) {\n    // Create a new item for each videoId\n    resultItems.push({\n      json: {\n        videoId: item.json.id.videoId\n      }\n    });\n  }\n}\n\n// Return each videoId as a separate item that can be processed individually\nreturn resultItems;"},"typeVersion":2},{"id":"8131b388-b842-4c46-b82b-e53283d938ed","name":"Loop over Video IDs","type":"n8n-nodes-base.splitInBatches","position":[-1440,40],"parameters":{"options":{}},"typeVersion":3},{"id":"665cb406-f6b9-4ba4-82fa-daa1141eb0a3","name":"Get Video Data","type":"n8n-nodes-base.youTube","position":[-1220,60],"parameters":{"options":{},"videoId":"={{ $json.videoId }}","resource":"video","operation":"get"},"credentials":{"youTubeOAuth2Api":{"id":"cpgVAMXp8iMLXwKW","name":"Private Pres"}},"typeVersion":1},{"id":"3d366fb3-f579-46f0-9254-8d4c1612038e","name":"Return Private Videos","type":"n8n-nodes-base.code","position":[-1220,-120],"parameters":{"jsCode":"// Utility function to get next Friday at 17:00 UTC in YouTube ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ)\nfunction getNextFridayUTC(startDate, weekOffset = 0) {\n  const date = new Date(startDate); // Work with a copy\n  \n  const currentUTCDay = date.getUTCDay(); // 0 for Sunday, ..., 5 for Friday\n  const daysUntilFriday = (5 - currentUTCDay + 7) % 7; // Calculate days to next Friday\n  \n  date.setUTCDate(date.getUTCDate() + daysUntilFriday + (weekOffset * 7));\n  date.setUTCHours(17, 0, 0, 0); // Set time to 17:00:00.000 UTC\n  \n  // toISOString() returns \"YYYY-MM-DDTHH:mm:ss.sssZ\"\n  // We split at '.' to remove milliseconds and add 'Z' back for \"YYYY-MM-DDTHH:mm:ssZ\"\n  return date.toISOString().split('.')[0] + \"Z\";\n}\n\n// INPUT `items` is an array from n8n.\n// Each item.json is expected to be a YouTube video object from a previous node.\nconst videosToSchedule = items.filter(item => \n  item.json && \n  item.json.status && \n  (item.json.status.privacyStatus === \"unlisted\" || item.json.status.privacyStatus === \"private\")\n  // Adjust this filter if you only want to process \"unlisted\" or only \"private\" videos\n);\n\nif (videosToSchedule.length === 0) {\n  // console.log(\"No videos found matching the filter criteria.\");\n  return []; // Return empty array if no videos to schedule\n}\n\n// Sort videos by their original published/uploaded date (snippet.publishedAt), earliest first.\nvideosToSchedule.sort((a, b) => {\n  const dateA = new Date(a.json?.snippet?.publishedAt || '1970-01-01T00:00:00Z');\n  const dateB = new Date(b.json?.snippet?.publishedAt || '1970-01-01T00:00:00Z');\n  return dateA - dateB;\n});\n\nconst now = new Date(); // Current date to calculate future Fridays\n\n// Map the filtered and sorted videos to the desired output structure for the YouTube update node.\nconst scheduledItems = videosToSchedule.map((item, index) => {\n  const videoData = item.json; // The actual video data object\n  const scheduleDate = getNextFridayUTC(now, index); // Calculate the publishAt date\n  \n  return {\n    json: { // This is the structure the next n8n YouTube node will receive\n      videoId: videoData.id,                             // ID of the video to update\n      publishAt: scheduleDate,                           // The calculated schedule time: YYYY-MM-DDTHH:mm:ssZ\n      title: videoData.snippet?.title || \"Untitled Video\", // Keep original title or use a default\n      \n      // --- CRITICAL PARAMETERS FOR THE YOUTUBE API ---\n      privacy: \"private\", // **MUST BE 'private' FOR 'publishAt' TO WORK!**\n                          // The API requires the video to be set to private when scheduling.\n      \n      // **VERY LIKELY REQUIRED: selfDeclaredMadeForKids**\n      // You MUST tell YouTube if the video is made for kids or not.\n      // Get it from existing data if available, otherwise set a default.\n      selfDeclaredMadeForKids: videoData.status?.selfDeclaredMadeForKids === true ? true : false,\n\n      // **POSSIBLY REQUIRED: categoryId (if updating snippet like title)**\n      // categoryId: videoData.snippet?.categoryId || \"YOUR_DEFAULT_CATEGORY_ID\", \n      // e.g., \"10\" for Music, \"22\" for People & Blogs.\n      // Check YouTube API docs for category IDs.\n      \n      // (Optional) You can include other fields like description if you want to update them\n      // description: videoData.snippet?.description || \"\" \n    }\n  };\n});\n\nreturn scheduledItems; // Return the array of video objects to be processed"},"typeVersion":2,"alwaysOutputData":true},{"id":"bb3e90ae-ff3f-4c22-b920-d1a99a1f99e8","name":"4s","type":"n8n-nodes-base.wait","position":[-260,240],"webhookId":"7d5c70f8-a592-4634-8c5a-0fbd0cebf6a4","parameters":{"amount":4},"typeVersion":1.1},{"id":"f67c7668-71eb-42e6-b385-f66f9e5e80eb","name":"Loop over All Videos not Published","type":"n8n-nodes-base.splitInBatches","position":[-1020,60],"parameters":{"options":{}},"typeVersion":3},{"id":"f98b1399-7970-4585-8f2a-be897562fa40","name":"get video id","type":"n8n-nodes-base.set","position":[380,80],"parameters":{"options":{},"assignments":{"assignments":[{"id":"c2e2eecd-ca73-40c9-a364-4713030ab451","name":"id.videoId","type":"string","value":"={{ $json.id.videoId }}"}]},"includeOtherFields":true},"typeVersion":3.4},{"id":"6b907512-945b-4c1e-8a97-b14409ddfcaa","name":"Remove Duplicates from previous Runs","type":"n8n-nodes-base.removeDuplicates","position":[600,80],"parameters":{"options":{},"operation":"removeItemsSeenInPreviousExecutions","dedupeValue":"={{ $json.id.videoId }}"},"typeVersion":2,"alwaysOutputData":false},{"id":"d6c7152e-e508-43c3-8748-ba12652ac117","name":"new video?","type":"n8n-nodes-base.if","position":[820,80],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"adfea7c7-ed64-4e1e-a9c3-dc5e33aa1147","operator":{"type":"array","operation":"notExists","singleValue":true},"leftValue":"={{$('Remove Duplicates from previous Runs').all() }}","rightValue":""}]}},"typeVersion":2.2},{"id":"d1c31718-4a26-4108-a618-f67cfb87053c","name":"getLatestVideoID","type":"n8n-nodes-base.youTube","position":[1000,160],"parameters":{"options":{},"videoId":"={{ $('get video id').item.json.id.videoId }}","resource":"video","operation":"get"},"credentials":{"youTubeOAuth2Api":{"id":"cpgVAMXp8iMLXwKW","name":"Private Pres"}},"typeVersion":1,"alwaysOutputData":true},{"id":"66814975-e4a5-4c23-9bf2-c8d30d96c122","name":"Get Transcript","type":"n8n-nodes-base.httpRequest","position":[1320,120],"parameters":{"url":"=https://api.apify.com/v2/acts/pintostudio~youtube-transcript-scraper/run-sync-get-dataset-items","method":"POST","options":{},"jsonBody":"={\n  \"videoUrl\": \"https://www.youtube.com/watch?v={{ $json.id }}\"\n}","sendBody":true,"sendQuery":true,"specifyBody":"json","queryParameters":{"parameters":[{"name":"token","value":"YOURAPITOKEN"}]}},"typeVersion":4.2,"alwaysOutputData":false},{"id":"fd355571-8c74-4d31-972e-13f737aaec05","name":"Adjust Transcript Format","type":"n8n-nodes-base.code","position":[1600,120],"parameters":{"jsCode":"const items = $input.all();\n\nconst transcriptStrings = items.flatMap(item => {\n  const dataArray = item.json.data;\n\n  if (!dataArray || !Array.isArray(dataArray)) {\n    return [];\n  }\n\n  const segmentTexts = dataArray.map(segment => {\n      if (segment && typeof segment.text === 'string') {\n          return segment.text;\n      } else {\n          return '';\n      }\n  });\n\n  return segmentTexts;\n});\n\nconst transcript = transcriptStrings.join(' ');\n\nreturn [\n  {\n    json: {\n      transcript: transcript,\n    },\n  },\n];"},"typeVersion":2},{"id":"7b69339f-aa12-430e-ba21-b85a0db596b5","name":"Update Video's Metadata","type":"n8n-nodes-base.youTube","position":[2660,140],"parameters":{"title":"={{ $('Fetch Latest Videos').first().json.snippet.title }}","videoId":"={{ $('getLatestVideoID').first().json.id }}","resource":"video","operation":"update","categoryId":"25","regionCode":"DE","updateFields":{"tags":"={{ $('YT Tags').first().json.message.content }}","description":"={{ $('Create Description').first().json.message.content }}\n\nDiese textbasierte Zusammenfassung des Videos wurde automatisch mit dem KI-Modell gpt-4.1-nano erstellt.]\n"}},"credentials":{"youTubeOAuth2Api":{"id":"cpgVAMXp8iMLXwKW","name":"Private Pres"}},"typeVersion":1}],"active":true,"pinData":{},"settings":{"callerPolicy":"workflowsFromSameOwner","errorWorkflow":"RhPW7iLQQJGJZqQQ","executionOrder":"v1"},"versionId":"268a8dc5-0408-458c-9dff-d7c91b223b76","connections":{"3s":{"main":[[{"node":"Loop Over Items","type":"main","index":0}]]},"4s":{"main":[[{"node":"Fetch Latest Videos","type":"main","index":0}]]},"YT Tags":{"main":[[{"node":"Update Video's Metadata","type":"main","index":0}]]},"YT Title":{"main":[[]]},"gettitle":{"main":[[{"node":"Set Publish Date","type":"main","index":0}]]},"Every Day":{"main":[[{"node":"Get Videos to reschedule","type":"main","index":0}]]},"new video?":{"main":[[],[{"node":"getLatestVideoID","type":"main","index":0}]]},"2.5FlashPrev":{"ai_languageModel":[[{"node":"YT Tags","type":"ai_languageModel","index":0}]]},"get video id":{"main":[[{"node":"Remove Duplicates from previous Runs","type":"main","index":0}]]},"Get Transcript":{"main":[[{"node":"Adjust Transcript Format","type":"main","index":0}]]},"Get Video Data":{"main":[[{"node":"Loop over Video IDs","type":"main","index":0}]]},"Loop Over Items":{"main":[[],[{"node":"get video id","type":"main","index":0}]]},"Set Publish Date":{"main":[[{"node":"Loop over All Videos not Published","type":"main","index":0}]]},"getLatestVideoID":{"main":[[{"node":"Get Transcript","type":"main","index":0}]]},"Create Description":{"main":[[{"node":"YT Tags","type":"main","index":0},{"node":"YT Title","type":"main","index":0}]]},"Fetch Latest Videos":{"main":[[{"node":"Loop Over Items","type":"main","index":0}]]},"Loop over Video IDs":{"main":[[{"node":"Return Private Videos","type":"main","index":0}],[{"node":"Get Video Data","type":"main","index":0}]]},"Return Private Videos":{"main":[[{"node":"Loop over All Videos not Published","type":"main","index":0}]]},"Get video Ids seperated":{"main":[[{"node":"Loop over Video IDs","type":"main","index":0}]]},"Update Video's Metadata":{"main":[[{"node":"3s","type":"main","index":0}]]},"Adjust Transcript Format":{"main":[[{"node":"Create Description","type":"main","index":0}]]},"Get Videos to reschedule":{"main":[[{"node":"Get video Ids seperated","type":"main","index":0}]]},"When clicking ‘Test workflow’":{"main":[[{"node":"Get Videos to reschedule","type":"main","index":0}]]},"Loop over All Videos not Published":{"main":[[{"node":"4s","type":"main","index":0}],[{"node":"gettitle","type":"main","index":0}]]},"Remove Duplicates from previous Runs":{"main":[[{"node":"new video?","type":"main","index":0}]]}}}


name: Amazon Product Search Scraper with BrightData, GPT-4, and Google Sheets
author_name: phil
author_username: phil
categories: []
created_at: 2025-05-06T14:51:48.388Z
updated_at: 2025-05-06T14:51:48.388Z
url: https://n8n.io/workflows/3901
scraped_at: 2025-06-19 22:07:56
description:
  This workflow automates web scraping of Amazon search result pages by retrieving raw HTML, cleaning it to retain only the relevant product elements, and then using an LLM to extract structured product data (name, description, rating, reviews, and price), before saving the results back to Google Sheets.

  It integrates Google Sheets to supply and collect URLs, BrightData to fetch page HTML, a custom n8n Function node to sanitize the HTML, LangChain (OpenRouter GPT-4) to parse product details, and Google Sheets again to store the output.


  URL to scape

  .


  Result

  Who Needs Amazon Search Result Scraping?

  This scraping workflow is ideal for teams and businesses that need to monitor Amazon product listings at scale:

  E-commerce Analysts** – Track competitor pricing, ratings, and inventory trends.  
  Market Researchers** – Collect data on product popularity and reviews for market analysis.  
  Data Teams** – Automate ingestion of product metadata into BI pipelines or data lakes.  
  Affiliate Marketers** – Keep affiliate catalogs up to date with latest product details and prices.

  If you need reliable, structured data from Amazon search results delivered directly into your spreadsheets, this workflow saves you hours of manual copy-and-paste.

  Why Use This Workflow?

  End-to-End Automation** – From URL list to clean JSON output in Sheets.  
  Robust HTML Cleaning** – Strips scripts, styles, unwanted tags, and noise.  
  Accurate Structured Parsing** – Leverages GPT-4 via LangChain for reliable extraction.  
  Scalable & Repeatable** – Processes thousands of URLs in batches.

  Step-by-Step: How This Workflow Scrapes Amazon

  Get URLs from Google Sheets – Reads a list of search result URLs.  
  Loop Over Items – Iterates through each URL in controlled batches.  
  Fetch Raw HTML – Uses BrightData’s Web Unlocker proxy to retrieve the page.  
  Clean HTML – A Function node removes doctype, scripts, styles, head, comments, classes, and non-whitelisted tags, collapsing extra whitespace.  
  Extract with LLM – Passes cleaned HTML into LangChain → GPT-4 to output JSON for each product:  
     name, description, rating, reviews, price  
  Save Results – Appends the JSON fields as columns back into a “results” sheet in Google Sheets.

  Customization: Tailor to Your Needs

  Adaptable Sites** – This workflow can be adapted to any e-commerce or other website, for example Walmart or eBay. 
  Whitelist Tags** – Modify the allowedTags array in the Code node to keep additional HTML elements.  
  Schema Changes** – Update the Structured Output Parser schema to include more fields (e.g., availability, SKU).  
  Alternate Data Sink** – Instead of Sheets, route output to a database, CSV file, or webhook.

  🔑 Prerequisites

  Google Sheets Credentials** – OAuth credentials configured in n8n.  
  BrightData API token** – Stored in n8n credentials as BRIGHTDATA_TOKEN.  
  OpenRouter API Key** – Configured for the LangChain node to call GPT-4.  
  n8n Instance** – Self-hosted or cloud with sufficient quota for HTTP requests and LLM calls.

  🚀 Installation & Setup

  Configure Credentials**  
    In n8n, set up Google Sheets OAuth under “Credentials.”  
    Add BrightData token as a new HTTP Request credential.  
    Create an OpenRouter API key credential for the LangChain node.  
  Import the Workflow**  
    Copy the JSON workflow into n8n’s “Import” dialog.  
    Map your Google Sheet IDs and GIDs to the {{WEB_SHEET_ID}}, {{TRACK_SHEET_GID}}, and {{RESULTS_SHEET_GID}} placeholders.  
    Ensure the BRIGHTDATA_TOKEN credential is selected on the HTTP Request node.  
  Test & Run**  
    Add a few Amazon search URLs to your “track” sheet.  
    Execute the workflow and verify product data appears in your “results” sheet.  
    Tweak batch size or parser schema as needed.

  ⚠ Important

  API Rate Limits** – Monitor your BrightData and OpenRouter usage to avoid throttling.  
  Amazon’s Terms** – Ensure your scraping complies with Amazon’s policies and legal requirements.

  Summary

  This workflow delivers a fully automated, scalable solution to extract structured product data from Amazon search pages directly into Google Sheets—streamlining your competitive analysis and data collection. 🚀

  Phil | Inforeole 

nodes_used:
  ["n8n-nodes-base.googleSheets","n8n-nodes-base.httpRequest","n8n-nodes-base.code","@n8n/n8n-nodes-langchain.chainLlm","@n8n/n8n-nodes-langchain.outputParserStructured","@n8n/n8n-nodes-langchain.lmChatOpenRouter"]

workflow_json:
  {"meta":{"instanceId":"4a11afdb3c52fd098e3eae9fad4b39fdf1bbcde142f596adda46c795e366b326"},"nodes":[{"id":"f1b36f4b-6558-4e83-a999-e6f2d24e196c","name":"OpenRouter Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenRouter","position":[620,240],"parameters":{"model":"openai/gpt-4.1","options":{}},"typeVersion":1},{"id":"89ca0a07-286f-4e68-9e85-0327a4859cc0","name":"Structured Output Parser","type":"@n8n/n8n-nodes-langchain.outputParserStructured","position":[900,240],"parameters":{"schemaType":"manual","inputSchema":"{\n  \"type\": \"array\",\n  \"items\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"name\": { \"type\": \"string\" },\n      \"description\": { \"type\": \"string\" },\n      \"rating\": { \"type\": \"number\" },\n      \"reviews\": { \"type\": \"integer\" },\n      \"price\": { \"type\": \"string\" }\n    },\n    \"required\": [\"name\", \"description\", \"rating\", \"reviews\", \"price\"]\n  }\n}"},"typeVersion":1.2},{"id":"e4800c1d-c0d8-4093-81ec-fc19ad0034cd","name":"scrap url","type":"n8n-nodes-base.httpRequest","position":[240,60],"parameters":{"url":"https://api.brightdata.com/request","method":"POST","options":{},"sendBody":true,"sendHeaders":true,"bodyParameters":{"parameters":[{"name":"zone","value":"web_unlocker1"},{"name":"url","value":"={{ $json.url }}"},{"name":"format","value":"raw"}]},"headerParameters":{"parameters":[{"name":"Authorization","value":"{{BRIGHTDATA_TOKEN}}"}]}},"typeVersion":4.2},{"id":"1a1f768f-615d-4035-81b0-63b860f8e6ac","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[160,-140],"parameters":{"content":"## Web Scraper API\n\n[Inscription - Free Trial](https://get.brightdata.com/website-scraper)"},"typeVersion":1},{"id":"2f260d96-4fff-4a4f-af29-1e43f465d54c","name":"When clicking ‘Test workflow’","type":"n8n-nodes-base.manualTrigger","position":[-440,200],"parameters":{},"typeVersion":1},{"id":"4be9033f-0b9f-466d-916e-88fbb2a80417","name":"url","type":"n8n-nodes-base.splitInBatches","position":[20,200],"parameters":{"options":{}},"typeVersion":3},{"id":"21b6d21c-b977-4175-9068-e0e2e19fa472","name":"get urls to scrape","type":"n8n-nodes-base.googleSheets","position":[-200,200],"parameters":{"options":{},"sheetName":"{{TRACK_SHEET_GID}}","documentId":"{{WEB_SHEET_ID}}"},"credentials":{"googleSheetsOAuth2Api":{"id":"KsXWRZTrfCUFrrHD","name":"Google Sheets"}},"typeVersion":4.5},{"id":"25ef76ec-cf0d-422e-b060-68c49192a008","name":"clean html","type":"n8n-nodes-base.code","position":[460,60],"parameters":{"jsCode":"// CleanHtmlFunction.js\n// Purpose: n8n Function node to clean HTML: remove doctype, scripts, styles, head, comments, classes, extra blank lines, and non-whitelisted tags\n\nreturn items.map(item => {\n  const rawHtml = item.json.data;\n\n  // 1) remove doctype, scripts, styles, comments and head section, and strip class attributes\n  let cleaned = rawHtml\n    .replace(/<!doctype html>/gi, '')\n    .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n    .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n    .replace(/<!--[\\s\\S]*?-->/g, '')\n    .replace(/<head[\\s\\S]*?<\\/head>/gi, '')\n    .replace(/\\sclass=\"[^\"]*\"/gi, '');\n\n  // 2) define whitelist of tags to keep\n  const allowedTags = [\n    'h1','h2','h3','h4','h5','h6',\n    'p','ul','ol','li',\n    'strong','em','a','blockquote',\n    'code','pre'\n  ];\n\n  // 3) strip out all tags not in the whitelist, reconstruct allowed tags cleanly\n  cleaned = cleaned.replace(\n    /<\\/?([a-z][a-z0-9]*)\\b[^>]*>/gi,\n    (match, tagName) => {\n      const name = tagName.toLowerCase();\n      if (allowedTags.includes(name)) {\n        return match.startsWith('</') ? `</${name}>` : `<${name}>`;\n      }\n      return '';\n    }\n  );\n\n  // 4) collapse multiple blank or whitespace-only lines into a single newline\n  cleaned = cleaned.replace(/(\\s*\\r?\\n\\s*){2,}/g, '\\n');\n\n  // 5) trim leading/trailing whitespace\n  cleaned = cleaned.trim();\n\n  return {\n    json: { cleanedHtml: cleaned }\n  };\n});"},"typeVersion":2},{"id":"f72660d5-8427-4655-acbe-10365273c27b","name":"extract data","type":"@n8n/n8n-nodes-langchain.chainLlm","position":[680,60],"parameters":{"text":"={{ $json.cleanedHtml }}","messages":{"messageValues":[{"message":"=You are an expert in web page scraping. Provide a structured response in JSON format. Only the response, without commentary.\n\nExtract the product information for {{ $(‘url’).item.json.url.split(’/s?k=’)[1].split(’&’)[0] }} present on the page.\n\nname\ndescription\nrating\nreviews\nprice"}]},"promptType":"define","hasOutputParser":true},"typeVersion":1.6},{"id":"8b4af1bb-d7f8-456e-b630-ecd9b6e4bcdc","name":"add results","type":"n8n-nodes-base.googleSheets","position":[1280,200],"parameters":{"columns":{"value":{"name":"={{ $json.output.name }}","price":"={{ $json.output.price }}","rating":"={{ $json.output.rating }}","reviews":"={{ $json.output.reviews }}","description":"={{ $json.output.description }}"},"schema":[{"id":"name","type":"string"},{"id":"description","type":"string"},{"id":"rating","type":"string"},{"id":"reviews","type":"string"},{"id":"price","type":"string"}],"mappingMode":"defineBelow"},"options":{},"operation":"append","sheetName":"{{RESULTS_SHEET_GID}}","documentId":"{{WEB_SHEET_ID}}"},"credentials":{"googleSheetsOAuth2Api":{"id":"KsXWRZTrfCUFrrHD","name":"Google Sheets"}},"typeVersion":4.5},{"id":"7a5ba438-2ede-4d6c-b8fa-9a958ba1ef3e","name":"Split items","type":"n8n-nodes-base.splitOut","position":[1060,60],"parameters":{"include":"allOtherFields","options":{},"fieldToSplitOut":"output"},"typeVersion":1}],"pinData":{},"connections":{"url":{"main":[[],[{"node":"scrap url","type":"main","index":0}]]},"scrap url":{"main":[[{"node":"clean html","type":"main","index":0}]]},"clean html":{"main":[[{"node":"extract data","type":"main","index":0}]]},"Split items":{"main":[[{"node":"add results","type":"main","index":0}]]},"add results":{"main":[[{"node":"url","type":"main","index":0}]]},"extract data":{"main":[[{"node":"Split items","type":"main","index":0}]]},"get urls to scrape":{"main":[[{"node":"url","type":"main","index":0}]]},"OpenRouter Chat Model":{"ai_languageModel":[[{"node":"extract data","type":"ai_languageModel","index":0}]]},"Structured Output Parser":{"ai_outputParser":[[{"node":"extract data","type":"ai_outputParser","index":0}]]},"When clicking ‘Test workflow’":{"main":[[{"node":"get urls to scrape","type":"main","index":0}]]}}}


name: Search LinkedIn companies, Score with AI and add them to Google Sheet CRM
author_name: Matthieu
author_username: yaznow
nodes_used: ["n8n-nodes-base.googleSheets","n8n-nodes-base.httpRequest","@n8n/n8n-nodes-langchain.openAi"]
categories: []
created_at: 2025-05-06T16:14:52.314Z
updated_at: 2025-05-06T16:14:52.314Z
url: https://n8n.io/workflows/3904
scraped_at: 2025-06-19 22:07:56
description:
  Search LinkedIn companies, Score with AI and add them to Google Sheet CRM

  Setup Video: https://youtube.com/watch?v=m904RNxtF0w&t

  Who is this for?
  This template is ideal for sales teams, business development professionals, and marketers looking to build a targeted prospect database with automatic qualification. Perfect for agencies, consultants, and B2B companies wanting to identify and prioritize the most promising potential clients.

  What problem does this workflow solve?
  Manually researching companies on LinkedIn, evaluating their fit for your services, and tracking them in your CRM is time-consuming and subjective. This automation streamlines lead generation by automatically finding, scoring, and importing qualified prospects into your database.

  What this workflow does
  This workflow automatically searches for companies on LinkedIn based on your criteria, retrieves detailed information about each company, filters them based on quality indicators, uses AI to score how well they match your ideal customer profile, and adds them to your Google Sheet CRM while preventing duplicates.

  Setup
  Create a Ghost Genius API account and get your API key
  Configure HTTP Request nodes with Header Auth credentials
  Create a copy of the provided Google Sheet template
  Set up your Google Sheet and OpenAI credentials following n8n documentation
  Customize the "Set Variables" node to match your target audience and scoring criteria

  How to customize this workflow
  Modify search parameters to target different industries, locations, or company sizes
  Adjust the follower count threshold based on your qualification criteria
  Customize the AI scoring system to align with your specific product or service offering
  Add notification nodes to alert you when high-scoring companies are identified

workflow_json:
  {"id":"r82OA8ExycLYuK1u","meta":{"instanceId":"95a1299fb2b16eb2219cb044f54e72c2d00dcd2c72efe717b3c308d200f29927"},"name":"My workflow 8","tags":[],"nodes":[{"id":"ed517555-fff0-4cbb-9ea0-59794b95e6f4","name":"Process Each Company","type":"n8n-nodes-base.splitInBatches","onError":"continueRegularOutput","position":[520,780],"parameters":{"options":{}},"typeVersion":3,"alwaysOutputData":false},{"id":"5b1c73ea-812f-4623-ad6e-64b52dc33c58","name":"Get Company Info","type":"n8n-nodes-base.httpRequest","onError":"continueRegularOutput","position":[760,780],"parameters":{"url":"https://api.ghostgenius.fr/v2/company","options":{"batching":{"batch":{"batchSize":1,"batchInterval":2000}}},"sendQuery":true,"authentication":"genericCredentialType","genericAuthType":"httpHeaderAuth","queryParameters":{"parameters":[{"name":"url","value":"={{ $json.url }}"}]}},"credentials":{"httpHeaderAuth":{"id":"XdFg4wGkcxwRPUMo","name":"Header Auth account 4"}},"retryOnFail":true,"typeVersion":4.2},{"id":"06a1acfb-7aa3-43fa-abd7-0fdf1a5f0cf9","name":"Filter Valid Companies","type":"n8n-nodes-base.if","onError":"continueRegularOutput","position":[980,780],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"5ea943a6-8f6c-4cb0-b194-8c92d4b2aacc","operator":{"type":"string","operation":"notEmpty","singleValue":true},"leftValue":"={{ $json.website }}","rightValue":"[null]"},{"id":"8235b9bb-3cd4-4ed4-a5dc-921127ff47c7","operator":{"type":"number","operation":"gt"},"leftValue":"={{ $json.followers_count }}","rightValue":200}]}},"typeVersion":2.2},{"id":"4803085d-ba62-4e38-bf49-af867a909939","name":"Is New Company?","type":"n8n-nodes-base.if","position":[1380,780],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"050c33be-c648-44d7-901c-51f6ff024e97","operator":{"type":"object","operation":"empty","singleValue":true},"leftValue":"={{ $('Check If Company Exists').all().first().json }}","rightValue":""}]}},"typeVersion":2.2},{"id":"744592d9-bcbe-448d-893d-d8fe4d55bb19","name":"Set Variables","type":"n8n-nodes-base.set","position":[-220,780],"parameters":{"options":{},"assignments":{"assignments":[{"id":"e81e4891-4786-4dd9-a338-d1095e27f382","name":"Your target","type":"string","value":"Growth Marketing Agency"},{"id":"ed2b6b08-66aa-4d4b-b68c-698b5e841930","name":"B: 1-10 employees, C: 11-50 employees, D: 51-200 employees, E: 201-500 employees, F: 501-1000 employees, G: 1001-5000 employees, H: 5001-10,000 employees, I: 10,001+ employees","type":"string","value":"C"},{"id":"f1d02f1a-8115-4e0c-a5ec-59bf5b54263b","name":"Location (find it on : https://www.ghostgenius.fr/tools/search-sales-navigator-locations-id)","type":"string","value":"103644278"},{"id":"21bdb871-9327-4553-bb4a-a138be9f735c","name":"Your product or service","type":"string","value":"our CRM implementation services"},{"id":"31f5adfc-8a8f-498c-9e57-24584c42f7de","name":"Positive indicators (3-5 specific factors that indicate a company might need your product)","type":"string","value":"- Mentions challenges with customer relationships or sales processes \n- Company is in growth phase with expanding client base \n- Mentions need for better data organization or customer insights \n- References marketing automation, sales pipelines, or customer retention "},{"id":"630807cb-9d06-41ee-8534-a652ed76cb4c","name":"Negative indicators (3-5 specific factors that indicate a company might NOT need your product)","type":"string","value":"- Very small companies (1-5 employees) or extremely large enterprises \n- Already mentions using advanced CRM solutions \n- No indication of sales process or customer relationship management in description \n- Pure manufacturing or product-based business with minimal customer interaction \n- Non-profit or government entity with unique relationship management needs"}]}},"typeVersion":3.4},{"id":"289016bf-2d5f-4bd0-b69d-cc2e2e437015","name":"Search Companies","type":"n8n-nodes-base.httpRequest","position":[-20,780],"parameters":{"url":"https://api.ghostgenius.fr/v2/search/companies","options":{"pagination":{"pagination":{"parameters":{"parameters":[{"name":"page","value":"={{ $pageCount + 1 }}"}]},"maxRequests":3,"requestInterval":2000,"limitPagesFetched":true,"completeExpression":"={{ $response.body.data.isEmpty() }}","paginationCompleteWhen":"other"}}},"sendQuery":true,"authentication":"genericCredentialType","genericAuthType":"httpHeaderAuth","queryParameters":{"parameters":[{"name":"keywords","value":"={{ $json['Your target'] }}"},{"name":"locations","value":"={{ $json['Location (find it on : https://www'].ghostgenius['fr/tools/search-sales-navigator-locations-id)'] }}"},{"name":"company_size","value":"={{ $json['B: 1-10 employees, C: 11-50 employees, D: 51-200 employees, E: 201-500 employees, F: 501-1000 employees, G: 1001-5000 employees, H: 5001-10,000 employees, I: 10,001+ employees'] }}"}]}},"credentials":{"httpHeaderAuth":{"id":"XdFg4wGkcxwRPUMo","name":"Header Auth account 4"}},"typeVersion":4.2},{"id":"0ce071a3-bbd2-4f4d-8a09-ed66a63e976f","name":"Extract Company Data","type":"n8n-nodes-base.splitOut","onError":"continueRegularOutput","position":[180,780],"parameters":{"options":{},"fieldToSplitOut":"data"},"typeVersion":1},{"id":"3f4b829a-915f-4cce-84e5-c8f5fd92b24d","name":"Check If Company Exists","type":"n8n-nodes-base.googleSheets","position":[1200,780],"parameters":{"options":{},"filtersUI":{"values":[{"lookupValue":"={{ $json.id }}","lookupColumn":"ID"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1LfhqpyjimLjyQcmWY8mUr6YtNBcifiOVLIhAJGV9jiM/edit#gid=0","cachedResultName":"Companies"},"documentId":{"__rl":true,"mode":"list","value":"1LfhqpyjimLjyQcmWY8mUr6YtNBcifiOVLIhAJGV9jiM","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1LfhqpyjimLjyQcmWY8mUr6YtNBcifiOVLIhAJGV9jiM/edit?usp=drivesdk","cachedResultName":"CRM"}},"credentials":{"googleSheetsOAuth2Api":{"id":"Y8D8KsfgZCZmP2Vh","name":"Google Sheets account"}},"typeVersion":4.5,"alwaysOutputData":true},{"id":"7e40feaa-c4f9-4d9d-bfbe-1586ca64374b","name":"AI Company Scoring","type":"@n8n/n8n-nodes-langchain.openAi","position":[1700,800],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-4.1","cachedResultName":"GPT-4.1"},"options":{"temperature":0.2},"messages":{"values":[{"role":"system","content":"=You are an AI assistant that evaluates companies to determine if they might be interested in {{ $('Set Variables').item.json['Your product or service'] }}.\n\nEvaluate the company information provided on a scale of 0 to 10, where:\n- 0 = Not at all likely to be interested\n- 10 = Extremely likely to be interested\n\nBase your evaluation on these criteria:\n1. Industry fit: How well does the company's industry align with {{ $('Set Variables').item.json['Your product or service'] }}?\n2. Company profile: Is the company size, growth stage, and location appropriate for {{ $('Set Variables').item.json['Your product or service'] }}?\n3. Pain points: Based on their description, do they likely have challenges that {{ $('Set Variables').item.json['Your product or service'] }} solves?\n\nFactors that indicate a good fit:\n{{ $('Set Variables').item.json['Positive indicators (3-5 specific factors that indicate a company might need your product)'] }}\n\nFactors that indicate a poor fit:\n{{ $('Set Variables').item.json['Negative indicators (3-5 specific factors that indicate a company might NOT need your product)'] }}\n\nRespond ONLY with this JSON format:\n```json\n{\n  \"score\": [number between 0 and 10],\n}"},{"content":"=Here is the company to analyze:\nName: {{ $('Filter Valid Companies').item.json.name }}\n{{ $('Filter Valid Companies').item.json.tagline }}\n{{ $('Filter Valid Companies').item.json.description }}\nNumber of employees: {{ $('Filter Valid Companies').item.json.staff_count }}\nIndustry: {{ $('Filter Valid Companies').item.json.industries }}\nSpecialties: {{ $('Filter Valid Companies').item.json.specialities }}\nLocation: {{ $('Filter Valid Companies').item.json.locations?.toJsonString() }}\nFounded in: {{ $('Filter Valid Companies').item.json.founded_on }}\nFunding: {{ $('Filter Valid Companies').item.json.funding?.toJsonString() }}\n"}]},"jsonOutput":true},"credentials":{"openAiApi":{"id":"SSQ6BcbSKW6I0uSn","name":"OpenAi account"}},"typeVersion":1.8},{"id":"dfe8263f-a4ed-411f-a962-7dcca478a4be","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-500,440],"parameters":{"color":6,"width":860,"height":560,"content":"## LinkedIn Company Search\nThis section initiates the workflow and searches for your target companies on LinkedIn using the Ghost Genius API.\n\nYou can filter and refine your search using keywords, company size, location, industry, or even whether the company has active job postings. Use the \"Set Variables\" node for it (this node also allows you to customize the AI Lead Scoring prompt).\n\nNote that you can retrieve a maximum of 1000 companies per search (corresponding to 100 LinkedIn pages), so it's important not to exceed this number of results to avoid losing prospects.\n\nExample: Let's say I want to target Growth Marketing Agencies with 11-50 employees. I do my search and see that there are 10,000 results. So I refine my search by using location to go country by country and retrieve all 10,000 results in several batches ranging from 500 to 1000 depending on the country.\n\nTips: To test the workflow or to see the number of results of your search, change the pagination parameter (Max Pages) in the \"Search Companies\" node. It will be displayed at the very top of the response JSON."},"typeVersion":1},{"id":"ec72fb51-1113-435d-b4fb-575d478a7bed","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[440,440],"parameters":{"color":4,"width":1120,"height":560,"content":"## Company Data Processing \nThis section processes each company individually.\n\nWe retrieve all the company information using Get Company Details by using the LinkedIn link obtained from the previous section.\n\nThen we filter the company based on the number of followers, which gives us a first indication of the company's credibility (200 in this case), and whether their LinkedIn page has a website listed.\n\nThe workflow implements batch processing with a 2-second delay between requests to respect API rate limits. This methodical approach ensures reliable data collection while preventing API timeouts.\n\nYou can adjust these thresholds based on your target market - increasing the follower count for more established businesses or decreasing it for emerging markets.\n\nThe last two modules checks if the company already exists in your database (using LinkedIn ID) to prevent duplicates because when you do close enough searches, some companies may come up several times."},"typeVersion":1},{"id":"5fe1edea-58fa-44d0-8ae7-4dc2720f1e34","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[1640,440],"parameters":{"color":5,"width":780,"height":560,"content":"## AI Scoring and Storage\nThis section scores the company and stores it in a Google Sheet.\n\nIt's important to properly fill in the \"Set variables\" node at the beginning of the workflow to get a result relevant to your use case. You can also manually modify the system prompt.\n\nRegardless of the score obtained, it's very important to always store the company. Note that we add a 2-second \"wait\" module to respect Google Sheet's rate limits.\n\nWe add the company to the \"Companies\" sheet in this [Google Sheet](https://docs.google.com/spreadsheets/d/1LfhqpyjimLjyQcmWY8mUr6YtNBcifiOVLIhAJGV9jiM/edit?usp=sharing) which you can make a copy of and use.\n\nThis AI scoring functionality is extremely impressive once perfectly configured, so I recommend taking some time to test with several companies to ensure the scoring system works well for your needs!\n\n"},"typeVersion":1},{"id":"bf61dc0f-68a7-4ddc-b281-b03a8d8979c1","name":"Wait 2s","type":"n8n-nodes-base.wait","position":[2060,800],"webhookId":"d22fd305-d8f6-4fc3-8a96-62386fa30f94","parameters":{"amount":2},"typeVersion":1.1},{"id":"a428ece3-c4e1-4ba0-826f-3fdb861ba867","name":"Add Company to CRM","type":"n8n-nodes-base.googleSheets","position":[2260,800],"parameters":{"columns":{"value":{"ID":"={{ $('Get Company Info').item.json.id }}","Name":"={{ $('Get Company Info').item.json.name }}","Score":"={{ $json.message.content.score }}","State":"Qualified","Summary":"={{ $('Get Company Info').item.json.description }}","Website":"={{ $('Get Company Info').item.json.website }}","LinkedIn":"={{ $('Get Company Info').item.json.url }}"},"schema":[{"id":"Name","type":"string","display":true,"required":false,"displayName":"Name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Website","type":"string","display":true,"required":false,"displayName":"Website","defaultMatch":false,"canBeUsedToMatch":true},{"id":"LinkedIn","type":"string","display":true,"required":false,"displayName":"LinkedIn","defaultMatch":false,"canBeUsedToMatch":true},{"id":"ID","type":"string","display":true,"required":false,"displayName":"ID","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Summary","type":"string","display":true,"required":false,"displayName":"Summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Score","type":"string","display":true,"required":false,"displayName":"Score","defaultMatch":false,"canBeUsedToMatch":true},{"id":"State","type":"string","display":true,"removed":false,"required":false,"displayName":"State","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/10lxvwdeCf7vrAuWsNRGIsSTICEI3z-SUCDVHr8XzAYQ/edit#gid=0","cachedResultName":"Companies"},"documentId":{"__rl":true,"mode":"list","value":"1LfhqpyjimLjyQcmWY8mUr6YtNBcifiOVLIhAJGV9jiM","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1LfhqpyjimLjyQcmWY8mUr6YtNBcifiOVLIhAJGV9jiM/edit?usp=drivesdk","cachedResultName":"CRM"}},"credentials":{"googleSheetsOAuth2Api":{"id":"Y8D8KsfgZCZmP2Vh","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"66bb5166-a259-433d-afa7-3d08c3fc0291","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[0,0],"parameters":{"width":600,"height":380,"content":"## Introduction\nWelcome to my template! Before explaining how to set it up, here's some important information:\n\nThis automation is an alternative version of [this template](https://n8n.io/workflows/3717-search-linkedin-companies-and-add-them-to-airtable-crm/) that differs by using Google Sheets instead of Airtable and adding a Lead Scoring system allowing for more precision in our targeting.\n\nThis automation therefore allows you, starting from a LinkedIn search, to enrich company data and score them to determine if they might be interested in your services/product.\n\nFor any questions, you can send me a DM on my [LinkedIn](https://www.linkedin.com/in/matthieu-belin83/) :)  "},"typeVersion":1},{"id":"65163811-d0e8-4fbc-8cf6-f7534cdbf952","name":"Sticky Note5","type":"n8n-nodes-base.stickyNote","position":[680,0],"parameters":{"width":600,"height":380,"content":"## Setup\n- Create an account on [Ghost Genius API](ghostgenius.fr) and get your API key.\n\n- Configure the Search Companies and Get Company Info modules by creating a \"Header Auth\" credential:\n**Name: \"Authorization\"**\n**Value: \"Bearer api_key\"**\n\n- Create a copy of this [Google Sheet](https://docs.google.com/spreadsheets/d/1LfhqpyjimLjyQcmWY8mUr6YtNBcifiOVLIhAJGV9jiM/edit?usp=sharing) by clicking on File => Make a copy (in Google Sheet).\n\n- Configure your Google Sheet credential by following the n8n documentation.\n\n- Create an OpenAI key [here](https://platform.openai.com/docs/overview) and add the credential to the \"AI Company Scoring\" node following the n8n documentation.\n\n- Add your information to the \"Set Variables\" node."},"typeVersion":1},{"id":"46d71790-f8f1-4d27-b1c2-127e86981721","name":"Sticky Note6","type":"n8n-nodes-base.stickyNote","position":[1360,0],"parameters":{"width":600,"height":380,"content":"## Tools \n**(You can use the API and CRM of your choice; this is only a suggestion)**\n\n- API Linkedin : [Ghost Genius API](https://ghostgenius.fr) \n\n- API Documentation : [Documentation](https://ghostgenius.fr/docs)\n\n- CRM : [Google Sheet](https://workspace.google.com/intl/en/products/sheets/)\n\n- AI : [OpenAI](https://openai.com)\n\n- LinkedIn Location ID Finder : [Ghost Genius Locations ID Finder](https://ghostgenius.fr/tools/search-sales-navigator-locations-id)"},"typeVersion":1},{"id":"efa59bb6-f08b-449d-90d5-6adef12ed05d","name":"Start","type":"n8n-nodes-base.manualTrigger","position":[-420,780],"parameters":{},"typeVersion":1},{"id":"1014422f-95c8-4473-8760-acbe7f9f9898","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[880,-120],"parameters":{"color":3,"height":80,"content":"# [Setup Video](http://youtube.com/watch?v=m904RNxtF0w&t)\n"},"typeVersion":1}],"active":false,"pinData":{},"settings":{"executionOrder":"v1"},"versionId":"08f2e920-6ec1-46a2-8e39-3dfe5f479528","connections":{"Start":{"main":[[{"node":"Set Variables","type":"main","index":0}]]},"Wait 2s":{"main":[[{"node":"Add Company to CRM","type":"main","index":0}]]},"Set Variables":{"main":[[{"node":"Search Companies","type":"main","index":0}]]},"Is New Company?":{"main":[[{"node":"AI Company Scoring","type":"main","index":0}],[{"node":"Process Each Company","type":"main","index":0}]]},"Get Company Info":{"main":[[{"node":"Filter Valid Companies","type":"main","index":0}]]},"Search Companies":{"main":[[{"node":"Extract Company Data","type":"main","index":0}]]},"AI Company Scoring":{"main":[[{"node":"Wait 2s","type":"main","index":0}]]},"Add Company to CRM":{"main":[[{"node":"Process Each Company","type":"main","index":0}]]},"Extract Company Data":{"main":[[{"node":"Process Each Company","type":"main","index":0}]]},"Process Each Company":{"main":[[],[{"node":"Get Company Info","type":"main","index":0}]]},"Filter Valid Companies":{"main":[[{"node":"Check If Company Exists","type":"main","index":0}],[{"node":"Process Each Company","type":"main","index":0}]]},"Check If Company Exists":{"main":[[{"node":"Is New Company?","type":"main","index":0}]]}}}


name: Build a Personal Assistant with Google Gemini, Gmail and Calendar using MCP
author_name: Aitor | 1Node
author_username: aitoralonso
categories: []
created_at: 2025-05-06T17:01:07.757Z
updated_at: 2025-05-06T17:01:07.757Z
url: https://n8n.io/workflows/3905
scraped_at: 2025-06-19 22:07:56
description:
  Talk to Your Apps: Building a Personal Assistant MCP Server with Google Gemini

  Wouldn't it be cool to just tell your computer or phone to "schedule a meeting with Sarah next Tuesday at 3 PM" or "find John Doe's email address" and have it actually do it? That's the dream of a personal assistant!

  With n8n and the power of MCP and AI models like Google Gemini, you can actually build something pretty close to that. We've put together a workflow that shows you how you can use a natural language chat interface to interact with your other apps, like your CRM, email, and calendar.

  What You Need to Get Started

  Before you dive in, you'll need a few things:

  n8n:** An n8n instance (either cloud or self-hosted) to build and run your workflow.
  Google Gemini Access:** Access to the Google Gemini model via an API key.
  Credentials for Your Apps:** API keys or login details for the specific CRM, Email, and Calendar services you want to connect (like Google Sheets for CRM, Gmail, Google Calendar, etc., depending on your chosen nodes).
  A Chat Interface:** A way to send messages to n8n to trigger the workflow (e.g., via a chat app node or webhook).

  How it Works (In Simple Terms)

  Imagine this workflow is like a helpful assistant who sits between you and your computer.

  Step 1: You Talk, the AI Agent Listens

  It all starts when you send a message through your connected chat interface. Think of this as you speaking directly to your assistant.

  Step 2: The Assistant's Brain (Google Gemini)

  Your message goes straight to the assistant's "brain." In this case, the brain is powered by a smart AI model like Google Gemini. In our template we are using the latest Gemini 2.5 Pro. But this is totally up to you. Experiment and track which model fits the kind of tasks you will pass to the agent. Its job is to understand exactly what you're asking for.

  Are you asking to create something?
  Are you asking to find information?
  Are you asking to update something?

  The brain also uses a "memory" so it can remember what you've talked about recently, making the conversation feel more natural. We are using the default context window, which is the past 5 interactions.

  Step 3: The Assistant Decides What Tool to Use

  Once the brain understands your request, the assistant figures out the best way to help you. It looks at the request and thinks, "Okay, to do this, I need to use one of my tools."

  Step 4: The Assistant's Toolbox (MCP & Your Apps)

  Here's where the "MCP" part comes in. Think of "MCP" (Model Context Protocol) as the assistant's special toolbox. Inside this toolbox are connections to all the different apps and services you use – your CRM for contacts, your email service, and your calendar.

  The MCP system acts like a manager for these tools, making them available to the assistant whenever they're needed.

  Step 5: Using the Right Tool for the Job

  Based on what you asked for, the assistant picks the correct tool from the toolbox.

  If you asked to find a contact, it grabs the "Get Contact" node from the CRM section.
  If you wanted to schedule a meeting, it picks the "Create Event" node from the Calendar section.
  If you asked to draft an email, it uses the "Draft Email" node.

  Step 6: The Tool Takes Action

  Now, the node or set of nodes get to work! It performs the action you requested within the specific app.

  The CRM tool finds or adds the contact.
  The Email tool drafts the message.
  The Calendar tool creates the event.

  Step 7: Task Completed!

  And just like that, your request is handled automatically, all because you simply told your assistant what you wanted in plain language.

  Why This is Awesome

  This kind of workflow shows the power of combining AI with automation platforms like n8n. You can move beyond clicking buttons and filling out forms, and instead, interact with your digital life using natural conversation. n8n makes it possible to visually build these complex connections between your chat, the AI brain, and all your different apps.

  Taking it Further (Possible Enhancements)

  This is just the start! You could enhance this personal assistant by:

  Connecting more apps and services (task managers, project tools, etc.).
  Adding capabilities to search the web or internal documents.
  Implementing more sophisticated memory or context handling.
  Getting a notification when the AI agent is done completing each task such as in Slack or Microsoft Teams.
  Allowing the assistant to ask clarifying questions if needed. Building a robust prompt for the AI agent.

  Ready to Automate Your Workflow?

  Imagine the dozens of hours your team could save weekly by automating repetitive tasks through a simple, natural language interface. 

  Need help?
  Feel free to contact us at 1 Node.
  Get instant access to a library of free resources we created.

nodes_used:
  ["@n8n/n8n-nodes-langchain.agent","@n8n/n8n-nodes-langchain.memoryBufferWindow","@n8n/n8n-nodes-langchain.lmChatGoogleGemini","@n8n/n8n-nodes-langchain.mcpClientTool"]

workflow_json:
  {"id":"KhUd3rHKtZAImiXZ","meta":{"instanceId":"9219ebc7795bea866f70aa3d977d54417fdf06c41944be95e20cfb60f992db19","templateCredsSetupCompleted":true},"name":"Personal Assistant MCP server","tags":[],"nodes":[{"id":"f27f3d00-8019-401f-a1c4-5c9754ca5d7e","name":"When chat message received","type":"@n8n/n8n-nodes-langchain.chatTrigger","position":[-220,-60],"webhookId":"989c3a79-5a0c-4ca1-a542-55e060816121","parameters":{"options":{}},"typeVersion":1.1},{"id":"49e4bd69-141f-47ae-bb97-f03a92e56131","name":"Google Gemini Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatGoogleGemini","position":[-80,140],"parameters":{"options":{},"modelName":"models/gemini-2.5-pro-preview-05-06"},"credentials":{"googlePalmApi":{"id":"MF12DwQJWL1egyiN","name":"Google Gemini(PaLM) Api account"}},"typeVersion":1},{"id":"aaa58803-52ad-439b-8876-05a84fc63eaf","name":"Simple Memory","type":"@n8n/n8n-nodes-langchain.memoryBufferWindow","position":[120,140],"parameters":{},"typeVersion":1.3},{"id":"5c49309e-054c-4097-b8c3-1bf0b10539ec","name":"MCP Server Trigger","type":"@n8n/n8n-nodes-langchain.mcpTrigger","position":[20,340],"webhookId":"b37ab045-0b99-4d57-af44-6ae1e9ac6381","parameters":{"path":"b37ab045-0b99-4d57-af44-6ae1e9ac6381"},"typeVersion":1},{"id":"24e5ee35-c53c-4e82-9d79-d48d9220d7ac","name":"MCP Client","type":"@n8n/n8n-nodes-langchain.mcpClientTool","position":[480,140],"parameters":{"sseEndpoint":"<set-your-url-here>"},"typeVersion":1},{"id":"24d7de59-9db2-43e8-ad2a-923bbfc9877b","name":"Create event","type":"n8n-nodes-base.googleCalendarTool","position":[820,380],"parameters":{"calendar":{"__rl":true,"mode":"list","value":"hello@1node.ai","cachedResultName":"hello@1node.ai"},"additionalFields":{}},"credentials":{"googleCalendarOAuth2Api":{"id":"UfvyikkM1kt4EcMl","name":"Google Calendar account"}},"typeVersion":1.3},{"id":"54a2e041-8c5c-40bb-ae6b-1494b8a5a198","name":"Update event","type":"n8n-nodes-base.googleCalendarTool","position":[580,720],"parameters":{"eventId":"={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Event_ID', ``, 'string') }}","calendar":{"__rl":true,"mode":"list","value":"hello@1node.ai","cachedResultName":"hello@1node.ai"},"operation":"update","updateFields":{}},"credentials":{"googleCalendarOAuth2Api":{"id":"UfvyikkM1kt4EcMl","name":"Google Calendar account"}},"typeVersion":1.3},{"id":"7bebda2e-711f-478b-8ba3-36306b1ffb49","name":"Draft email","type":"n8n-nodes-base.gmailTool","position":[260,780],"webhookId":"4e76cb3d-4239-4030-a23a-544029535f70","parameters":{"message":"={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Message', `Sign off as \"Your name, company name\"`, 'string') }}","options":{},"subject":"={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Subject', ``, 'string') }}","resource":"draft"},"credentials":{"gmailOAuth2":{"id":"q3P6IybvNdDiPZ52","name":"Gmail account"}},"typeVersion":2.1},{"id":"26538db2-f3af-47a8-b97e-2afa7d9ea05d","name":"Personal Assistant","type":"@n8n/n8n-nodes-langchain.agent","position":[40,-60],"parameters":{"options":{}},"typeVersion":1.9},{"id":"04c5d14f-a80d-4113-b3ff-a6ee1ab3917e","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[920,280],"parameters":{"color":5,"width":560,"height":620,"content":"# Calendar nodes\n\nYou could order your agent to create a new event in your Google Calendar, find a specific event, get multiple events or update an event's details. \n\n**The true power of these nodes regarding Email, CRM and Calendar remains in combining multiple into one set of instructions**.\n\n## Examples:\n\n- Find the contact for Jhon for A. Corp and send him an email asking saying that you have scheduled the meeting for next Wednesday at 9AM. Draft an email to remind him of the details and the topic of discussion being the weekly update call and the main company bottlenecks.\n- Update the contact details for Jhon since he changed his email and company to B corp and john[at]bcorpfakeemail[dot]com and please update me about my upcoming meetings with him next month.\n- Send me a summary for all my meetings today. Draft one email for each different person that I'll meet with today, reminding them about today's meeting\n"},"typeVersion":1},{"id":"0de610d2-20bf-4fd4-b93e-60b082d22e56","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[0,700],"parameters":{"color":3,"width":460,"height":500,"content":"# Email nodes\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nYour AI Agent will be able to search through your email inbox to find specific email content for you. Based on this records you can fetch information quickly and order to draft responses to review later.\n\n## Examples:\n\n- hey what were the last 5 emails sent to Jon from X corp? \n- Draft an email with these details to Jon sharing I can't make it today and propose a new time for 9AM tomorrow. "},"typeVersion":1},{"id":"e084e23e-473b-4798-a39c-00529ef9e827","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[-720,360],"parameters":{"color":4,"width":660,"height":480,"content":"# CRM nodes\n\nWith these node operations your \nAI agent will be able to do the following:\n\n- Add a new row with contact data\n- Find a row and its details in the table\n- Update a value or group of values\n\n\n## Examples:\n\n- Add a new contact data with Rick as first name\n his cell is +1 XXX XXX XXXX. \nI will tell you the email later on.\n- Can you tell me the details and email for Jon Doe?\n I want to send him an email reminder.\n- Update Rick's email to rick[at]someemail[dot]com from X corp. please."},"typeVersion":1},{"id":"dc9dcee5-35ec-4ea3-8c67-21c277705dec","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[360,-220],"parameters":{"width":480,"height":480,"content":"## MCP Client\n\nPaste your MCP client URL from the MCP server trigger node.\n\nCustomize your output node to receive the workflow completion notifications (eg. Telegram, Gmail) from your personal assistant"},"typeVersion":1},{"id":"1764e9cd-7fc1-46e7-bc97-33d4b81d5141","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[520,280],"parameters":{"color":5,"width":400,"height":620,"content":""},"typeVersion":1},{"id":"b4dda81f-fa22-43ec-a841-7b924b8884e8","name":"Add new row","type":"n8n-nodes-base.googleSheetsTool","position":[-340,440],"parameters":{"columns":{"value":{},"schema":[],"mappingMode":"autoMapInputData","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1JDoEkNqk1c_TrIht2n1XF-jmIWpk48DP3NUaNbhcFV8/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1JDoEkNqk1c_TrIht2n1XF-jmIWpk48DP3NUaNbhcFV8","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1JDoEkNqk1c_TrIht2n1XF-jmIWpk48DP3NUaNbhcFV8/edit?usp=drivesdk","cachedResultName":"Contacts"}},"credentials":{"googleSheetsOAuth2Api":{"id":"twZdLFsI3kTnqtpG","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"e3bc61e0-1d95-4554-b7ba-f76c3f105339","name":"Find row","type":"n8n-nodes-base.googleSheetsTool","position":[-280,580],"parameters":{"options":{},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1JDoEkNqk1c_TrIht2n1XF-jmIWpk48DP3NUaNbhcFV8/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1JDoEkNqk1c_TrIht2n1XF-jmIWpk48DP3NUaNbhcFV8","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1JDoEkNqk1c_TrIht2n1XF-jmIWpk48DP3NUaNbhcFV8/edit?usp=drivesdk","cachedResultName":"Contacts"}},"credentials":{"googleSheetsOAuth2Api":{"id":"twZdLFsI3kTnqtpG","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"461469da-c47a-486f-98c2-71fcc9abc235","name":"Update row","type":"n8n-nodes-base.googleSheetsTool","position":[-180,680],"parameters":{"columns":{"value":{},"schema":[{"id":"row_number","type":"string","display":true,"removed":false,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"autoMapInputData","matchingColumns":["row_number"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1JDoEkNqk1c_TrIht2n1XF-jmIWpk48DP3NUaNbhcFV8/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1JDoEkNqk1c_TrIht2n1XF-jmIWpk48DP3NUaNbhcFV8","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1JDoEkNqk1c_TrIht2n1XF-jmIWpk48DP3NUaNbhcFV8/edit?usp=drivesdk","cachedResultName":"Contacts"}},"credentials":{"googleSheetsOAuth2Api":{"id":"twZdLFsI3kTnqtpG","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"01c0ba70-c1b1-454f-9b4e-0727c8280ace","name":"Find emails","type":"n8n-nodes-base.gmailTool","position":[120,780],"webhookId":"b36e3112-52b1-4e03-a2d3-74d5d4705891","parameters":{"filters":{},"operation":"getAll","returnAll":"={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Return_All', ``, 'boolean') }}"},"credentials":{"gmailOAuth2":{"id":"q3P6IybvNdDiPZ52","name":"Gmail account"}},"typeVersion":2.1},{"id":"40efd312-032f-496b-8485-a6a49001aa75","name":"Find single event","type":"n8n-nodes-base.googleCalendarTool","position":[760,520],"parameters":{"eventId":"={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Event_ID', ``, 'string') }}","options":{},"calendar":{"__rl":true,"mode":"list","value":"hello@1node.ai","cachedResultName":"hello@1node.ai"},"operation":"get"},"credentials":{"googleCalendarOAuth2Api":{"id":"UfvyikkM1kt4EcMl","name":"Google Calendar account"}},"typeVersion":1.3},{"id":"8ac5c33e-f4fb-4627-98d5-b66838db3037","name":"Find multiple events","type":"n8n-nodes-base.googleCalendarTool","position":[680,620],"parameters":{"limit":10,"options":{},"calendar":{"__rl":true,"mode":"list","value":"hello@1node.ai","cachedResultName":"hello@1node.ai"},"operation":"getAll"},"credentials":{"googleCalendarOAuth2Api":{"id":"UfvyikkM1kt4EcMl","name":"Google Calendar account"}},"typeVersion":1.3}],"active":false,"pinData":{},"settings":{"executionOrder":"v1"},"versionId":"99536a4c-8e1b-4b7e-9a2a-8baa404499fe","connections":{"Find row":{"ai_tool":[[{"node":"MCP Server Trigger","type":"ai_tool","index":0}]]},"MCP Client":{"ai_tool":[[{"node":"Personal Assistant","type":"ai_tool","index":0}]]},"Update row":{"ai_tool":[[{"node":"MCP Server Trigger","type":"ai_tool","index":0}]]},"Add new row":{"ai_tool":[[{"node":"MCP Server Trigger","type":"ai_tool","index":0}]]},"Draft email":{"ai_tool":[[{"node":"MCP Server Trigger","type":"ai_tool","index":0}]]},"Find emails":{"ai_tool":[[{"node":"MCP Server Trigger","type":"ai_tool","index":0}]]},"Create event":{"ai_tool":[[{"node":"MCP Server Trigger","type":"ai_tool","index":0}]]},"Update event":{"ai_tool":[[{"node":"MCP Server Trigger","type":"ai_tool","index":0}]]},"Simple Memory":{"ai_memory":[[{"node":"Personal Assistant","type":"ai_memory","index":0}]]},"Find single event":{"ai_tool":[[{"node":"MCP Server Trigger","type":"ai_tool","index":0}]]},"Find multiple events":{"ai_tool":[[{"node":"MCP Server Trigger","type":"ai_tool","index":0}]]},"Google Gemini Chat Model":{"ai_languageModel":[[{"node":"Personal Assistant","type":"ai_languageModel","index":0}]]},"When chat message received":{"main":[[{"node":"Personal Assistant","type":"main","index":0}]]}}}


name: Automate Lead Qualification with RetellAI Phone Agent, OpenAI GPT & Google Sheet
author_name: Dr. Firas
author_username: drfiras
categories: []
created_at: 2025-05-06T23:32:32.465Z
updated_at: 2025-05-06T23:32:32.465Z
url: https://n8n.io/workflows/3912
scraped_at: 2025-06-19 22:07:56
description:
  👉 Build a Phone Agent to qualify outbound leads and schedule inbound calls

  Who is this for?

  This workflow is designed for sales teams, call centers, and businesses handling both outbound and inbound lead calls who want to automate their qualification, follow-up, and call documentation process without manual intervention. It’s ideal for teams using Google Sheets, RetellAI, OpenAI, and Gmail as part of their tech stack.

  Real-World Use Cases

  🛍 E-commerce – Instantly handle product FAQs and order status checks, 24/7.
  🏬 Retail Stores – Share store hours, directions, and return policies without lifting a finger.
  🍽 Restaurants – Take reservations or answer menu questions automatically.
  💼 Service Providers – Book appointments or consultations while you focus on your craft.
  📞 Any Local Business – Deliver friendly, consistent phone support — no live agent required.

  What problem is this workflow solving?

  Managing lead calls at scale can be chaotic—between scheduling outbound qualification calls, handling inbound appointment requests, and making sure every call is documented and followed up. This workflow automates the entire process, reducing human error and saving time by:

  ✅ Sending reminders to reps for outbound calls
  ✅ Automatically placing calls with RetellAI
  ✅ Handling inbound calls and checking caller details
  ✅ Generating and emailing call summaries automatically

  What this workflow does

  This n8n template connects Google Sheets, RetellAI, OpenAI, and Gmail into a seamless workflow:

  Outbound Lead Qualification Workflow
     Triggers when a new lead is added to Google Sheets
     Sends an SMS notification to remind the rep to call in 5 minutes
     (Optional) Waits 5 minutes
     Initiates an automated call to the lead via RetellAI

  Inbound Call Appointment Scheduler
     Receives inbound calls from RetellAI (via webhook)
     Checks if the caller’s number exists in Google Sheets
     Responds to RetellAI with a success or error message

  Post-Call Workflow
     Receives post-call data from RetellAI
     Filters only analyzed calls
     Updates the lead’s record in Google Sheets
     Uses OpenAI to generate a call summary
     Emails the summary to a team inbox or rep

  Setup

  ✅ You need an active RetellAI API key

  Sign up for RetellAI, create an agent, and set the webhook URLs (n8n_call for call events).
  Purchase a Twilio phone number and link it to the agent.  

  ✅ Your Google Sheet must have a column for phone numbers (e.g., "Phone")  
  ✅ Gmail account connected and authorized in n8n  
  ✅ OpenAI API key added to your environment variables or credentials

  Configure your Google Sheets node with the correct spreadsheet ID and range
  Add your RetellAI API key to the HTTP request nodes
  Connect your Gmail account in the Gmail node
  Add your OpenAI key in the OpenAI node

  👉 See full setup guide here: Notion Documentation

  How to customize this workflow to your needs

  Change SMS content**: Edit the text in the “Send SMS reminder” node to match your team’s tone
  Modify call wait time**: Enable and adjust the “Wait 5 minutes” node to any delay you prefer
  Add CRM integration**: Replace or extend the Google Sheets node to update your CRM instead of a spreadsheet
  Customize call summary prompts**: Edit the prompt sent to OpenAI to change the summary style or add extra insights
  Send email to different recipients**: Change the recipient address in the Gmail node or make it dynamic from the lead record

  Need help customizing?
  Contact me for consulting and support : Linkedin

nodes_used:
  ["n8n-nodes-base.googleSheets","n8n-nodes-base.httpRequest","n8n-nodes-base.twilio","n8n-nodes-base.gmail","@n8n/n8n-nodes-langchain.openAi"]

workflow_json:
  {"id":"QO4Mg23JvVfNCICy","meta":{"instanceId":"a2b23892dd6989fda7c1209b381f5850373a7d2b85609624d7c2b7a092671d44","templateCredsSetupCompleted":true},"name":"Build a Phone Agent to qualify outbound leads and inbound calls with RetellAI -vide","tags":[{"id":"12w64ydbjEKDaM0B","name":"inbound","createdAt":"2025-05-06T20:31:43.427Z","updatedAt":"2025-05-06T20:31:43.427Z"},{"id":"xSqaFWDcbJCRECKZ","name":"outbound","createdAt":"2025-05-06T20:31:38.072Z","updatedAt":"2025-05-06T20:31:38.072Z"}],"nodes":[{"id":"78f39980-c9f8-49b6-93bb-a1f61d347ac3","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-240,0],"parameters":{"width":1260,"height":320,"content":"# Outbound lead qualification call workflow"},"typeVersion":1},{"id":"661006b9-dac7-4ac0-882a-2e0cba9dbae1","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[-240,360],"parameters":{"color":5,"width":1260,"height":320,"content":"# Inbound call appointment scheduler workflow"},"typeVersion":1},{"id":"96a278b9-8d2e-4f85-9f6a-2997932a7ca4","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[1060,-420],"parameters":{"color":4,"width":1400,"height":1100,"content":"# Post-call workflow\n## Triggers when a new lead is added in Google Sheets:\n\n### 1 -Sends SMS to remind rep to call in 5 min\n### 2- (Optional delay step)\n### 3- Triggers RetellAI to place an automated call to the lead\n\n## 💡 Requires phone numbers to be formatted in E.164"},"typeVersion":1},{"id":"d082f904-f185-4615-b0d8-9438c731786f","name":"Detect new lead in Google Sheets","type":"n8n-nodes-base.googleSheetsTrigger","position":[-160,100],"parameters":{"event":"rowAdded","options":{},"pollTimes":{"item":[{"mode":"everyHour"}]},"sheetName":{"__rl":true,"mode":"id","value":"="},"documentId":{"__rl":true,"mode":"id","value":"="}},"credentials":{"googleSheetsTriggerOAuth2Api":{"id":"pXaVMshaL2QzVDYh","name":"Google Sheets Trigger account"}},"typeVersion":1},{"id":"c61172c2-7795-47be-acaa-d4824ca69680","name":"Send SMS reminder to call lead in 5 minutes","type":"n8n-nodes-base.twilio","position":[140,100],"parameters":{"to":"={{ $json['Phone Number'] }}","from":"+33600000000","message":"Hello, thanks for your interest in our roofing services. We'll be calling you shortly to ask about your project!","options":{},"resource":"call"},"credentials":{"twilioApi":{"id":"udXVmM3xHYZbMW4g","name":"Twilio account"}},"typeVersion":1},{"id":"d88573d4-ec99-40e4-8603-f1e910d034d1","name":"Wait 5 minutes before making call","type":"n8n-nodes-base.wait","position":[460,100],"webhookId":"344c2d5d-5629-4466-866b-ac6359b3b042","parameters":{"unit":"minutes","amount":1},"typeVersion":1.1},{"id":"d6778895-90dd-471e-9d9d-c48a35154291","name":"Call new lead using RetellAI","type":"n8n-nodes-base.httpRequest","position":[760,100],"parameters":{"url":"https://api.retellai.com/v2/create-phone-call","method":"POST","options":{},"jsonBody":"={\n  \"from_number\": \"+33600000000\",\n  \"to_number\": \"{{ $json['Phone Number'] }}\",\n  \"retell_llm_dynamic_variables\": {\n    \"uuid\": \"{{ $('Detect new lead in Google Sheets').item.json.UUID }}\"\n  }\n}","sendBody":true,"sendHeaders":true,"specifyBody":"json","headerParameters":{"parameters":[{"name":"Authorization","value":"Bearer key_XXXXXXXXX"},{"name":"Content-Type","value":"application/json"}]}},"typeVersion":4.2},{"id":"8e7e7c0c-2600-4b20-ba30-b855d456d302","name":"Receive inbound call from RetellAI (webhook)","type":"n8n-nodes-base.webhook","position":[-160,460],"webhookId":"ebd11c9b-129c-4b59-8c27-9a4b567305f7","parameters":{"path":"ebd11c9b-129c-4b59-8c27-9a4b567305f7","options":{},"httpMethod":"POST","responseMode":"responseNode"},"typeVersion":2},{"id":"36bf25b0-d39d-4127-b005-5e3619069744","name":"Check if phone number exists in Google Sheets","type":"n8n-nodes-base.googleSheets","position":[300,460],"parameters":{"options":{},"filtersUI":{"values":[{"lookupValue":"={{ $json.body.call_inbound.from_number }}","lookupColumn":"Phone Number"}]},"sheetName":{"__rl":true,"mode":"id","value":"="},"documentId":{"__rl":true,"mode":"id","value":"="}},"credentials":{"googleSheetsOAuth2Api":{"id":"51us92xkOlrvArhV","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"0b2dc7b9-84c1-488b-9d02-47cf6ee460c7","name":"Send response to inbound webhook call","type":"n8n-nodes-base.respondToWebhook","position":[760,460],"parameters":{"options":{},"respondWith":"json","responseBody":"={\n  \"call_inbound\": {\n    \"dynamic_variables\": {\n        \"name\": \"{{ $json.Name }}\"\n    }\n  }\n}"},"typeVersion":1.1},{"id":"063a4a31-429f-4cf0-b248-869131e92633","name":"Receive post-call data from RetellAI (webhook)","type":"n8n-nodes-base.webhook","position":[1180,80],"webhookId":"f8878b78-43ea-4caa-b16c-cde9aaf2e9b1","parameters":{"path":"f8878b78-43ea-4caa-b16c-cde9aaf2e9b1","options":{},"httpMethod":"POST"},"typeVersion":2},{"id":"215e2031-983a-4785-b46d-026f64c115e4","name":"Filter for analyzed calls only","type":"n8n-nodes-base.filter","position":[1400,80],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"a0e40476-0054-43ec-b7a7-e872d1c6061a","operator":{"name":"filter.operator.equals","type":"string","operation":"equals"},"leftValue":"={{ $json.body.event }}","rightValue":"call_analyzed"}]}},"typeVersion":2.2},{"id":"f8cae0c3-1b5d-47e6-b7fd-b47558c30d3f","name":"Check if call was outbound","type":"n8n-nodes-base.if","position":[1620,80],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"46590184-4e33-48fd-a9f4-c63b13f88c1f","operator":{"type":"string","operation":"equals"},"leftValue":"={{ $json.body.call.direction }}","rightValue":"outbound"}]}},"typeVersion":2.2},{"id":"8997d5ec-bfb9-4ce9-9e13-6035f02b634e","name":"Update lead record in Google Sheets","type":"n8n-nodes-base.googleSheets","position":[1860,-40],"parameters":{"columns":{"value":{"UUID":"={{ $json.body.call.retell_llm_dynamic_variables.uuid }}","Qualification":"={{ $json.body.call.call_analysis.custom_analysis_data.qualification }}"},"schema":[{"id":"UUID","type":"string","display":true,"removed":false,"required":false,"displayName":"UUID","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Name","type":"string","display":true,"required":false,"displayName":"Name","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Phone Number","type":"string","display":true,"required":false,"displayName":"Phone Number","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Datetime Called","type":"string","display":true,"required":false,"displayName":"Datetime Called","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Qualification","type":"string","display":true,"required":false,"displayName":"Qualification","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"string","display":true,"removed":true,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["UUID"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"id","value":"="},"documentId":{"__rl":true,"mode":"id","value":"="}},"credentials":{"googleSheetsOAuth2Api":{"id":"51us92xkOlrvArhV","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"20757ff8-6604-4c80-96ec-32bfac983ed7","name":"Send call summary email","type":"n8n-nodes-base.gmail","position":[2220,-40],"webhookId":"806ec3f9-8bcb-48ef-8e22-9d1ce3b06bf0","parameters":{"sendTo":"youremail@gmail.com","message":"=Name:\n{{ $json.body.call.call_analysis.custom_analysis_data.first_name }}\n\nNumber:\n{{ $json.body.call.call_analysis.custom_analysis_data.phone_number }}\n\nQualification:\n{{ $json.body.call.call_analysis.custom_analysis_data.qualification }}\n\n\nCall Summary:\n{{ $json.body.call.call_analysis.custom_analysis_data.call_summary }}","options":{},"subject":"=New Lead - Call Summary","emailType":"text"},"credentials":{"gmailOAuth2":{"id":"rKxQHWZ2F5XLJmwF","name":"Gmail account"}},"typeVersion":2.1},{"id":"753bd92d-b95b-4710-bf49-6da89a43223f","name":"Generate call summary with OpenAI","type":"@n8n/n8n-nodes-langchain.openAi","position":[1860,180],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-4.1","cachedResultName":"GPT-4.1"},"options":{},"messages":{"values":[{"content":"=Analyze this call transcript to identify how the call went and identify possible improvements to the voice prompt:\n\n{{ $json.body.call.transcript }}"}]}},"credentials":{"openAiApi":{"id":"6h3DfVhNPw9I25nO","name":"OpenAi account"}},"typeVersion":1.8},{"id":"cf600277-bb07-4f7a-a9b7-3e20017f716d","name":"Send confirmation email to lead","type":"n8n-nodes-base.gmail","position":[2220,180],"webhookId":"453fe9d9-1de6-40a2-bd0c-88cb9c1cc7ef","parameters":{"sendTo":"youremail@gmail.com","message":"=New roofing appointment:\n\nClient Name:\n{{ $('Check if call was outbound').item.json.body.call.call_analysis.custom_analysis_data.first_name }}\n\nClient Number:\n{{ $('Check if call was outbound').item.json.body.call.call_analysis.custom_analysis_data.phone_number }}\n\nAvailabilities:\n{{ $('Check if call was outbound').item.json.body.call.call_analysis.custom_analysis_data.availabilities }}\n\n\nCall Summary:\n{{ $('Check if call was outbound').item.json.body.call.call_analysis.call_summary }}\n\n\nChatGPT analysis of how the call went and suggestions for improving the voice prompt:\n{{ $json.message.content }}","options":{},"subject":"=Roofing Appointment Scheduled","emailType":"text"},"credentials":{"gmailOAuth2":{"id":"rKxQHWZ2F5XLJmwF","name":"Gmail account"}},"typeVersion":2.1},{"id":"f75763b6-0867-4625-89e1-cafa3c9e6e44","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[-240,-420],"parameters":{"width":1260,"height":400,"content":"# ✅ General Workflow Explanation\n##  This workflow automates outbound and inbound lead calls with RetellAI, Google Sheets, OpenAI, and Gmail. It handles:\n\nScheduling and reminding outbound qualification calls\nHandling inbound appointment calls\nAutomatically updating records and sending summaries post-call\n\n## 👉 Dependencies:\n\nActive RetellAI API key\nGoogle Sheet set up with lead data\nGmail API credentials configured"},"typeVersion":1}],"active":false,"pinData":{},"settings":{"executionOrder":"v1"},"versionId":"879f8e4d-91d7-41fc-825d-08f2ef283c25","connections":{"Check if call was outbound":{"main":[[{"node":"Update lead record in Google Sheets","type":"main","index":0}],[{"node":"Generate call summary with OpenAI","type":"main","index":0}]]},"Filter for analyzed calls only":{"main":[[{"node":"Check if call was outbound","type":"main","index":0}]]},"Detect new lead in Google Sheets":{"main":[[{"node":"Send SMS reminder to call lead in 5 minutes","type":"main","index":0}]]},"Generate call summary with OpenAI":{"main":[[{"node":"Send confirmation email to lead","type":"main","index":0}]]},"Wait 5 minutes before making call":{"main":[[{"node":"Call new lead using RetellAI","type":"main","index":0}]]},"Update lead record in Google Sheets":{"main":[[{"node":"Send call summary email","type":"main","index":0}]]},"Send SMS reminder to call lead in 5 minutes":{"main":[[{"node":"Wait 5 minutes before making call","type":"main","index":0}]]},"Receive inbound call from RetellAI (webhook)":{"main":[[{"node":"Check if phone number exists in Google Sheets","type":"main","index":0}]]},"Check if phone number exists in Google Sheets":{"main":[[{"node":"Send response to inbound webhook call","type":"main","index":0}]]},"Receive post-call data from RetellAI (webhook)":{"main":[[{"node":"Filter for analyzed calls only","type":"main","index":0}]]}}}


name: Document Q&A Chatbot with Gemini AI and Supabase Vector Search for Telegram
author_name: Mohammad Ghaffarifar
author_username: mohamadghaffari
categories: []
created_at: 2025-05-08T14:40:13.702Z
updated_at: 2025-05-08T14:40:13.702Z
url: https://n8n.io/workflows/3940
scraped_at: 2025-06-19 22:07:56
description:
  This template creates a Telegram AI Assistant that answers questions based on your documents, powered by Google Gemini and Supabase. Key features include Intelligent HTML Post-processing for rich formatting in Telegram and Adaptive Message Chunking to handle long text responses.
  📹 Watch the Bot in Action



  ▶️ Click the image above to watch a live demo on YouTube. 

  This video provides a live demonstration of the bot's core features and how it interacts. See a quick walkthrough of its capabilities and user flow.

  How it works:

  User uploads a PDF document to a Telegram bot.

  The workflow processes the PDF, creates embeddings using Google Gemini, and stores these embeddings in a Supabase vector table.

  Users then ask questions to the bot.

  The workflow performs a vector search in Supabase to find relevant document chunks based on the user's query.

  Google Gemini uses the retrieved relevant chunks to generate an intelligent answer.

  The bot sends the formatted answer back to the user on Telegram, utilizing HTML markup for enhanced presentation.

  Set up steps:

  Setup should take approximately 15-20 minutes.

  Import the workflow into your n8n instance.

  Configure credentials for Telegram, Google Gemini, and Supabase.

  Set up your Supabase vector table using the provided SQL script.

  Activate the workflow.

  Detailed setup instructions, including how to get API keys and configure nodes, are available in the sticky notes within the workflow itself.

nodes_used:
  ["n8n-nodes-base.telegram","n8n-nodes-base.code","@n8n/n8n-nodes-langchain.agent","@n8n/n8n-nodes-langchain.memoryBufferWindow","@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter","@n8n/n8n-nodes-langchain.vectorStoreSupabase","@n8n/n8n-nodes-langchain.documentDefaultDataLoader","@n8n/n8n-nodes-langchain.embeddingsGoogleGemini","@n8n/n8n-nodes-langchain.lmChatGoogleGemini","@n8n/n8n-nodes-langchain.toolVectorStore","@n8n/n8n-nodes-langchain.toolThink"]

workflow_json:
  {"id":"LL0TBxEbXoK2zhqp","meta":{"instanceId":"af80dcc2dbd3882359ca17a5fe5b2d4bd4ca3cf3cbe39546ecc263e2e97807e5","templateId":"self-building-ai-agent","templateCredsSetupCompleted":true},"name":"AI Document Assistant via Telegram + Supabase","tags":[{"id":"Fo1OtHUY0RXxPbjJ","name":"google-gemini","createdAt":"2025-05-01T23:10:32.399Z","updatedAt":"2025-05-01T23:10:32.399Z"},{"id":"HcgCSAB27xdCFyCf","name":"vectorstore","createdAt":"2025-05-01T23:10:13.148Z","updatedAt":"2025-05-01T23:10:13.148Z"},{"id":"NFkP0TdshXJdwIOG","name":"chatbot","createdAt":"2025-05-01T23:09:53.855Z","updatedAt":"2025-05-01T23:09:53.855Z"},{"id":"QXeMQNrN4XlEXs1I","name":"telegram","createdAt":"2025-05-01T23:09:23.634Z","updatedAt":"2025-05-01T23:09:23.634Z"},{"id":"RLZgltwJo60sK1Dm","name":"embeddings","createdAt":"2025-05-01T23:10:20.621Z","updatedAt":"2025-05-01T23:10:20.621Z"},{"id":"fMH2im2pHJBOzkXp","name":"document-qa","createdAt":"2025-05-01T23:10:07.948Z","updatedAt":"2025-05-01T23:10:07.948Z"},{"id":"ghpuX9kkAqpLyIVR","name":"n8n-ai","createdAt":"2025-05-01T23:10:38.373Z","updatedAt":"2025-05-01T23:10:38.373Z"},{"id":"tSHEttl48VrqMYiV","name":"supabase","createdAt":"2025-05-01T23:10:16.583Z","updatedAt":"2025-05-01T23:10:16.583Z"}],"nodes":[{"id":"0213dfab-a1b2-42c9-9ab1-8a0f1de4c4c0","name":"Google Gemini Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatGoogleGemini","position":[480,40],"parameters":{"options":{},"modelName":"models/gemini-2.5-flash-preview-04-17"},"credentials":{"googlePalmApi":{"id":"QuysglXiB421WI90","name":"Google Gemini(PaLM) Api account"}},"typeVersion":1},{"id":"9c166f83-8ea4-4dc7-8ea2-92ec186c9f32","name":"OpenWeatherMap","type":"n8n-nodes-base.openWeatherMapTool","position":[740,-100],"parameters":{"cityName":"={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('City', ``, 'string') }}"},"credentials":{"openWeatherMapApi":{"id":"MCzSGdWHBJE7l1aN","name":"OpenWeatherMap account"}},"typeVersion":1},{"id":"aa0abeff-b5e9-497b-9d9c-8f79721a5c11","name":"AI Agent","type":"@n8n/n8n-nodes-langchain.agent","position":[480,-320],"parameters":{"text":"={{ $json.message.text }}","options":{"systemMessage":"=4. If the user sends you a message starting with / sign, it means this is a Telegram bot command. For example, all users send /start command as their first message. Try to figure out what these commands mean and reply accodringly.\nUser can only send pdf files and text messages and let them know that this type is not supported if it was not a PDF file or text.\nAt first let them know that they can ask questions about sent PDF files you can use your own capabilities as well. \nGenerate a detailed, well-structured response ,\nFormat the response strictly using Telegram's supported HTML syntax. Use tags like <b>, <i>, <u>, <s>, <span class=\"tg-spoiler\">, <code>, <pre> (with optional <code class=\"language-...\"> inside), <a href=\"...\">, and <blockquote> where appropriate.\n\nStructure the content logically using paragraphs and distinct sections. **Be mindful that this text might need to be split into multiple messages due to character limits (Telegram's limit is around 4096 characters per message). Try to make sections or paragraphs relatively self-contained where possible to facilitate splitting.**\n\n**Ensure all <, >, and & symbols within the *text content* (i.e., not part of an HTML tag or entity) are replaced with the corresponding HTML entities: < with &lt;, > with &gt;, and & with &amp;.**\n\nMaintain proper nesting of HTML tags according to Telegram's rules. While the final splitting will be handled by a script, aim for a structure that is easy to break into logical parts without leaving tags improperly open mid-message."},"promptType":"define","hasOutputParser":true},"typeVersion":1.9},{"id":"72b85aff-4fe7-4705-a07c-463f381cb806","name":"Telegram Trigger","type":"n8n-nodes-base.telegramTrigger","position":[-20,100],"webhookId":"d4f286b2-8094-40e3-aeb2-813eb1895ecf","parameters":{"updates":["message"],"additionalFields":{}},"credentials":{"telegramApi":{"id":"jOxapcl3g1n1HrCE","name":"Telegram account"}},"typeVersion":1.2},{"id":"ea716dba-2856-40a8-ad73-86132f52dda8","name":"Telegram","type":"n8n-nodes-base.telegram","onError":"continueErrorOutput","position":[1540,-320],"webhookId":"137d8d2f-a941-4803-8646-8932525360c3","parameters":{"text":"={{ $json.text }}","chatId":"={{ $json.chatId }}","additionalFields":{"parse_mode":"HTML","appendAttribution":false}},"credentials":{"telegramApi":{"id":"jOxapcl3g1n1HrCE","name":"Telegram account"}},"typeVersion":1.2,"alwaysOutputData":true},{"id":"59a22620-0d26-4e19-940a-5c07efccbdfa","name":"Think","type":"@n8n/n8n-nodes-langchain.toolThink","position":[640,-100],"parameters":{},"typeVersion":1},{"id":"7bb66887-c9c6-4057-bbc0-306d1e20ea12","name":"Embeddings Google Gemini","type":"@n8n/n8n-nodes-langchain.embeddingsGoogleGemini","position":[840,340],"parameters":{"modelName":"models/text-embedding-004"},"credentials":{"googlePalmApi":{"id":"QuysglXiB421WI90","name":"Google Gemini(PaLM) Api account"}},"typeVersion":1},{"id":"668db8fd-3d5f-433a-8ccb-4bea237107ce","name":"Default Data Loader","type":"@n8n/n8n-nodes-langchain.documentDefaultDataLoader","position":[1220,460],"parameters":{"options":{}},"typeVersion":1},{"id":"d1495354-bfc0-4ef1-9102-dc3577580d5b","name":"Recursive Character Text Splitter","type":"@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter","position":[1440,620],"parameters":{"options":{}},"typeVersion":1},{"id":"6a8e5ce6-4c52-4ca0-962f-045ea42dac7c","name":"Extract from File","type":"n8n-nodes-base.extractFromFile","position":[1020,480],"parameters":{"options":{},"operation":"pdf"},"typeVersion":1,"alwaysOutputData":true},{"id":"3f4a9da9-0364-4861-a6f3-33b1d5c501e0","name":"Answer questions with a vector store","type":"@n8n/n8n-nodes-langchain.toolVectorStore","position":[860,-60],"parameters":{"description":"Use this data if the user's question appears to reference an uploaded file, document content, or specific information that might be stored in prior user documents. If not relevant, ignore this source."},"typeVersion":1.1},{"id":"933a93c7-9401-4bac-9b9c-395866b46d61","name":"Supabase Vector Store","type":"@n8n/n8n-nodes-langchain.vectorStoreSupabase","position":[760,80],"parameters":{"options":{"queryName":"match_documents"},"tableName":{"__rl":true,"mode":"list","value":"user_knowledge_base","cachedResultName":"user_knowledge_base"}},"credentials":{"supabaseApi":{"id":"jq6dt73fwyUImYqH","name":"Supabase account"}},"typeVersion":1.1},{"id":"5f37c202-a1ca-4ee0-9de0-267349adffbd","name":"Sticky Note10","type":"n8n-nodes-base.stickyNote","position":[360,200],"parameters":{"color":5,"width":1625,"height":779,"content":"✅ Scenario 2 – Document Upload and Embedding\n\nFlow for downloading a document sent via Telegram, extracting its text, generating embeddings, and inserting them into Supabase Vector Store."},"typeVersion":1},{"id":"6e9c1070-90bc-4ab7-a8a0-62461bede708","name":"Sticky Note11","type":"n8n-nodes-base.stickyNote","position":[360,-420],"parameters":{"color":5,"width":1625,"height":599,"content":"✅ Scenario 1 – Chatbot Interaction\n\nFlow for handling user messages sent to the bot. Includes accessing weather data, answering questions based on user-uploaded documents, and running code using a code execution tool."},"typeVersion":1},{"id":"3b211a14-6813-459f-8d23-b40fc0eb4bd6","name":"Telegram - Embedding Complete","type":"n8n-nodes-base.telegram","position":[1760,320],"webhookId":"4eaead72-f9a7-49a3-95ca-b3bc8f6b9a95","parameters":{"text":"=✅ Document saved!\nFeel free to start asking questions about it.","chatId":"={{ $('Command Router').item.json.message.chat.id }}","additionalFields":{"appendAttribution":false}},"credentials":{"telegramApi":{"id":"jOxapcl3g1n1HrCE","name":"Telegram account"}},"typeVersion":1.2},{"id":"05703266-aaed-491d-87a6-ed7f96a9c49a","name":"Supabase - Save Embeddings","type":"@n8n/n8n-nodes-langchain.vectorStoreSupabase","position":[1200,320],"parameters":{"mode":"insert","options":{},"tableName":{"__rl":true,"mode":"list","value":"user_knowledge_base","cachedResultName":"user_knowledge_base"}},"credentials":{"supabaseApi":{"id":"jq6dt73fwyUImYqH","name":"Supabase account"}},"typeVersion":1.1,"alwaysOutputData":false},{"id":"3b7db0e6-b551-4698-921a-306e837ceffc","name":"Command Router","type":"n8n-nodes-base.switch","position":[160,100],"parameters":{"rules":{"values":[{"outputKey":"document","conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"loose"},"combinator":"and","conditions":[{"id":"895b32db-777d-4d8e-b1d3-596cc9863d09","operator":{"type":"boolean","operation":"exists","singleValue":true},"leftValue":"={{ $json.message.document }}","rightValue":"={{ $json.message.document }}"}]},"renameOutput":true},{"outputKey":"text","conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"loose"},"combinator":"and","conditions":[{"id":"26c12573-8e00-4832-8410-73d2d739c455","operator":{"type":"boolean","operation":"exists","singleValue":true},"leftValue":"={{ $json.message.text }}","rightValue":""}]},"renameOutput":true}]},"options":{"fallbackOutput":"extra"},"looseTypeValidation":true},"typeVersion":3.2},{"id":"fa06fc6c-3661-4065-81fc-09f93d6a4a25","name":"Telegram - Download file","type":"n8n-nodes-base.telegram","position":[600,540],"webhookId":"11b8f884-34bc-401c-8978-b28507d96e40","parameters":{"fileId":"={{ $('Telegram Trigger').item.json.message.document.file_id }}","resource":"file"},"credentials":{"telegramApi":{"id":"jOxapcl3g1n1HrCE","name":"Telegram account"}},"typeVersion":1.2},{"id":"756a36aa-187d-48ca-894c-f8c9a79a4794","name":"Aggregate","type":"n8n-nodes-base.aggregate","notes":"This is used to flag the end of progress—no real aggregation.","position":[1580,320],"parameters":{"options":{},"fieldsToAggregate":{"fieldToAggregate":[{}]}},"notesInFlow":true,"typeVersion":1},{"id":"3b49f357-5d21-4710-bd32-3218d23b1bd9","name":"Fallback- No formatting","type":"n8n-nodes-base.telegram","notes":"This is used if, even after HTML formatting,g Telegram wasn't able to process the text, so we send it without formatting.","position":[1740,-260],"webhookId":"dd2182fe-0b11-4d96-9838-30d60bf8c229","parameters":{"text":"={{ $('Manual Mapping').item.json.text }}","chatId":"={{ $('Manual Mapping').item.json.chatId }}","additionalFields":{"appendAttribution":false}},"credentials":{"telegramApi":{"id":"jOxapcl3g1n1HrCE","name":"Telegram account"}},"notesInFlow":true,"typeVersion":1.2},{"id":"eafdbacb-17e5-4de6-a4e9-b986140353e5","name":"Split Out","type":"n8n-nodes-base.splitOut","position":[1120,-320],"parameters":{"options":{},"fieldToSplitOut":"output"},"typeVersion":1},{"id":"538be3ed-4bd6-4295-ac11-e4d46b943f5a","name":"Simple Memory","type":"@n8n/n8n-nodes-langchain.memoryBufferWindow","position":[540,-100],"parameters":{"sessionKey":"={{ $('Telegram Trigger').item.json.message.from.id }}","sessionIdType":"customKey"},"typeVersion":1.3},{"id":"0afca77d-0e08-4f04-a6d3-b107c1dd54f9","name":"Handle formatting and split","type":"n8n-nodes-base.code","notes":"This is used to prevent Markdown issues in Telegram while sending messages.","position":[900,-320],"parameters":{"language":"python","pythonCode":"import re\nimport html\n\ngemini_output_text = _('AI Agent').first().json.output;\n# Regex to match any HTML tag <...>\nHTML_TAG_PATTERN = re.compile(r'(<[^>]*?>)', re.IGNORECASE)\n\n# List of UNSUPPORTED Telegram HTML tag names\nUNSUPPORTED_TAG_NAMES = [\n    'p', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol',\n    'table', 'thead', 'tbody', 'tr', 'td', 'th', 'div', 'br', 'font',\n    'span', # Span is unsupported *unless* it has the specific class\n    'a'     # A is unsupported *unless* it has the href attribute\n    # Add more unsupported tags if you encounter them\n]\n\n# Regex to match unsupported opening or closing tags based on the names list\n# This pattern is simplified and might misinterpret complex attributes\nUNSUPPORTED_TAG_PATTERN = re.compile(r'<\\/?(' + '|'.join(UNSUPPORTED_TAG_NAMES) + r')\\b[^>]*?>', re.IGNORECASE)\n\n# Regex to match a span tag *without* the class=\"tg-spoiler\" attribute\n# This tries to capture the tag and its content to remove both\nUNSUPPORTED_SPAN_FULL_PATTERN = re.compile(r'<span(?! class=\"tg-spoiler\"\\b)[^>]*?>.*?<\\/span>', re.IGNORECASE | re.DOTALL) # DOTALL allows . to match newlines\n\n# Regex to match an a tag *without* an href attribute\n# This tries to capture the tag and its content to remove both\nUNSUPPORTED_A_FULL_PATTERN = re.compile(r'<a(?![^>]*href=)[^>]*?>.*?<\\/a>', re.IGNORECASE | re.DOTALL)\n\n\n# --- Cleaning Function (Regex Only) ---\n\ndef unescape_common_html_entities(text):\n    \"\"\"\n    Unescapes a limited set of common HTML entities in text.\n    Does NOT use html.unescape for maximum compatibility with \"no external library\" rule.\n    \"\"\"\n    # Order matters: &amp; must be replaced first!\n    text = text.replace('&amp;', '&')\n    text = text.replace('&lt;', '<')\n    text = text.replace('&gt;', '>')\n    text = text.replace('&quot;', '\"')\n    text = text.replace('&apos;', \"'\")\n    # Add more common entities here if needed, e.g., text = text.replace('&nbsp;', ' ')\n    return text\n\n\ndef clean_html_regex_only(html_string):\n    \"\"\"\n    Cleans HTML string using regex: removes unsupported tags and escapes text content.\n    Handles &apos; and other basic entities.\n    WARNING: This is a regex-based approach and is NOT as robust as using an HTML parser.\n    It may fail on complex or malformed HTML.\n\n    Args:\n        html_string (str): The input HTML string.\n\n    Returns:\n        str: The cleaned HTML string.\n    \"\"\"\n    # 1. Remove unsupported tags and their content where specific attributes are missing\n    # Process specific full patterns first\n    cleaned_text = UNSUPPORTED_SPAN_FULL_PATTERN.sub('', html_string)\n    cleaned_text = UNSUPPORTED_A_FULL_PATTERN.sub('', cleaned_text)\n\n    # 2. Remove remaining unsupported opening/closing tags, leaving content behind\n    cleaned_text = UNSUPPORTED_TAG_PATTERN.sub('', cleaned_text)\n\n    # 3. Split the remaining string into tags and text segments\n    # This pattern captures the tags themselves so we can differentiate them from text\n    parts = HTML_TAG_PATTERN.split(cleaned_text)\n\n    cleaned_parts = []\n    for part in parts:\n        if not part:\n            continue\n\n        if HTML_TAG_PATTERN.fullmatch(part):\n            # If the part is a tag (matches the full tag pattern)\n            # We assume at this point it's a supported tag due to previous removal steps.\n            # Keep the tag as is.\n            cleaned_parts.append(part)\n        else:\n            # If the part is text content\n            # 1. Unescape common HTML entities (like &apos;) that might be in the text\n            unescaped_text = unescape_common_html_entities(part)\n\n            # 2. Escape the literal characters <, >, & that are *in* the text content\n            # This ensures only the characters themselves are escaped, not entities.\n            # Need to escape & first to avoid issues with '&amp;' if it resulted from unescaping or was original.\n            re_escaped_text = unescaped_text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')\n\n            cleaned_parts.append(re_escaped_text)\n\n    # Join the processed parts back into a single string\n    return \"\".join(cleaned_parts)\n\n# --- Splitting Logic ---\nSPLIT_PATTERN_REGEX_ONLY = re.compile(r'(<\\/blockquote>|<\\/pre>|\\n\\n|\\s{2,}|(?<=[.!?])\\s+|<[a-z]+[^>]*?>|<\\/[a-z]+>)', flags=re.IGNORECASE)\n\n\ndef split_telegram_message_regex_only(text, max_length=4096):\n    \"\"\"\n    Splits text into multiple messages based on character count and basic patterns.\n    Operates on text already cleaned by clean_html_regex_only.\n    Does NOT guarantee HTML tag integrity across splits due to lack of parsing.\n\n    Args:\n        text (str): The input text (preferably cleaned by clean_html_regex_only).\n        max_length (int): The maximum length for each message part.\n\n    Returns:\n        list: A list of strings, where each string is a message part.\n    \"\"\"\n    if len(text) <= max_length:\n        return [text]\n\n    messages = []\n    current_chunk = \"\"\n\n    # Split by the defined pattern\n    parts = SPLIT_PATTERN_REGEX_ONLY.split(text)\n\n    for part in parts:\n        # Handle parts that are None (can happen with split) or just short whitespace\n        if part is None or (not part.strip() and len(part) < 2 and part != '\\n\\n'):\n             if part is not None and len(part) > 0: # Keep meaningful whitespace splits like \\n\\n\n                  if len(current_chunk) + len(part) <= max_length:\n                       current_chunk += part\n                  else:\n                       # Split happens within meaningful whitespace, finalize chunk\n                       if current_chunk.strip(): # Only add if chunk has content\n                            messages.append(current_chunk.strip())\n                       current_chunk = part # Start new chunk with the whitespace\n             continue # Skip to next part\n\n\n        # Check if adding the current part exceeds the max length\n        if len(current_chunk) + len(part) > max_length:\n            # If the current chunk is empty or only whitespace after stripping,\n            # it means the 'part' itself is too long to fit in a new chunk.\n            if not current_chunk.strip():\n                # Handle very long individual parts (e.g., a huge code block line, a very long word, a single huge tag)\n                # Hard split the long part. WARNING: This can break tags, words, or escape sequences.\n                while len(part) > max_length:\n                    messages.append(part[:max_length])\n                    part = part[max_length:]\n                if part.strip():\n                    current_chunk = part # Remaining part starts a new chunk\n                else:\n                     current_chunk = \"\" # If remainder is just whitespace, clear\n            else:\n                # The current part makes the chunk too long, finalize the current chunk\n                messages.append(current_chunk.strip())\n                # Start a new chunk with the current part\n                current_chunk = part # Keep original part for the new chunk\n\n        else:\n            # Add the current part to the chunk\n            current_chunk += part\n\n    # Add the last chunk\n    if current_chunk.strip(): # Only add if the final chunk has content\n        messages.append(current_chunk.strip())\n\n    # Clean up any empty messages that might have been created\n    messages = [msg for msg in messages if msg.strip()]\n\n    return messages\n  \ncleaned_html_regex = clean_html_regex_only(gemini_output_text)\nmessage_parts_regex = split_telegram_message_regex_only(cleaned_html_regex)\n\nreturn dict({'output': message_parts_regex })"},"typeVersion":2},{"id":"dbea9e13-6ad4-4eb3-8da1-9db9e2116283","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[2000,-420],"parameters":{"width":1960,"height":3520,"content":"# 🤖 Telegram AI Assistant for Your Documents (n8n + Supabase + Gemini)\n\nThis project transforms a standard **Telegram bot** into your dedicated AI assistant – designed to understand and answer questions based on **your own documents**. It seamlessly integrates the power of **Google Gemini** for advanced language capabilities and **Supabase's vector database** for efficient, intelligent document retrieval. Built entirely within the no-code platform **n8n**, it allows you to deploy a sophisticated document chatbot without writing a single line of code.\n\nSimply upload any PDF document to the bot, and instantly gain the ability to chat with it, querying its contents as if it were a knowledgeable expert on your uploaded files.\n\n---\n## 📹 Watch the Bot in Action\n\n[![𝗨𝗻𝗹𝗲𝗮𝘀𝗵𝗶𝗻𝗴 𝗔𝗜 𝗼𝗻 𝗠𝘆 𝗕𝗼𝗼𝗸𝘀𝗵𝗲𝗹𝗳: 𝗙𝗹𝗼𝘄 𝗣𝗿𝗼𝗴𝗿𝗮𝗺𝗺𝗶𝗻𝗴 𝗣𝗼𝘄𝗲𝗿𝘀 𝗮 𝗡𝗲𝘅𝘁-𝗟𝗲𝘃𝗲𝗹 𝗧𝗲𝗹𝗲𝗴𝗿𝗮𝗺 𝗕𝗼𝘁🤖](https://img.youtube.com/vi/r_KGyJApy5M/maxresdefault.jpg)](https://www.youtube.com/watch?v=r_KGyJApy5M)\n\n**▶️ Click the image above to watch a live demo on YouTube.** \n\nThis video provides a live demonstration of the bot's core features and how it interacts. See a quick walkthrough of its capabilities and user flow.\n\n---\n\n## ✨ Ignite Your Workflow: Use Cases\n\nThis project empowers two core interactions:\n\n### 1. Conversational AI Interface (User Inquiry → Telegram Bot → Intelligent Answers)\n- Users pose questions directly to the Telegram bot.\n- The bot generates relevant, informative answers using the cutting-edge capabilities of the Google Gemini LLM.\n- Leveraging a powerful vector search mechanism, it can pull specific, contextual information from previously uploaded documents to provide highly relevant and informed responses.\n- (Optional) Augment answers with real-time data, like current **weather information**.\n\n### 2. Effortless Document Integration (User Upload PDF → Processing → Searchable Knowledge)\n- Users upload a PDF document directly to the bot.\n- The workflow automatically parses the document content, converts it into numerical representations called embeddings using Gemini's embedding models.\n- These embeddings, alongside the document's text content, are then securely stored in a dedicated **Supabase vector table**, creating a searchable knowledge base.\n- Immediately after successful processing, the document becomes part of the bot's memory, enabling users to ask questions about its contents via the standard chat interface.\n\n---\n## 🧠 Core Intelligence Features\n\n- ✅ **Pure No-Code**: Developed and managed entirely within the intuitive [n8n](https://n8n.io) automation platform.\n- 📄 **Seamless PDF Integration**: Easily upload and process PDF documents to expand the bot's knowledge.\n- 🧠 **Powered by Google Gemini**: Utilizes Gemini for both generating document embeddings and formulating intelligent conversational responses.\n- 🗂 **Vector Database Memory (Supabase)**: Employs **Supabase as a robust vector database** for storing and efficiently searching document embeddings, providing the bot with long-term memory about your content.\n- **⚡️ Rapid & Private Retrieval**: The vector search allows for swift identification and retrieval of the most relevant document snippets based on the user's query. This approach enhances response speed and significantly improves data privacy, as **the original document content remains securely stored in your Supabase instance, and only the user's query and the retrieved relevant chunks are sent to the LLM for generating a response.**\n- 🧹 **Intelligent HTML Post-processing**: Cleans the LLM's responses by removing HTML tags not supported by Telegram while preserving essential formatting and correctly escaping special characters in the text content.\n- 📤 **Adaptive Message Chunking**: Splits lengthy AI-generated answers into multiple messages that adhere to Telegram's 4096-character limit, ensuring the full response is delivered cleanly.\n- 🌦️ **Dynamic Weather Data**: (Optional) Integrates with OpenWeatherMap to provide current weather information upon request.\n- **📝 Note on Usage**: This workflow is designed primarily for **personal, single-user** scenarios. It processes each message independently and **does not include multi-user session management**, making it unsuitable for public deployment where different users require separate conversational contexts. For a session-based Telegram bot implemented in Python, you may refer to this project, which is a multi-model telegram bot: [https://github.com/mohamadghaffari/gemini-tel-bot](https://github.com/mohamadghaffari/gemini-tel-bot).\n---\n\n## 🛠 Getting Started: Setup\n\n### 1. Deploy the Workflow in n8n\n\n- Click the \"Use this workflow\" button on the n8n template page.\n- This will open the workflow directly in your n8n instance, ready for configuration.\n\n\n### 2. Connect Your Services: Configure Credentials\n\nCreate API credentials for the following services within your n8n instance:\n\n| Service          | Purpose                          |\n|------------------|------------------------------------|\n| Telegram API     | Receiving user messages & sending replies |\n| Google Gemini    | Generating embeddings & LLM responses |\n| Supabase         | Storing & searching document vectors |\n| OpenWeatherMap   | (Optional) Fetching weather data    |\n\n### 3. Prepare Your Supabase Knowledge Base\n\nSet up a vector-enabled table in your Supabase project to store your document embeddings. Execute the following SQL commands in your Supabase SQL Editor:\n\n``` sql\n-- Enable the pgvector extension to work with embedding vectors\ncreate extension vector;\n\n-- Create a table to store your documents and their embeddings\ncreate table user_knowledge_base (\n  id bigserial primary key,\n  content text, -- Stores the text chunk from the document\n  metadata jsonb, -- Stores document information (e.g., filename, page number)\n  embedding vector(768) -- Stores the vector representation (embedding) generated by Gemini. Adjust dimension if using a different model.\n);\n\n-- Create a function to perform vector similarity search against your documents\ncreate function match_documents (\n  query_embedding vector(768),\n  match_count int default null,\n  filter jsonb DEFAULT '{}'\n) returns table (\n  id bigint,\n  content text,\n  metadata jsonb,\n  similarity float\n)\nlanguage plpgsql\nas $$\n#variable_conflict use_column\nbegin\n  return query\n  select\n    id,\n    content,\n    metadata,\n    -- Calculate cosine similarity: 1 - cosine distance (using the '<=>' operator provided by pgvector)\n    1 - (user_knowledge_base.embedding <=> query_embedding) as similarity\n  from user_knowledge_base\n  where metadata @> filter -- Optional: filter results based on metadata\n  order by user_knowledge_base.embedding <=> query_embedding -- Order by similarity (closest first)\n  limit match_count; -- Limit the number of results\nend;\n$$;\n````\n\nThis sets up the necessary table and a function to perform vector similarity searches, allowing you to find document chunks most similar to a user's query.\n-----\n\n## 📚 Integrated Technologies\n\nThis project brings together powerful tools:\n\n  - [n8n](https://n8n.io) – The central hub for workflow automation and integration.\n  - [Telegram Bot API](https://core.telegram.org/bots/api) – The communication layer for user interaction.\n  - [Supabase](https://supabase.com/) + [pgvector Extension](https://www.google.com/search?q=https://supabase.com/docs/guides/ai/vector-embeddings) – Provides a scalable database with powerful vector search capabilities.\n  - [Google Gemini API](https://ai.google.dev/) – The intelligence engine for embeddings and text generation.\n  - [OpenWeatherMap API](https://openweathermap.org/api) – (Optional) For adding real-time weather features.\n\n-----\n"},"typeVersion":1},{"id":"965ba2bd-747d-4718-a76e-9f7d685dcea4","name":"Manual Mapping","type":"n8n-nodes-base.set","position":[1320,-320],"parameters":{"options":{},"assignments":{"assignments":[{"id":"cdeb5bf1-c91c-44ae-bebd-ab3f4ba2561a","name":"text","type":"string","value":"={{ $json.output }}"},{"id":"7cd7d120-96fa-4539-b343-25bc9b75abb4","name":"chatId","type":"number","value":"={{ $('Command Router').item.json.message.from.id }}"}]}},"typeVersion":3.4},{"id":"c6a315f1-6f0b-4127-b377-b7b12975929f","name":"Unsupported message","type":"n8n-nodes-base.telegram","position":[500,760],"webhookId":"52f3456a-06ef-4799-b245-0293213dcc4b","parameters":{"text":"Unsupported command or file. 😓 Please upload a valid PDF document or ask your question regarding your files.","chatId":"={{ $('Command Router').item.json.message.chat.id }}","additionalFields":{"appendAttribution":false}},"credentials":{"telegramApi":{"id":"jOxapcl3g1n1HrCE","name":"Telegram account"}},"typeVersion":1.2},{"id":"375bd185-3836-4f25-8708-d6dcd79b2675","name":"Send processing document message","type":"n8n-nodes-base.telegram","position":[920,720],"webhookId":"32ade357-f14b-4d10-91f2-02c8aa6e198e","parameters":{"text":"=<b>Processing document...</b>\n<b>Please wait...⏳</b>","chatId":"={{ $('Command Router').item.json.message.chat.id }}","additionalFields":{"parse_mode":"HTML","appendAttribution":false}},"credentials":{"telegramApi":{"id":"jOxapcl3g1n1HrCE","name":"Telegram account"}},"typeVersion":1.2},{"id":"d01f8b15-e495-46cf-bfdf-20b4399c23d7","name":"Send embedding Started message","type":"n8n-nodes-base.telegram","position":[1220,660],"webhookId":"32ade357-f14b-4d10-91f2-02c8aa6e198e","parameters":{"text":"=<b>Document processed ✅ </b> \n<b>Num of pages:</b> {{ $json.numpages }} \n<b>Creator:</b> {{ $json.info.Creator }}\n<b>Title:</b> {{ $json.info.Title }} \n<b>Version:</b> {{ $json.version }}","chatId":"={{ $('Command Router').item.json.message.chat.id }}","additionalFields":{"parse_mode":"HTML","appendAttribution":false}},"credentials":{"telegramApi":{"id":"jOxapcl3g1n1HrCE","name":"Telegram account"}},"typeVersion":1.2}],"active":true,"pinData":{},"settings":{"executionOrder":"v1"},"versionId":"749ec7d0-e135-478a-b02e-9241dbf4ab68","connections":{"Think":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]},"AI Agent":{"main":[[{"node":"Handle formatting and split","type":"main","index":0}]]},"Telegram":{"main":[[],[{"node":"Fallback- No formatting","type":"main","index":0}]]},"Aggregate":{"main":[[{"node":"Telegram - Embedding Complete","type":"main","index":0}]]},"Split Out":{"main":[[{"node":"Manual Mapping","type":"main","index":0}]]},"Simple Memory":{"ai_memory":[[{"node":"AI Agent","type":"ai_memory","index":0}]]},"Command Router":{"main":[[{"node":"Telegram - Download file","type":"main","index":0}],[{"node":"AI Agent","type":"main","index":0}],[{"node":"Unsupported message","type":"main","index":0}]]},"Manual Mapping":{"main":[[{"node":"Telegram","type":"main","index":0}]]},"OpenWeatherMap":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]},"Telegram Trigger":{"main":[[{"node":"Command Router","type":"main","index":0}]]},"Extract from File":{"main":[[{"node":"Supabase - Save Embeddings","type":"main","index":0},{"node":"Send embedding Started message","type":"main","index":0}]]},"Default Data Loader":{"ai_document":[[{"node":"Supabase - Save Embeddings","type":"ai_document","index":0}]]},"Supabase Vector Store":{"ai_vectorStore":[[{"node":"Answer questions with a vector store","type":"ai_vectorStore","index":0}]]},"Embeddings Google Gemini":{"ai_embedding":[[{"node":"Supabase - Save Embeddings","type":"ai_embedding","index":0},{"node":"Supabase Vector Store","type":"ai_embedding","index":0}]]},"Google Gemini Chat Model":{"ai_languageModel":[[{"node":"AI Agent","type":"ai_languageModel","index":0},{"node":"Answer questions with a vector store","type":"ai_languageModel","index":0}]]},"Telegram - Download file":{"main":[[{"node":"Extract from File","type":"main","index":0},{"node":"Send processing document message","type":"main","index":0}]]},"Supabase - Save Embeddings":{"main":[[{"node":"Aggregate","type":"main","index":0}]]},"Handle formatting and split":{"main":[[{"node":"Split Out","type":"main","index":0}]]},"Send embedding Started message":{"main":[[]]},"Recursive Character Text Splitter":{"ai_textSplitter":[[{"node":"Default Data Loader","type":"ai_textSplitter","index":0}]]},"Answer questions with a vector store":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]}}}


name: AI-Powered Restaurant Order Chatbot with GPT-4o for POS Integration
author_name: Kumar Shivam
author_username: shivam840708
categories: []
created_at: 2025-05-08T15:20:19.166Z
updated_at: 2025-05-08T15:20:19.166Z
url: https://n8n.io/workflows/3942
scraped_at: 2025-06-19 22:07:56
description:
  This workflow automates the restaurant POS (Point of Sale) data management process, facilitating seamless order handling, customer tracking, inventory management, and sales reporting. It retrieves order details, processes payment information, updates inventory, and generates real-time sales reports, all integrated into a centralized system that improves restaurant operations.

  The workflow integrates various systems, including a POS terminal to gather order data, payment gateways to process transactions, inventory management tools to update stock, and reporting tools like Google Sheets or an internal database for generating sales and performance reports.

  Who Needs Restaurant POS Automation?
  This POS automation workflow is ideal for restaurant owners, managers, and staff looking to streamline their operations:

  Restaurant Owners – Automate order processing, track sales, and monitor inventory to ensure smooth operations.

  Managers – Access real-time sales data and performance reports to make informed decisions.

  Staff – Reduce manual work, focusing on providing better customer service while the system handles orders and payments.

  Inventory Teams – Automatically update inventory levels based on orders and ingredient usage.

  If you need a reliable and automated POS solution to manage restaurant orders, payments, inventory, and reporting, this workflow minimizes human error, boosts efficiency, and saves valuable time.

  Why Use This Workflow?

  End-to-End Automation – Automates everything from order input to inventory updates and sales reporting.

  Seamless Integration – Connects POS, payment systems, inventory management, and reporting tools for smooth data flow.(if needed)

  Real-Time Data – Provides up-to-the-minute reports on sales, stock levels, and order statuses.

  Scalable & Efficient – Supports multiple locations, multiple users, and high order volumes.

  Step-by-Step: How This Workflow Manages POS Data

  Collect Orders – Retrieves order details from the POS system, including customer information, ordered items, and payment details.

  Update Inventory – Decreases inventory levels based on sold items, ensuring stock counts are always accurate.

  Generate Reports – Compiles sales, revenue, and inventory data into real-time reports and stores them in Google Sheets or an internal database.

  Track Customer Data – Keeps a log of customer details and order history for better service and marketing insights.

  Customization: Tailor to Your Needs

  Multiple POS Systems – Adapt the workflow to work with different POS systems or terminals based on your restaurant setup.

  Custom Reporting – Modify the reporting format or include specific sales metrics (e.g., daily totals, best-selling items, employee performance).

  Inventory Management – Adjust inventory updates to include alerts when stock reaches critical levels or needs reordering.

  Integration with Accounting Software – Connect with platforms like QuickBooks for automated financial tracking.

  🔑 Prerequisites

  POS System Integration – Ensure the POS system can export order data in a compatible format.

  Payment Gateway API – Set up the necessary API keys for payment processing (e.g., Stripe, PayPal).

  Inventory Management Tools – Use inventory software or databases that can automatically update stock levels.

  Reporting Tools – Use Google Sheets or an internal database to store and generate sales and inventory reports.

  🚀 Installation & Setup

  Configure Credentials

  Set up API credentials for payment gateways and inventory management tools.

  Import Workflow

  Import the workflow into your automation platform (e.g., n8n, Zapier).

  Link POS system, payment gateway, and inventory management systems.

  Test & Run

  Process a test order to ensure that data flows correctly through each step.

  Verify that inventory updates and reports are generated as expected.

  ⚠ Important

  Data Privacy – Ensure compliance with data protection regulations (e.g., GDPR, PCI DSS) when handling customer payment and order data.

  System Downtime – Monitor system performance to ensure that the workflow runs without disruptions during peak hours.

  Summary
  This restaurant POS automation workflow integrates order management, payment processing, inventory updates, and real-time reporting, enabling efficient restaurant operations. Whether you are running a single location or a chain of restaurants, this solution streamlines daily tasks, reduces errors, and provides valuable insights, saving time and improving customer satisfaction. 🚀

nodes_used:
  ["n8n-nodes-base.googleSheets","n8n-nodes-base.code","@n8n/n8n-nodes-langchain.agent","@n8n/n8n-nodes-langchain.lmChatOpenAi","@n8n/n8n-nodes-langchain.memoryBufferWindow","@n8n/n8n-nodes-langchain.toolWorkflow","@n8n/n8n-nodes-langchain.informationExtractor"]

workflow_json:
  {"meta":{"instanceId":"d73282515b90623d4a8783919a2d772c706425d649e1512792f37ac51e07e4a8"},"nodes":[{"id":"62b3c7cb-1993-44f1-8b86-38a34ca1d029","name":"Information Extractor","type":"@n8n/n8n-nodes-langchain.informationExtractor","position":[-200,500],"parameters":{"text":"={{ $json.query }}","options":{},"schemaType":"fromJson","jsonSchemaExample":"{\n  \"name\": \"Information Extractor\",\n  \"type\": \"n8n-nodes-base.informationExtractor\",\n  \"parameters\": {\n    \"extract\": [\n      {\n        \"name\": \"items\",\n        \"pattern\": \"(latte|coffee|tea|cappuccino)\"\n      },\n      {\n        \"name\": \"quantity\",\n        \"pattern\": \"\\\\d+\"\n      },\n      {\n        \"name\": \"table\",\n        \"pattern\": \"table number (\\\\d+)\"\n      }\n    ]\n  }\n}\n"},"typeVersion":1},{"id":"75883f27-af58-4791-9e1a-a70b83e1cead","name":"OpenAI Chat Model1","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","position":[-180,740],"parameters":{"model":{"__rl":true,"mode":"list","value":"gpt-4o-mini"},"options":{}},"credentials":{"openAiApi":{"id":"OizdHUANhz9NIHyd","name":"OpenAi account"}},"typeVersion":1.2},{"id":"aeefdd4b-bf7d-4824-97d8-0afc356fb7d6","name":"If","type":"n8n-nodes-base.if","position":[120,540],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"8a5dda0c-a567-4305-83a3-68d6fb573dd3","operator":{"type":"array","operation":"empty","singleValue":true},"leftValue":"={{ $json.output.parameters.extract }}","rightValue":""}]}},"typeVersion":2.2},{"id":"9e3f8a1b-ccd8-4f4d-91cb-b99cc46f412f","name":"Google Sheets","type":"n8n-nodes-base.googleSheets","position":[840,420],"parameters":{"columns":{"value":{"Item":"={{ $json.item }}","Quantity":"={{ $json.quantity }}","Table No":"={{ $json.table }}","Timestamp":"={{ $now }}"},"schema":[{"id":"Timestamp","type":"string","display":true,"required":false,"displayName":"Timestamp","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Table No","type":"string","display":true,"required":false,"displayName":"Table No","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Item","type":"string","display":true,"required":false,"displayName":"Item","defaultMatch":false,"canBeUsedToMatch":true},{"id":"Quantity","type":"string","display":true,"required":false,"displayName":"Quantity","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/16fXaxEcfnq_-oif9tp94-3uTeHSFWoSnuBPNTljuW-k/edit#gid=0","cachedResultName":"Order log"},"documentId":{"__rl":true,"mode":"url","value":"https://docs.google.com/spreadsheets/d/16fXaxEcfnq_-oif9tp94-3uTeHSFWoSnuBPNTljuW-k/edit?usp=sharing"}},"credentials":{"googleSheetsOAuth2Api":{"id":"0RSJGMBcFzxY9GkS","name":"Google Sheets account"}},"typeVersion":4.5},{"id":"4cc1818f-1585-42e1-a111-7b55557aebcb","name":"Code","type":"n8n-nodes-base.code","position":[380,560],"parameters":{"language":"python","pythonCode":"# Input from n8n\ninput_data = items\n\n# Get the extracted list\nextract_data = input_data[0].get('json', {}).get('output', {}).get('parameters', {}).get('extract', [])\n\n# Prepare variables\norder_items = []\ntable_number = None\n\n# Separate entries by type\nitems_list = []\nquantities = []\n\n# Parse all entries\nfor entry in extract_data:\n    if entry['name'] == 'table number':\n        table_number = entry['pattern']\n    elif entry['name'] == 'item':\n        items_list.append(entry['pattern'])\n    elif entry['name'] == 'quantity':\n        quantities.append(int(entry['pattern']))\n\n# Pair items and quantities\nfor i in range(len(items_list)):\n    item_data = {\n        'item': items_list[i],\n        'quantity': quantities[i] if i < len(quantities) else None,\n        'table': table_number\n    }\n    order_items.append(item_data)\n\n# Set final output\noutput = [{'json': item} for item in order_items]\n\nreturn output"},"typeVersion":2},{"id":"a92d2745-148b-4e2a-b8f7-82d3993ff34f","name":"Loop Over Items","type":"n8n-nodes-base.splitInBatches","position":[620,500],"parameters":{"options":{}},"typeVersion":3},{"id":"aea89e6c-37a9-4859-adc8-b7e449701503","name":"Replace Me","type":"n8n-nodes-base.noOp","position":[800,660],"parameters":{},"typeVersion":1},{"id":"b31dba52-b27e-4267-be32-a7730b4d08a8","name":"No Operation, do nothing","type":"n8n-nodes-base.noOp","position":[440,400],"parameters":{},"typeVersion":1},{"id":"d7f9a381-6bc2-44d0-81ac-6e0fbe77d70a","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-260,220],"parameters":{"color":3,"width":340,"height":680,"content":"## JSON PARSER\n\n1.converts the textual data final order like\nitem name \nquantity \nand table name in a json.\n\n2.if the data doesn't include the above it returns null."},"typeVersion":1},{"id":"acc7a528-f767-4576-b08d-6fc386f57648","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[100,220],"parameters":{"color":2,"width":460,"height":680,"content":"## Refine/Split the jsons into multiple items\n\nIf the data from previous item is not null the custom code block splits the data into multiple json items in a list."},"typeVersion":1},{"id":"857a3102-f5e1-4db5-afb4-154544414701","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[580,220],"parameters":{"color":4,"width":440,"height":680,"content":"## Send each item as a record in Google sheet\n\n\n**Each item is looped over and produce a batch of 1 item and appended as row in sheet with timestamp.\n"},"typeVersion":1},{"id":"a1ff2b0f-0b48-4ea2-8121-4e2d72197ef7","name":"Triggered on Restaurant Chat workflow","type":"n8n-nodes-base.executeWorkflowTrigger","position":[-440,500],"parameters":{"inputSource":"passthrough"},"typeVersion":1.1},{"id":"8689b773-a1c4-4de4-a66e-fab8c9eb6244","name":"When chat message received","type":"@n8n/n8n-nodes-langchain.chatTrigger","position":[-140,-280],"webhookId":"d931c4a7-02f5-4359-918f-7ad3fae7b144","parameters":{"public":true,"options":{}},"typeVersion":1.1},{"id":"de310ce2-3868-4a0f-aa9b-38253e75dbda","name":"AI Agent","type":"@n8n/n8n-nodes-langchain.agent","position":[100,-260],"parameters":{"options":{"systemMessage":"\n\nYou are a polite and efficient restaurant assistant.\n\nYour job is to take customer orders, verify the order details, correct any mistakes, and confirm the order.\n\nFollow these steps:\n\nGreeting and Asking for the Order\n\nIf the customer greets you (e.g., \"Hello\", \"Hi\", \"Good evening\"), respond with:\n\n\"Hello! How can I assist you today? What would you like to order?\"\n\nOrder Parsing and Understanding\n\nAccept orders in flexible formats, such as:\n\n\"1 latte, 2 coffee, table number 5\"\n\n\"latte 2, pepsi 1, table 3\"\n\n\"1 cappucino\"\n\n\"1 tea table no 4\"\n\nYour goal is to extract the following:\n\nItem names (e.g., latte, coffee, chocolate, tea, pepsi)\n\nQuantities (must be numeric)\n\nTable number (must be numeric)\n\nVerify and Handle Missing or Incorrect Information\n\nFor each item in the order:\n\nIf the item name is missing, respond:\n\"Sorry, the item name is missing. What would you like to order?\"\n\nIf the quantity is missing, respond:\n\"How many [item] would you like?\"\n\nIf the table number is missing, respond:\n\"Could you please provide a table number?\"\n\nIf there are spelling mistakes in the item name, suggest corrections. Example:\n\"Did you mean chocolate instead of chocolat? Please confirm.\"\n\nUse fuzzy matching to detect common variations and typos.\n\nFinal Confirmation\n\nOnce all necessary details are collected, present an order summary like this:\n\nHere’s your order summary:\n\n1 latte\n\n2 coffee\n\nTable number: 5\nShall I confirm this order?\n\nOn Confirmation: Use the Tool\n\nWhen the user confirms, use the tool ConfirmOrder to send the final confirmation message as plain text in this format:\n\nThank you for confirming! Your order will be prepared shortly. Enjoy your time with us!\n\nOrder details are following:\nitem quantity\nlatte 1\ncoffee 2\n\nAdded to table number 5\n\nEnsure numeric values (quantities and table numbers) are correctly extracted, even if they appear at the start or end. Always confirm with the user if there is any uncertainty.\n\n\n\n\n\n\n\n\n"}},"typeVersion":1.9},{"id":"9dda45ee-0a92-448c-8a7e-8daa99282cda","name":"OpenAI Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","position":[-20,20],"parameters":{"model":{"__rl":true,"mode":"list","value":"gpt-4o-mini"},"options":{"responseFormat":"text"}},"credentials":{"openAiApi":{"id":"OizdHUANhz9NIHyd","name":"OpenAi account"}},"typeVersion":1.2},{"id":"0c0189d5-8fb4-4679-b2e2-221a3e2a4c88","name":"Call n8n Workflow Tool","type":"@n8n/n8n-nodes-langchain.toolWorkflow","position":[360,20],"parameters":{"workflowId":{"__rl":true,"mode":"list","value":"wgaJ0eJQtYA8oKSC","cachedResultName":"Restaurant POS workflow"},"description":"This tool sends the text output generated by the AI Agent node to another n8n workflow for additional handling or automation.","workflowInputs":{"value":{},"schema":[],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false}},"notesInFlow":false,"typeVersion":2.2},{"id":"9292db7f-6ffc-486e-b31a-bcbd6ef7ab98","name":"Last 5 conversations Memory","type":"@n8n/n8n-nodes-langchain.memoryBufferWindow","position":[140,40],"parameters":{},"typeVersion":1.3},{"id":"2782d5b6-d33b-4c89-ac79-90bf380f0828","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[60,-380],"parameters":{"width":340,"height":300,"content":"## Restaurant Order Chat bot\n** It chats with the user and refines the order for the pos system in another workflow."},"typeVersion":1},{"id":"7c298718-e9e3-40d3-a612-94c578bd3100","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[500,-20],"parameters":{"color":5,"content":"## Call the subworkflow\nit passes the data to the subworkflow for further process\n"},"typeVersion":1}],"pinData":{},"connections":{"If":{"main":[[{"node":"No Operation, do nothing","type":"main","index":0}],[{"node":"Code","type":"main","index":0}]]},"Code":{"main":[[{"node":"Loop Over Items","type":"main","index":0}]]},"AI Agent":{"main":[[]]},"Replace Me":{"main":[[{"node":"Loop Over Items","type":"main","index":0}]]},"Loop Over Items":{"main":[[{"node":"Google Sheets","type":"main","index":0}],[{"node":"Replace Me","type":"main","index":0}]]},"OpenAI Chat Model":{"ai_languageModel":[[{"node":"AI Agent","type":"ai_languageModel","index":0}]]},"OpenAI Chat Model1":{"ai_languageModel":[[{"node":"Information Extractor","type":"ai_languageModel","index":0}]]},"Information Extractor":{"main":[[{"node":"If","type":"main","index":0}]]},"Call n8n Workflow Tool":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]},"When chat message received":{"main":[[{"node":"AI Agent","type":"main","index":0}]]},"Last 5 conversations Memory":{"ai_memory":[[{"node":"AI Agent","type":"ai_memory","index":0}]]},"Triggered on Restaurant Chat workflow":{"main":[[{"node":"Information Extractor","type":"main","index":0}]]}}}


