{
  "name": "LinkedIn Growth & Intelligence Agent",
  "nodes": [
    {
      "parameters": {
        "chatId": "={{ $('Prep - Telegram').item.json.chat_id }}",
        "text": "=\ud83d\udc4b *Hey! Welcome to LinkedIn Growth & Intelligence Agent*\n\n\ud83c\udfaf *What I do*  \nI track public LinkedIn company data in real time and turn it into actionable insights for:  \n\u2022 \ud83d\udd0e Smart B2B prospecting (opportunity scoring & next best actions)  \n\u2022 \ud83d\udcca Competitive intelligence (hiring spikes, leadership changes, company pivots)  \n\u2022 \ud83d\udca1 Content recommendations (top engaging posts & ideas you can reuse)  \n\n\u2728 *Commands*  \n\u2022 /prospect \u2192 analyze a LinkedIn company URL and score opportunities  \n\u2022 /watch \u2192 start monitoring competitors and get alerts on key changes  \n\u2022 /content \u2192 get weekly trending content + new post ideas  \n\u2022 /clear \u2192 reset your current selection  \n\u2022 /help \u2192 show usage tips  \n\n\u2139\ufe0f *Good to know*  \nOnly public LinkedIn data is processed. Insights are delivered straight to Slack/Telegram or your CRM.\n",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        -1248,
        640
      ],
      "id": "e1543155-4cd0-4476-b53d-816b1d23bb35",
      "name": "/start",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "chatId": "={{ $('Prep - Telegram').item.json.chat_id }}",
        "text": "=\ud83e\udd14 *How to use LinkedIn Growth & Intelligence Agent*\n\n\ud83d\udd0e *Step 1 \u2014 Send me a LinkedIn company URL*  \nJust type or paste one a public LinkedIn company profiles, like:  \nhttps://www.linkedin.com/company/openai/  \nhttps://www.linkedin.com/company/nvidia/  \n\n\ud83d\udee0\ufe0f *Step 2 \u2014 Commands*  \n\u2022 /prospect \u2192 analyze the company you sent and score opportunities  \n\u2022 /content \u2192 get trending posts & ideas for your own LinkedIn content  \n\u2022 /clear \u2192 reset your current company list  \n\u2022 /help \u2192 show this help message again  \n\n\ud83d\udcca *What you\u2019ll get*  \nFor each company, I\u2019ll provide:  \n- Size, industry & followers  \n- Hiring activity & growth signals  \n- Engagement trends on recent posts  \n- A *business opportunity score* + recommended next actions  \n\n\u2139\ufe0f *Pro tip*  \nOnly public LinkedIn data is used. Perfect for *B2B prospecting, competitive intelligence, and content strategy*.\n",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        -1472,
        1168
      ],
      "id": "616e5d58-0acc-4ffd-b700-5dc93bde4ebc",
      "name": "/help",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "chatId": "={{ $('Prep - Telegram').item.json.chat_id }}",
        "text": "=\ud83e\uddf9 *Selection cleared* \n\nSend me new LinkedIn company or profile URL, then use:  \n\n\u2022 /prospect \u2192 analyze & score   \n\u2022 /content \u2192 get content ideas\n",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        -1248,
        832
      ],
      "id": "ba68206f-6480-4d91-a315-6e81d350fc40",
      "name": "/clear",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "765ed8db-7152-4c40-87b8-e0b9cfd3e80f",
              "name": "chat_id",
              "value": "={{ $json.message.chat.id }}",
              "type": "string"
            },
            {
              "id": "6afe501f-5edc-4c26-b482-f62748f2b245",
              "name": "text",
              "value": "={{ $json.message.text }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -2592,
        1072
      ],
      "id": "6618de56-7422-4a27-95df-7568eab561f5",
      "name": "Prep - Telegram"
    },
    {
      "parameters": {
        "operation": "upsert",
        "schema": {
          "__rl": true,
          "mode": "list",
          "value": "public"
        },
        "table": {
          "__rl": true,
          "value": "linkedin_selection",
          "mode": "list",
          "cachedResultName": "linkedin_selection"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "chat_id": "={{ $json.chat_id }}",
            "urls": "={{ $json.urls }}"
          },
          "matchingColumns": [
            "chat_id"
          ],
          "schema": [
            {
              "id": "chat_id",
              "displayName": "chat_id",
              "required": true,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "urls",
              "displayName": "urls",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "array",
              "canBeUsedToMatch": false
            },
            {
              "id": "updated_at",
              "displayName": "updated_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "dateTime",
              "canBeUsedToMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        -1920,
        1168
      ],
      "id": "7f59afeb-7a8f-4446-9500-70f9d1347b4b",
      "name": "Upsert LinkedIn URLs",
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "deleteTable",
        "schema": {
          "__rl": true,
          "mode": "list",
          "value": "public"
        },
        "table": {
          "__rl": true,
          "value": "linkedin_selection",
          "mode": "list",
          "cachedResultName": "linkedin_selection"
        },
        "deleteCommand": "delete",
        "where": {
          "values": [
            {
              "column": "chat_id",
              "value": "={{ $('Prep - Telegram').item.json.chat_id }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        -1472,
        832
      ],
      "id": "3f1ef9ab-a8af-415e-99d1-c3dd88de751d",
      "name": "Delete stored chat",
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $('Prep - Telegram').item.json.text }}",
                    "rightValue": "/start",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "a26ffeca-a4f9-43ef-83c7-0c6c0adaa733"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "start"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "9f7c3717-89b2-4da6-8f9b-6893921eaea2",
                    "leftValue": "={{ $('Prep - Telegram').item.json.text }}",
                    "rightValue": "/clear",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "name": "filter.operator.equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "clear"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "64c4d192-61dc-4886-b8e0-16628b3ecada",
                    "leftValue": "={{ $('Prep - Telegram').item.json.text }}",
                    "rightValue": "/help",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "name": "filter.operator.equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "help"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "5ae6aa3e-2b59-4fd5-8688-abc31697bd5c",
                    "leftValue": "={{ $('Prep - Telegram').item.json.text }}",
                    "rightValue": "",
                    "operator": {
                      "type": "string",
                      "operation": "notEmpty",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "default"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        -1696,
        1040
      ],
      "id": "1dd96b17-8160-4c87-9b50-031d3aeadcc7",
      "name": "Check for global commands"
    },
    {
      "parameters": {
        "chatId": "={{ $('Prep - Telegram').item.json.chat_id }}",
        "text": "=\ud83d\udc4b *Welcome back!*  \n\nI found *{{ $json.urls.length }} LinkedIn URL(s)* already saved for you.  \n\nYou can now:  \n\u2022 `/prospect` \u2192 analyze and score them  \n\u2022 `/watch` \u2192 start monitoring for changes \n\u2022 `/content` \u2192 get top posts & new ideas  \n\nOr send new LinkedIn URLs to update your selection.",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        -1248,
        448
      ],
      "id": "6dc20f46-b87c-4e70-b051-d80e9d5356c4",
      "name": "welcome-back",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "34043973-4b4f-4b67-9989-0dd5c3af105a",
              "leftValue": "={{ $json.urls.urls }}",
              "rightValue": "",
              "operator": {
                "type": "array",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -1472,
        448
      ],
      "id": "2a9b258e-375c-469c-a085-21f821053396",
      "name": "User has set URLS"
    },
    {
      "parameters": {
        "chatId": "={{ $('Prep - Telegram').item.json.chat_id }}",
        "text": "=Please send public LinkedIn company URL first, then /prospect.",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        -1248,
        1456
      ],
      "id": "5931c1f8-00a1-44bc-9225-bf3f7b444d6d",
      "name": "No URLs set by user",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Code \u00e0 utiliser dans un n\u0153ud Code n8n\nconst inputData = $('Has URLs Stored').all();\n\nconst result = inputData.map(item => {\n  const data = item.json;\n  \n  // Transformer le tableau d'URLs en tableau d'objets\n  const urlObjects = data.urls.map(url => ({\n    url: url\n  }));\n  \n  return {\n    json: {\n      urls: urlObjects\n    }\n  };\n});\n\nreturn result;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1024,
        1216
      ],
      "id": "1c0f5348-d845-400a-9504-dc36bfbafa69",
      "name": "Code"
    },
    {
      "parameters": {
        "chatId": "={{ $('Prep - Telegram').item.json.chat_id }}",
        "text": "=\ud83d\udd0e Analyzing {{$json.urls.length}} LinkedIn URL(s)\u2026 I\u2019ll send results here.",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        -1248,
        1216
      ],
      "id": "2a9db65c-526a-4931-b94f-29df84a29c1a",
      "name": "Send Analyzing",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "b935ae85-a3d7-4cfb-a545-bbc92bee3cb4",
              "leftValue": "={{ $json.urls }}",
              "rightValue": "",
              "operator": {
                "type": "array",
                "operation": "empty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -2144,
        1072
      ],
      "id": "986828da-f58e-424a-bc7e-5ef0727baabd",
      "name": "If"
    },
    {
      "parameters": {
        "operation": "select",
        "schema": {
          "__rl": true,
          "mode": "list",
          "value": "public"
        },
        "table": {
          "__rl": true,
          "value": "linkedin_selection",
          "mode": "list",
          "cachedResultName": "linkedin_selection"
        },
        "limit": 1,
        "where": {
          "values": [
            {
              "column": "chat_id",
              "value": "={{ $json.chat_id }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        -1920,
        976
      ],
      "id": "0f41e7e3-4de2-4938-8ec6-5b424c0b679e",
      "name": "Select rows from a table",
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "workflowInputs": {
          "values": [
            {
              "name": "snapshot_id"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.1,
      "position": [
        -2816,
        2160
      ],
      "id": "956eadf4-cb21-4b8b-a294-89eafeb60e7e",
      "name": "BrightData Scraper Sub-Workflow"
    },
    {
      "parameters": {
        "resource": "webScrapper",
        "operation": "triggerCollectionByUrl",
        "dataset_id": {
          "__rl": true,
          "value": "gd_l1vikfnt1wgvvqz95w",
          "mode": "list",
          "cachedResultName": "LinkedIn company information"
        },
        "urls": "={{ $json.urls.toJsonString() }}",
        "requestOptions": {}
      },
      "type": "@brightdata/n8n-nodes-brightdata.brightData",
      "typeVersion": 1,
      "position": [
        -800,
        1216
      ],
      "id": "fc279645-5c1f-4651-b04c-0eab1e09c97a",
      "name": "Initiate batch extraction for Companies",
      "credentials": {
        "brightdataApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "workflowId": {
          "__rl": true,
          "value": "yNGc5OuaemnrXMaQ",
          "mode": "list",
          "cachedResultName": "LinkedIn Growth & Intelligence Agent"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {},
          "matchingColumns": [],
          "schema": [
            {
              "id": "snapshot_id",
              "displayName": "snapshot_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "canBeUsedToMatch": true,
              "type": "string",
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "mode": "each",
        "options": {
          "waitForSubWorkflow": true
        }
      },
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.2,
      "position": [
        -576,
        1216
      ],
      "id": "a20d7eba-47e5-4c40-90b1-c4f39d20df2d",
      "name": "Execute Bright Data Sub workflow Loop for Companies"
    },
    {
      "parameters": {
        "options": {
          "reset": true
        }
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        -2592,
        2160
      ],
      "id": "c02bb61b-82e4-4c23-bf1c-86f8231d8f17",
      "name": "Loop Over Items"
    },
    {
      "parameters": {
        "resource": "webScrapper",
        "operation": "monitorProgressSnapshot",
        "snapshot_id": "={{ $json.snapshot_id }}",
        "requestOptions": {}
      },
      "type": "@brightdata/n8n-nodes-brightdata.brightData",
      "typeVersion": 1,
      "position": [
        -2368,
        2096
      ],
      "id": "a27175df-9ca6-43aa-9d28-d861818847b2",
      "name": "Check the status of a batch extraction",
      "credentials": {
        "brightdataApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        -2144,
        2096
      ],
      "id": "cc2cfcee-f81e-4f15-83b1-9bcd53ed3883",
      "name": "Wait 5 seconds"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "aaf6fec4-cbe7-4fa3-94c7-d169dcb83ac1",
              "leftValue": "={{ $('Check the status of a batch extraction').item.json.status }}",
              "rightValue": "ready",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -1920,
        2096
      ],
      "id": "49277942-0704-4638-872d-683fb92639d5",
      "name": "Check if Batch ready"
    },
    {
      "parameters": {
        "resource": "webScrapper",
        "operation": "downloadSnapshot",
        "snapshot_id": "={{ $('Loop Over Items').item.json.snapshot_id }}",
        "requestOptions": {}
      },
      "type": "@brightdata/n8n-nodes-brightdata.brightData",
      "typeVersion": 1,
      "position": [
        -1696,
        2048
      ],
      "id": "3e583d1f-1d7d-4685-b90b-2bfae047cdbc",
      "name": "Download the snapshot content",
      "credentials": {
        "brightdataApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.noOp",
      "name": "Check Snapshot Again for Success",
      "typeVersion": 1,
      "position": [
        -1696,
        2256
      ],
      "id": "d445bf9d-efed-4d25-b053-411f10185416"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const cleanInt = (s) =>\n  s == null ? null : (Number(String(s).replace(/[^0-9]/g, \"\")) || 0);\n\nconst asStr = (v, max = 4000) =>\n  (v == null ? \"\" : String(v)).slice(0, max);\n\nconst source = $json;\nconst p0 = source || {};\n\nconst followers = cleanInt(p0.followers);\nconst sizeRange = p0.company_size || p0.size_range || null;\n\n// Map des posts depuis \"updates\" Bright Data\nconst posts = Array.isArray(p0.updates)\n  ? p0.updates.map(u => ({\n      id: u.post_id ?? null,\n      date: u.date ?? null,\n      text: asStr(u.text, 1500),\n      likes: cleanInt(u.likes_count),\n      comments: cleanInt(u.comments_count),\n      post_url: u.post_url ?? null\n    }))\n  : [];\n\nlet jobsOpenProxy = null;\nif (typeof p0.additional_information === \"string\") {\n  const m =\n    p0.additional_information.match(/(\\d+)\\s+open jobs/i) ||\n    p0.additional_information.match(/\\((\\d+)\\s+open jobs\\)/i);\n  jobsOpenProxy = m ? Number(m[1]) : null;\n}\n\nreturn {\n  sourceUrl: p0.input?.url || p0.url,\n  name: p0.name,\n  linkedin_url: p0.url,\n  industry: p0.industries || p0.specialties || null,\n  size_range: sizeRange,\n  employee_count_est: cleanInt(p0.employees_in_linkedin),\n  followers,\n  description: asStr(p0.description),\n  last_posts: posts,\n  jobs_open_proxy: jobsOpenProxy,\n  executives: p0.employees || [],\n  metrics: {},\n};\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        96,
        896
      ],
      "id": "912a7c77-1955-4799-8b7c-efd579a95f54",
      "name": "Normalizer"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const j = $json;\nconst followers = j.followers || 1;\nconst posts = Array.isArray(j.last_posts) ? j.last_posts : [];\n\nconst now = new Date();\nconst D30 = 1000 * 60 * 60 * 24 * 30;\n\nconst recentPosts = posts.filter(p => {\n  if (!p?.date) return false;\n  const t = Date.parse(p.date);\n  return Number.isFinite(t) && (now - t) <= D30;\n});\n\n// fallback: si aucun post horodat\u00e9 dans 30j, on prend les 10 plus r\u00e9cents\nconst postsForER = recentPosts.length\n  ? recentPosts\n  : posts.slice(0, 10);\n\nconst avgER = postsForER.length\n  ? postsForER.reduce((s, p) => {\n      const l = Number(p.likes || 0);\n      const c = Number(p.comments || 0);\n      const sh = Number(p.shares || 0);\n      return s + (l + c + sh) / followers;\n    }, 0) / postsForER.length\n  : 0;\n\n// Sans la liste des jobs dat\u00e9s 30j, on renvoie 0 par d\u00e9faut\n// (on conserve jobs_open_proxy pour d'autres features/affichage)\nconst hiring30 = 0;\n\nreturn {\n  ...j,\n  metrics: {\n    ...j.metrics,\n    avg_eng_rate_30d: avgER,\n    jobs_last_30d: hiring30,\n  },\n  features: {\n    // Fit taille: favorise 100\u20131000 employ\u00e9s si employee_count_est dispo\n    size_fit: (() => {\n      const x = j.employee_count_est || 0;\n      if (!x) return 0.5;\n      if (x >= 100 && x <= 1000) return 1;\n      return Math.max(0.2, Math.exp(-Math.abs(x - 500) / 500));\n    })(),\n    growth_headcount: 0.5, // \u00e0 am\u00e9liorer avec historique\n    hiring_volume: Math.min(1, (j.jobs_open_proxy || 0) / 20),\n    icp_relevance: 0.6,    // \u00e0 ajuster selon ton ICP/mots-cl\u00e9s\n    engagement_activity: Math.min(1, (postsForER.length / 8) * 0.5 + avgER * 0.5),\n    leadership_change: 0,\n    risk_noise: (j.followers < 500 ? 0.6 : 0.2),\n  }\n};\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        320,
        896
      ],
      "id": "04df0561-34de-4ea1-bb77-71caad7b994b",
      "name": "Metrics"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=You are a B2B sales intelligence assistant.  \nAnalyze the company profile below and return a **strict JSON object**, matching the schema exactly.  \nUse the data provided to generate a factual summary, identify post-related insights, and recommend sales actions.  \nAdapt dynamically to the data (e.g., if no job postings, reflect that accordingly).\n\n---\n\nCompany data:\n- Name: {{$json.name}}\n- Industry: {{$json.industry}}\n- Size: {{$json.employee_count_est}} employees ({{$json.size_range}})\n- Followers: {{$json.followers}}\n- Jobs posted in last 30 days: {{$json.metrics.jobs_last_30d}}\n- Average engagement rate (30 days): {{$json.metrics.avg_eng_rate_30d}}\n- Description: {{$json.description}}\n- Recent posts: {{ JSON.stringify($json.last_posts.slice(0,3)) }}\n\n---\n\nSchema (valid JSON only, no markdown, no comments):\n\n{\n  \"summary\": [\n    \ud83e\udde0 *Industry*: {{$json.industry}}  \n    \ud83d\udc65 *Employees*: {{$json.employee_count_est}} (size range: {{$json.size_range}})  \n    \ud83d\udce3 *Followers*: {{$json.followers}}  \n    \ud83d\udcca *Engagement Rate (30d)*: {{$json.metrics.avg_eng_rate_30d}}\n    \ud83d\udcbc *Jobs (30 d)*: {{$json.metrics.jobs_last_30d}}\n  ],\n  \"post_insights\": [\n    {\n      \"key_themes\": [...],\n      \"summary\": \"...\",\n      \"potential_opportunity\": \"...\"\n    }\n  ],\n  \"next_best_actions\": [\n    {\n      \"action\": \"...\",\n      \"description\": \"...\"\n    },\n    {\n      \"action\": \"...\",\n      \"description\": \"...\"\n    },\n    {\n      \"action\": \"...\",\n      \"description\": \"...\"\n    }\n  ]\n}\n\n---\n\nInstructions:\n- Replace all placeholders with actual values from the input JSON.\n- In the `\"summary\"`, write one sentence about job activity:\n  - If `jobs_last_30d > 0`: mention it as active hiring.\n  - If `jobs_last_30d = 0`: mention absence of job postings and suggest possible internal focus.\n- In the `\"summary\"`, the value for `\"avg_eng_rate_30d\"` must be **rounded to 2 decimal places**.\n- `\"post_insights\"`: summarize themes in recent posts (e.g., product launches, geographic expansion, hiring signals, use cases).\n- `\"next_best_actions\"`: must be concrete and sales-relevant (LinkedIn outreach, email, engagement strategy, etc.).\n- Do not include any explanations, markdown, or formatting. Return JSON only.\n",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "You are a B2B Sales Intelligence Assistant.\n\nYour mission is to analyze raw company profiles (provided as structured JSON input) and return **only a JSON object**, strictly following a predefined schema.\n\n---\n\n\ud83c\udfaf Core task:\n- Extract actionable insights useful for B2B sales or prospecting.\n- Adapt dynamically to the presence or absence of data (e.g., hiring activity, post frequency).\n- Always include: a factual summary, key insights from recent social posts, and 3 high-value sales actions.\n\n---\n\n\ud83e\udde0 Behavior Rules:\n- Never ask questions.\n- Never return markdown, explanations, HTML, or comments.\n- Output **JSON only**. No introductory or closing sentences.\n- If a data field is missing or null, adapt the language accordingly (e.g., \"no recent job postings\").\n- Keep tone professional, concise, and neutral.\n- Output should be ready for use in a CRM, sales dashboard, or automation tool.\n\n---\n\n\ud83d\udcca Output JSON Schema:\nYour output must follow this structure, every time:\n\n1. `\"summary\"`: factual, readable overview based on the input.\n2. `\"post_insights\"`: extract key themes and signals from the latest social posts (launches, growth, partnerships, topics).\n3. `\"next_best_actions\"`: 3 actionable recommendations for sales outreach, tailored to the company's context.\n\n---\n\n\ud83e\udde9 Input Structure:\nEach request will provide:\n- A reminder of your role\n- A structured list of company data fields (name, size, industry, etc.)\n- A JSON schema defining the expected output\n- Instructions for dynamic formatting (e.g., conditional language based on hiring activity)\n\n---\n\n\ud83d\udeab Strict Do-Nots:\n- Never output \u201cHere is the JSON\u201d or similar.\n- Never include free-form explanation.\n- Never include markdown formatting or non-JSON content.\n\n---\n\n\ud83c\udfaf Goal:\nBe ready to integrate into Make.com, Slack, Airtable, Zapier, or a GPT-based assistant.  \nYour output must be 100% clean, reusable, and directly usable by a person or system.\n\n"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        544,
        800
      ],
      "id": "3fa484ae-1743-4177-b8d0-20fe71165cc3",
      "name": "AI Agent"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Scoring (n8n Function node)\n\n// 1) Pull canonical input from the Metrics node (has features + metrics)\nconst metricsNode = $('Metrics').first();\nif (!metricsNode) {\n  // Hard fail-safe: no Metrics node found\n  return {\n    json: {\n      error: \"Metrics node not found for scoring.\",\n      score: 0,\n      priority: \"C\"\n    }\n  };\n}\n\nconst canonical = metricsNode.json || {};\nconst features = canonical.features || {};\nconst nz = v => (v == null || v === '' ? 0 : Number(v));\n\nconst W = {\n  size_fit: 0.18,\n  growth_headcount: 0.22,\n  hiring_volume: 0.15,\n  icp_relevance: 0.20,\n  engagement_activity: 0.15,\n  leadership_change: 0.05,\n  risk_noise: -0.05\n};\n\n// 2) Compute score 's' from features\nlet s = 0;\ns += W.size_fit * nz(features.size_fit);\ns += W.growth_headcount * nz(features.growth_headcount);\ns += W.hiring_volume * nz(features.hiring_volume);\ns += W.icp_relevance * nz(features.icp_relevance);\ns += W.engagement_activity * nz(features.engagement_activity);\ns += W.leadership_change * nz(features.leadership_change);\ns += W.risk_noise * nz(features.risk_noise);\n\n// 3) Normalize to 0..100, set priority\nconst score = Math.max(0, Math.min(100, Math.round(100 * s)));\nconst priority = score >= 75 ? 'A' : score >= 50 ? 'B' : 'C';\n\n// 4) Get current branch item (likely holds the AI Agent output)\nconst current = $json || {};\nconst aiOutput = current.output || current; // supports either {output:{...}} or the object itself\n\n// 5) Build a clean 'input' payload from Metrics (avoid echoing 'output' if present)\nconst { output: _drop, ...inputPayload } = canonical;\n\n// 6) Return final unified object\nreturn {\n  json: {\n    input: inputPayload,   // original normalized data + metrics + features\n    output: aiOutput,      // AI Agent result\n    score,\n    priority\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        896,
        896
      ],
      "id": "75b19056-8a4f-4aa7-bd74-4c39c7b132f6",
      "name": "Scoring"
    },
    {
      "parameters": {
        "jsCode": "function filterCompanyUrls(chatData) {\n  // Check if input is valid and convert to array if needed\n  if (!chatData) {\n    console.log(\"No input data provided\");\n    return [];\n  }\n  \n  // Convert single object to array\n  let dataArray = Array.isArray(chatData) ? chatData : [chatData];\n  console.log(\"Processing data:\", dataArray);\n  \n  // Helper function to check if a URL is a company URL (more flexible patterns)\n  function isCompanyUrl(url) {\n    return url.includes('/company/');\n  }\n  \n  // Helper function to clean company URLs (remove everything after company name)\n  function cleanCompanyUrl(url) {\n    if (!url.includes('/company/')) return url;\n    \n    // Find the company part and extract just the base company URL\n    const companyMatch = url.match(/https?:\\/\\/[^\\/]+\\/company\\/[^\\/\\?#]+/);\n    return companyMatch ? companyMatch[0] : url;\n  }\n  \n  // Helper function to extract URLs from text\n  function extractUrls(text) {\n    if (typeof text !== 'string') return [];\n    const urlRegex = /https?:\\/\\/[^\\s]+/g;\n    const urls = text.match(urlRegex) || [];\n    console.log(\"Found URLs:\", urls);\n    const companyUrls = urls.filter(isCompanyUrl).map(cleanCompanyUrl);\n    console.log(\"Cleaned company URLs:\", companyUrls);\n    return companyUrls;\n  }\n  \n  // Group data by chat_id and collect company URLs\n  const result = {};\n  \n  dataArray.forEach(item => {\n    const chatId = item.chat_id;\n    const companyUrls = extractUrls(item.text);\n    \n    if (!result[chatId]) {\n      result[chatId] = {\n        chat_id: chatId,\n        urls: []\n      };\n    }\n    \n    // Add company URLs to the existing array\n    result[chatId].urls.push(...companyUrls);\n  });\n  \n  // Convert result object to array\n  return Object.values(result);\n}\n\nreturn filterCompanyUrls($input.first().json);\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -2368,
        1072
      ],
      "id": "fd4749a2-9ffd-4d82-b8fe-2b13c5cf6037",
      "name": "Extract URLs"
    },
    {
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Company Engagement Schema\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"summary\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"post_insights\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"key_themes\": {\n            \"type\": \"array\",\n            \"items\": { \"type\": \"string\" }\n          },\n          \"summary\": { \"type\": \"string\" },\n          \"potential_opportunity\": { \"type\": \"string\" }\n        },\n        \"required\": [\"key_themes\", \"summary\", \"potential_opportunity\"]\n      }\n    },\n    \"next_best_actions\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"action\": { \"type\": \"string\" },\n          \"description\": { \"type\": \"string\" }\n        },\n        \"required\": [\"action\", \"description\"]\n      }\n    }\n  },\n  \"required\": [\"summary\", \"next_best_actions\"]\n}\n"
      },
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [
        688,
        1024
      ],
      "id": "71069b27-216f-4bc8-8b58-200b42576bf4",
      "name": "Structured Output Parser"
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $('Prep - Telegram').item.json.text }}",
                    "rightValue": "/prospect",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "8d84603b-6201-4553-b55b-e001c7a3fe9f"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "prospect"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "0bf8c52b-6e4d-4901-91a2-c8ad9e563124",
                    "leftValue": "={{ $('Prep - Telegram').item.json.text }}",
                    "rightValue": "/content",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "name": "filter.operator.equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "content"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "1c5d74e3-1258-4afd-9e6c-563191165768",
                    "leftValue": "={{ $('Prep - Telegram').item.json.text }}",
                    "rightValue": "",
                    "operator": {
                      "type": "string",
                      "operation": "notEmpty",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "other"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        -352,
        1200
      ],
      "id": "b843904b-0ffe-4c1b-b898-532738411790",
      "name": "Switch"
    },
    {
      "parameters": {
        "jsCode": "const cleanInt = s => s==null ? null : (Number(String(s).replace(/[^0-9]/g,'')) || 0);\nconst asStr = (v, max=1500) => (v==null?'':String(v)).slice(0,max);\n\nconst json = $input.first().json;\n\n// Followers & URL\nconst followers = cleanInt(json.followers) || 0;\nconst F = followers > 0 ? followers : 1;\n\nconst postsRaw = Array.isArray(json.updates) ? json.updates : [];\n\n// Normalizerr by post + ER fallback (likes+comments+shares)/max(1,followers)\nconst posts = postsRaw.map(u => {\n  const likes = cleanInt(u.likes_count ?? u.likes) || 0;\n  const comments = cleanInt(u.comments_count ?? u.comments) || 0;\n\n  const date = u.date || null;\n  const er = (likes + comments) / F;\n\n  return {\n    id: u.post_id ?? u.id ?? null,\n    date,\n    text: asStr(u.text ?? u.text_html?.replace(/<[^>]*>/g,' ') ?? ''),\n    likes, comments,\n    engagement_rate: er,\n    post_url: u.post_url ?? u.url ?? null,\n    title: u.title ?? null,\n  };\n});\n\nreturn [{\n  json: {\n    company: json.name || json.title || 'Company',\n    followers: followers || null,\n    linkedin_url: json.url || json.input?.url || null,\n    posts\n  }\n}];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -128,
        1456
      ],
      "id": "3e0bc147-15d7-4430-8f6a-3b3c112554b2",
      "name": "Normalize Posts"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const now = Date.now(), D30 = 1000*60*60*24*30;\nconst data = $json;\n\nconst parseTs = (s) => {\n  const t = Date.parse(s || '');\n  return Number.isFinite(t) ? t : null;\n};\n\n// filter last 30 days ; fallback = keep last 10\nconst recent = (data.posts || []).filter(p => {\n  const t = parseTs(p.date);\n  return t && (now - t) <= D30;\n});\n\nconst candidates = recent.length ? recent : (data.posts || []).slice(0, 10);\n\nfunction keywords(s){\n  return (String(s||'').toLowerCase()\n    .replace(/[^a-z0-9\\s]/g,' ')\n    .split(/\\s+/)\n    .filter(w => w.length >= 4)\n    .slice(0,8));\n}\n\nconst used = new Set();\nconst top_posts = candidates\n  .slice()\n  .sort((a,b) => (b.engagement_rate||0) - (a.engagement_rate||0))\n  .filter(p => {\n    const key = (keywords(p.text).slice(0,2).join('|')) || 'misc';\n    if (used.has(key)) return false;\n    used.add(key);\n    return true;\n  })\n  .slice(0,3);\n\nreturn { json: {\n  ...data,\n  recent_window_days: 30,\n  top_posts,\n  has_posts: !!top_posts.length\n}};\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        96,
        1456
      ],
      "id": "2e3b533d-1baa-4fdd-bcd5-e073d8346ed1",
      "name": "Rank Posts"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Company: {{$json.company}}\nFollowers: {{$json.followers}}\nWindow (days): {{$json.recent_window_days}}\n\nTopPosts (JSON):\n{{ JSON.stringify($json.top_posts) }}\n\nTask:\n1. Extract 3\u20135 short insights about what performs in these posts (themes, formats, angles).\n2. Generate 3\u20135 LinkedIn post ideas inspired by those posts (no copy-paste).\n3. Each idea must include: title, hook, angle, 3 bullets, CTA.\n4. Return valid JSON only, matching this structure:\n\n\nAdd company, recent_window_days and top_posts on output\nIf you cannot extract insights from the posts, write: [\"No insights available.\"]\nIf you cannot generate post ideas, write 3 ideas based on company\u2019s context alone.\n\n",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "You are a B2B LinkedIn content strategist.\n\nYour role:\n- Read the company profile and its top-performing LinkedIn posts.\n- Extract 3\u20135 key content insights.\n- Generate 3\u20135 new LinkedIn post ideas based on what worked, without copying.\n\nEach idea includes:\n- title\n- hook (max 120 chars)\n- angle\n- 3 short bullets\n- CTA (call to action)\n\nTone: expert, concise, realistic for LinkedIn.\n\nIf input is limited, still generate 3 ideas using only company info.\n\n\u26a0\ufe0f Return JSON only (no markdown, no prose). \n"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        544,
        1200
      ],
      "id": "dba6068a-fc8f-43fb-b000-f2471e1b8098",
      "name": "AI Ideas Content"
    },
    {
      "parameters": {
        "chatId": "={{ $('Prep - Telegram').item.json.chat_id }}",
        "text": "=\u2139\ufe0f No recent public posts found.\nTry widening the window or adding more sources. You can still use /prospect for scoring.\n",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        608,
        1600
      ],
      "id": "e104f8a7-35d8-4eb0-84a5-457cfd6371a1",
      "name": "Fallback",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Content Generation Output\",\n  \"type\": \"object\",\n  \"required\": [\"input\", \"insights\", \"post_ideas\"],\n  \"properties\": {\n    \"input\": {\n      \"type\": \"object\",\n      \"required\": [\"company\", \"followers\", \"recent_window_days\", \"top_posts\"],\n      \"properties\": {\n        \"company\": { \"type\": \"string\" },\n        \"followers\": { \"type\": \"number\" },\n        \"recent_window_days\": { \"type\": \"number\" },\n        \"top_posts\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"object\",\n            \"required\": [\"id\", \"date\", \"text\", \"likes\", \"comments\", \"engagement_rate\", \"post_url\", \"title\"],\n            \"properties\": {\n              \"id\": { \"type\": \"string\" },\n              \"date\": { \"type\": \"string\", \"format\": \"date-time\" },\n              \"text\": { \"type\": \"string\" },\n              \"likes\": { \"type\": \"number\" },\n              \"comments\": { \"type\": \"number\" },\n              \"engagement_rate\": { \"type\": \"number\" },\n              \"post_url\": { \"type\": \"string\", \"format\": \"uri\" },\n              \"title\": { \"type\": \"string\" }\n            }\n          }\n        }\n      }\n    },\n    \"insights\": {\n      \"type\": \"array\",\n      \"minItems\": 1,\n      \"items\": { \"type\": \"string\" }\n    },\n    \"post_ideas\": {\n      \"type\": \"array\",\n      \"minItems\": 1,\n      \"items\": {\n        \"type\": \"object\",\n        \"required\": [\"title\", \"hook\", \"angle\", \"bullets\", \"cta\"],\n        \"properties\": {\n          \"title\": { \"type\": \"string\" },\n          \"hook\": { \"type\": \"string\", \"maxLength\": 120 },\n          \"angle\": { \"type\": \"string\" },\n          \"bullets\": {\n            \"type\": \"array\",\n            \"minItems\": 3,\n            \"maxItems\": 3,\n            \"items\": { \"type\": \"string\" }\n          },\n          \"cta\": { \"type\": \"string\" }\n        }\n      }\n    }\n  }\n}\n"
      },
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [
        688,
        1424
      ],
      "id": "2c41a141-dd96-4958-ac87-01ae8d08664a",
      "name": "Structured Output Parser1"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "ecacd325-3734-4558-bc7f-5ba8c27a2b80",
              "leftValue": "={{ $json.has_posts }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        320,
        1456
      ],
      "id": "65bdbe0b-4283-40a8-b0c2-055b789aaa8a",
      "name": "HasPost"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [
        560,
        1024
      ],
      "id": "372746e3-8b6e-4ed2-9ca3-26381a204568",
      "name": "OpenAI Chat Prospect",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [
        560,
        1424
      ],
      "id": "c7470e38-fafc-4ff2-bd45-0631150f0be5",
      "name": "OpenAI Chat Content",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1.2,
      "position": [
        -2816,
        1072
      ],
      "id": "913c6534-76aa-42d4-b24c-c506c3b19da8",
      "name": "Telegram Trigger",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "chatId": "={{ $('Prep - Telegram').item.json.chat_id }}",
        "text": "=\ud83d\udca1 *Content Digest \u2013 {{ $json.output.input.company }}*\n\n\ud83d\uddd3\ufe0f *Top Posts (last {{$json.output.input.recent_window_days}} days)*:\n{{ $json.output.input.top_posts.map((item) => {\n    return `\u2022 ${item.date.slice(0,10)}  ${item.text.slice(0,60)} \u2014 ER ${Number(item.engagement_rate).toFixed(3)}`\n}).join('\\n')}}\n\n\ud83d\udccc *Insights*:\n{{ $json.output.insights.map((item) => (`\u2022 ${item}`)).join('\\n')}}\n\n\n\ud83e\udde0 *Post Ideas*:\n{{ $json.output.post_ideas.map((item, index) => {\n    return `${index + 1} *${item.title}*\n  _Hook_: ${item.hook}\n  _Angle_: ${item.angle}\n  _Structure_:\n${item.bullets.map((item) => (`    \u2022 ${item}`)).join('\\n')}\n  _CTA_: ${item.cta}\n`\n}).join('\\n')}}\n\n",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        896,
        1296
      ],
      "id": "251a0199-416a-4c9d-85c4-9108ae744b89",
      "name": "Send Content message",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "chatId": "={{ $('Prep - Telegram').item.json.chat_id }}",
        "text": "=\ud83d\udccc *Company Profile Summary \u2013 {{ $('Normalizer').item.json.name }}*\n\n\u26a0\ufe0f *Priority Score*: *{{$json.priority}}*\n---\n\n{{ $json.output.summary.join('\\n') }}\n---\n\n\ud83c\udfaf *Recommended Next Actions*\n\n1\ufe0f\u20e3 *{{ $json.output.next_best_actions[0].action }}*\n{{ $json.output.next_best_actions[0].description }}\n\n2\ufe0f\u20e3 *{{ $json.output.next_best_actions[1].action }}*\n{{ $json.output.next_best_actions[1].description }}\n\n3\ufe0f\u20e3 *{{ $json.output.next_best_actions[2].action }}*\n{{ $json.output.next_best_actions[2].description }}\n\n\n",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1120,
        896
      ],
      "id": "2ee76108-e2d6-485c-bf5e-5122e537f958",
      "name": "Send Prospect message",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "34043973-4b4f-4b67-9989-0dd5c3af105a",
              "leftValue": "={{ $json.urls }}",
              "rightValue": "",
              "operator": {
                "type": "array",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -1472,
        1360
      ],
      "id": "043366fb-5e2e-4a05-81a9-837b19c2f75a",
      "name": "Has URLs Stored"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "DROP TABLE IF EXISTS linkedin_selection;\nCREATE TABLE IF NOT EXISTS linkedin_selection (\n  chat_id TEXT PRIMARY KEY,\n  urls text[],\n  updated_at TIMESTAMPTZ DEFAULT now()\n);",
        "options": {}
      },
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        -2432,
        1632
      ],
      "id": "d0549907-1157-4a5c-ba75-acf38c710b5d",
      "name": "Initialize database tables",
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "content": "## \ud83c\udfaf Purpose: Initialize database tables for the Growth Radar bot\n\n- Creates table `linkedin_selection` if it doesn\u2019t exist\n- Stores mapping: { chat_id, urls, last_updated }\n- Ensures session persistence across Telegram messages\n- Run once at workflow start (safe to re-run, idempotent)\n\n\u26a0\ufe0f Important: Only stores *public LinkedIn company URLs*",
        "height": 256,
        "width": 672,
        "color": 3
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -2928,
        1536
      ],
      "id": "72139119-3bfb-470a-8a25-904e5972b7ea",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## \ud83d\udd04 Purpose: Wait until Bright Data snapshot is ready\n\n- Polls snapshot status every 5s\n- If status != \"ready\" \u2192 loop continues\n- Once ready \u2192 downloads snapshot JSON\n- Ensures we always get complete & valid data\n\n\ud83d\udca1 Could be extended with exponential backoff",
        "height": 528,
        "width": 1472,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -2944,
        1952
      ],
      "id": "39f64c89-8e7c-41b1-b2ee-730474006db3",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "## \ud83e\udd16 Purpose: Transform Bright Data results into actionable insights\n\n- Uses Structured Output Parser with enforced JSON schema:\n  {\n    \"summary\": [string],\n    \"post_insights\": [ { \"text\": string, \"engagement_rate\": number } ],\n    \"next_best_actions\": [string]\n  }\n\n- Ensures LLM always returns machine-parseable JSON\n- Adds robustness: downstream nodes (Scoring, Telegram Output) consume clean data\n\n\u26a0\ufe0f Fallback handling: if schema fails, default empty arrays are used",
        "height": 2160,
        "width": 4336
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -2976,
        352
      ],
      "id": "54d807ac-0832-4e0b-af75-2bea2af17f31",
      "name": "Sticky Note2"
    }
  ],
  "connections": {
    "Prep - Telegram": {
      "main": [
        [
          {
            "node": "Extract URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upsert LinkedIn URLs": {
      "main": [
        [
          {
            "node": "Check for global commands",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Delete stored chat": {
      "main": [
        [
          {
            "node": "/clear",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for global commands": {
      "main": [
        [
          {
            "node": "User has set URLS",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Delete stored chat",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "/help",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Has URLs Stored",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "User has set URLS": {
      "main": [
        [
          {
            "node": "welcome-back",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "/start",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "/help": {
      "main": [
        []
      ]
    },
    "Code": {
      "main": [
        [
          {
            "node": "Initiate batch extraction for Companies",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Analyzing": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "Select rows from a table",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Upsert LinkedIn URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Select rows from a table": {
      "main": [
        [
          {
            "node": "Check for global commands",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "BrightData Scraper Sub-Workflow": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Initiate batch extraction for Companies": {
      "main": [
        [
          {
            "node": "Execute Bright Data Sub workflow Loop for Companies",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute Bright Data Sub workflow Loop for Companies": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Check the status of a batch extraction",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check the status of a batch extraction": {
      "main": [
        [
          {
            "node": "Wait 5 seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 5 seconds": {
      "main": [
        [
          {
            "node": "Check if Batch ready",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if Batch ready": {
      "main": [
        [
          {
            "node": "Download the snapshot content",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check Snapshot Again for Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Snapshot Again for Success": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalizer": {
      "main": [
        [
          {
            "node": "Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Metrics": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Scoring",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scoring": {
      "main": [
        [
          {
            "node": "Send Prospect message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract URLs": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Switch": {
      "main": [
        [
          {
            "node": "Normalizer",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Normalize Posts",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Normalizer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Posts": {
      "main": [
        [
          {
            "node": "Rank Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rank Posts": {
      "main": [
        [
          {
            "node": "HasPost",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Ideas Content": {
      "main": [
        [
          {
            "node": "Send Content message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser1": {
      "ai_outputParser": [
        [
          {
            "node": "AI Ideas Content",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "HasPost": {
      "main": [
        [
          {
            "node": "AI Ideas Content",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fallback",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Prospect": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Content": {
      "ai_languageModel": [
        [
          {
            "node": "AI Ideas Content",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Prep - Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has URLs Stored": {
      "main": [
        [
          {
            "node": "Send Analyzing",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No URLs set by user",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Initialize database tables": {
      "main": [
        []
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": "tl6MZUA9SY1FvR1b"
  },
  "versionId": "e651fd1c-6fde-4aea-b9d3-704e95ca2754",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "yNGc5OuaemnrXMaQ",
  "tags": [
    {
      "createdAt": "2025-08-20T14:43:39.486Z",
      "updatedAt": "2025-08-20T14:43:39.486Z",
      "id": "gdSMwmdgI9LAuEUK",
      "name": "Challenge"
    },
    {
      "createdAt": "2025-08-20T14:42:15.849Z",
      "updatedAt": "2025-08-20T14:42:15.849Z",
      "id": "lJrhCYT58pnNO3s6",
      "name": "n8n"
    },
    {
      "createdAt": "2025-08-03T20:18:57.239Z",
      "updatedAt": "2025-08-03T20:18:57.239Z",
      "id": "pSYRZNvJjRmhwDfL",
      "name": "LinkedIn"
    },
    {
      "createdAt": "2025-08-20T14:43:14.428Z",
      "updatedAt": "2025-08-20T14:43:14.428Z",
      "id": "qZNchUGmNPT9hMjL",
      "name": "Bright Data"
    }
  ]
}