N
n8n Store
Workflow Market
Automate Google Ads Search Term Analysis and Send Insights to Slack

Automate Google Ads Search Term Analysis and Send Insights to Slack

by jj-tham2 views

描述

分类

📢 Marketing🤖 AI & Machine Learning

使用的节点

n8n-nodes-base.ifn8n-nodes-base.setn8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.slackn8n-nodes-base.filtern8n-nodes-base.filtern8n-nodes-base.splitOutn8n-nodes-base.aggregate
价格免费
浏览量2
最后更新2/26/2026
workflow.json
{
  "id": "hRopjodSWDxcjUSe",
  "meta": {
    "instanceId": "ba66afb641da72f937c53df420ad492fd21f2a9f2c92857275062b3d7831e1b1",
    "templateCredsSetupCompleted": true
  },
  "name": "Automate Google Ads Search Term Analysis and Send Insights to Slack",
  "tags": [],
  "nodes": [
    {
      "id": "6b312687-370d-4fe5-a752-3ef2dbe324c0",
      "name": "When clicking ‘Test workflow’",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -336,
        0
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "3c626336-8a80-45de-925b-2749601a87f4",
      "name": "Send a message",
      "type": "n8n-nodes-base.slack",
      "position": [
        3120,
        -16
      ],
      "parameters": {
        "text": "=",
        "select": "channel",
        "blocksUi": "={{ $json.blocksUi }}",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "INSERT YOUR SLACK CHANNEL ID HERE"
        },
        "messageType": "block",
        "otherOptions": {}
      },
      "typeVersion": 2.3
    },
    {
      "id": "87bc69c0-0351-4898-b86e-376e3275708d",
      "name": "Search Terms Checker",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        2496,
        -16
      ],
      "parameters": {
        "text": "={{ $json.data }}",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "=Act as a Google Ads SEM performance marketer. Your job is to analyze the provided data and generate a report in a standardized JSON format.\n\nContext:\n{{ $json.brand_info }}\n\nObjective:\n\nAnalyze search term data from the company's brand campaigns over the past 14 days to identify non-brand keywords.\n\nYour analysis must follow these strict rules:\nIdentify all search terms NOT directly related to the company/brand.\n\nCategorize these non-brand terms into two groups based on the conversions metric only (ignore allConversions).\n\nGroup A (Wastage): Terms with ZERO conversions. These will be added to the negativeKeywords list.\n\nGroup B (For Review): Terms with ONE OR MORE conversions. These will be added to the keywordsForReview list.\n\nCalculate totalAdWastageUSD using the cost from Group A only.\n\nThe output structure must be strictly followed to ensure it can be processed automatically.\n\nTime Now: {{ $now.setZone(\"Asia/Singapore\") }}\n\nOutput Format (Strict JSON):\nReturn the final result strictly in the following JSON structure. Both recommendations and keywordsForReview must always be included as arrays. If there are no items for a list, return an empty array [].\n\n{\n  \"reportTitle\": \"string\",\n  \"date\": \"YYYY-MM-DD\",\n  \"period\": \"string (e.g., '2025-10-28 to 2025-11-05')\",\n  \"totalAdWastageUSD\": \"number\",\n  \"summary\": \"string\",\n  \"recommendations\": [\n    {\n      \"campaign\": \"string\",\n      \"campaignId\": \"string\",\n      \"adGroup\": \"string\",\n      \"adGroupId\": \"string\",\n      \"negativeKeywords\": [\"string\", \"string\"]\n    }\n  ],\n  \"keywordsForReview\": [\n    {\n      \"searchTerm\": \"string\",\n      \"campaign\": \"string\",\n      \"adGroup\": \"string\",\n      \"costUSD\": \"number\",\n      \"conversions\": \"number\",\n      \"reason\": \"string\"\n    }\n  ]\n}"
            }
          ]
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.7
    },
    {
      "id": "adf1c74f-0df7-49ee-8410-7be82d5b1f50",
      "name": "Google Gemini Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        2480,
        176
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "16b263b9-7829-4432-a59c-2febe2ac7f90",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        2640,
        176
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"reportTitle\": \"string\",\n  \"date\": \"YYYY-MM-DD\",\n  \"period\": \"string (e.g., '2025-10-28 to 2025-11-05')\",\n  \"totalAdWastageUSD\": \"number\",\n  \"summary\": \"string\",\n  \"recommendations\": [\n    {\n      \"campaign\": \"string\",\n      \"campaignId\": \"string\",\n      \"adGroup\": \"string\",\n      \"adGroupId\": \"string\",\n      \"negativeKeywords\": [\"string\", \"string\"]\n    }\n  ],\n  \"keywordsForReview\": [\n    {\n      \"searchTerm\": \"string\",\n      \"campaign\": \"string\",\n      \"adGroup\": \"string\",\n      \"costUSD\": \"number\",\n      \"conversions\": \"number\",\n      \"reason\": \"string\"\n    }\n  ]\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "58cf010e-a12b-463f-9675-600aebdcfc51",
      "name": "Getting Google Campaigns",
      "type": "n8n-nodes-base.googleAds",
      "position": [
        -48,
        0
      ],
      "parameters": {
        "requestOptions": {},
        "clientCustomerId": "(INSERT YOUR CLIENT CUSTOMER ID HERE)",
        "additionalOptions": {
          "dateRange": "LAST_7_DAYS"
        },
        "managerCustomerId": "(INSERT YOUR MANAGER CUSTOMER ID HERE)"
      },
      "typeVersion": 1
    },
    {
      "id": "d10d4ddc-6c9d-431d-b9e5-4dd8e1c293fb",
      "name": "Filtering Only for 'Enabled' Campaigns",
      "type": "n8n-nodes-base.filter",
      "position": [
        224,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "9ceb1ebd-b204-444b-81be-b64e3f733825",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "ENABLED"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "80884ae8-36f7-4930-a1e8-1c3b7be2479d",
      "name": "Filtering For A Specific Search Google Campaign",
      "type": "n8n-nodes-base.if",
      "position": [
        528,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "0decde91-2061-464e-873d-b37191fc7c9d",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.name }}",
              "rightValue": "(INSERT THE GOOGLE CAMPAIGN NAME HERE)"
            },
            {
              "id": "f77d2307-2d25-4211-a5d6-ae8ae9bbc534",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.name }}",
              "rightValue": "search"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "bbd97f76-209d-4696-af86-c40060f78a8b",
      "name": "Extracting Search Terms In The Past 14 Days",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        832,
        -16
      ],
      "parameters": {
        "url": "https://googleads.googleapis.com/v19/customers/(INSERT YOUR GOOGLE ADS ACCOUNT ID HERE)/googleAds:search",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "=SELECT\n    search_term_view.search_term,\n    search_term_view.status,\n    metrics.impressions,\n    metrics.clicks,\n    metrics.cost_micros,\n    metrics.all_conversions,\n    metrics.conversions,\n    campaign.name,\n    ad_group.name,\n    ad_group.id,\n    segments.date\nFROM\n    search_term_view\nWHERE\n    campaign.id = {{ $json.id }}\n    AND segments.date DURING LAST_14_DAYS\n    AND metrics.clicks > 1\nORDER BY\n    metrics.clicks DESC\nLIMIT 1000\n"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "developer-token",
              "value": "PASTE YOUR DEVEOPER TOKEN HERE"
            },
            {
              "name": "login-customer-id",
              "value": "PASTE YOUR MCC ACCOUNT ID HERE"
            }
          ]
        },
        "nodeCredentialType": "googleAdsOAuth2Api"
      },
      "typeVersion": 4.2
    },
    {
      "id": "e5698d70-ed07-4afd-9abd-6d0629d1882e",
      "name": "Split Out HTTP Request Into Individual Items",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        1104,
        -16
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "results"
      },
      "executeOnce": false,
      "typeVersion": 1
    },
    {
      "id": "2d5a1774-c0a6-4345-8a02-be1c7f7e7fc2",
      "name": "Consolidating Search Terms Data (Removing Dates)",
      "type": "n8n-nodes-base.code",
      "position": [
        1344,
        -16
      ],
      "parameters": {
        "jsCode": "// Dynamic Google Ads API cleaner for n8n.\n// This version handles the direct, nested JSON output from the API.\nconst items = $input.all();\nconst cleanedData = [];\n\nfor (const item of items) {\n  const data = item.json;\n  const cleaned = {};\n  \n  // --- Campaign Info ---\n  // Extracts name and parses Campaign ID from the 'resourceName'\n  if (data.campaign) {\n    cleaned.campaign = data.campaign.name;\n    // Example resourceName: \"customers/8497428868/campaigns/1590383188\"\n    // We split by '/' and take the last part.\n    cleaned.campaignId = data.campaign.resourceName?.split('/').pop();\n  }\n  \n  // --- Ad Group Info ---\n  if (data.adGroup) {\n    cleaned.adGroup = data.adGroup.name;\n    cleaned.adGroupId = data.adGroup.id;\n  }\n  \n  // --- Metrics ---\n  // Parses all metrics and calculates cost in USD\n  if (data.metrics) {\n    cleaned.clicks = parseInt(data.metrics.clicks || 0);\n    cleaned.impressions = parseInt(data.metrics.impressions || 0);\n    cleaned.conversions = parseFloat(data.metrics.conversions || 0);\n    cleaned.allConversions = parseFloat(data.metrics.allConversions || 0);\n    \n    const costMicros = parseInt(data.metrics.costMicros || 0);\n    cleaned.costMicros = costMicros;\n    cleaned.costUSD = parseFloat((costMicros / 1000000).toFixed(2));\n  }\n  \n  // --- Search Term Info ---\n  if (data.searchTermView) {\n    cleaned.searchTerm = data.searchTermView.searchTerm;\n    cleaned.status = data.searchTermView.status;\n  }\n  \n  // --- Segments (like date) ---\n  if (data.segments) {\n    cleaned.date = data.segments.date;\n  }\n  \n  // --- Calculate Derived Metrics (CTR, CPC) ---\n  if (cleaned.impressions > 0) {\n    cleaned.ctr = ((cleaned.clicks / cleaned.impressions) * 100).toFixed(2) + '%';\n  } else {\n    cleaned.ctr = '0.00%';\n  }\n  \n  if (cleaned.clicks > 0 && cleaned.costUSD > 0) {\n    cleaned.cpc = (cleaned.costUSD / cleaned.clicks).toFixed(2);\n  } else {\n    cleaned.cpc = '0.00';\n  }\n  \n  cleanedData.push({ json: cleaned });\n}\n\nreturn cleanedData;"
      },
      "typeVersion": 2
    },
    {
      "id": "35fec52b-3526-441a-b119-181f3f604e0a",
      "name": "Removing Brand Terms + EXCLUDED Terms",
      "type": "n8n-nodes-base.filter",
      "position": [
        1568,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "3af2cbb8-24e9-40f3-9150-af97040a79f9",
              "operator": {
                "type": "string",
                "operation": "notContains"
              },
              "leftValue": "={{ $json.searchTerm }}",
              "rightValue": "(INSERT YOUR BRAND TERM HERE - THIS IS TO ENSURE IT IS EXCLUDED FROM THE LLM TO ANALYZE TO SAVE ON TOKEN USAGE)"
            },
            {
              "id": "5c8738cd-359c-410f-b163-d981d6153851",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "EXCLUDED"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "23503177-5d69-48d4-ab95-8e7f0bb88680",
      "name": "Aggregating Performance Metrics",
      "type": "n8n-nodes-base.code",
      "position": [
        1792,
        -16
      ],
      "parameters": {
        "jsCode": "// Input: items array from the previous node (with conversion data)\n// Output: combined items aggregated by searchTerm + campaign + adGroup\n\nconst combined = {};\n\nfor (const item of items) {\n  const d = item.json;\n  // Use a robust key to handle potential missing data\n  const key = `${d.searchTerm}_${d.campaignId}_${d.adGroupId}`;\n\n  if (!combined[key]) {\n    // If we see this combination for the first time, create its entry\n    combined[key] = {\n      searchTerm: d.searchTerm,\n      campaign: d.campaign,\n      campaignId: d.campaignId,\n      adGroup: d.adGroup,\n      adGroupId: d.adGroupId,\n      status: d.status,\n      // Initialize all metrics to zero\n      clicks: 0,\n      impressions: 0,\n      costMicros: 0,\n      costUSD: 0,\n      conversions: 0,      // <-- ADDED\n      allConversions: 0    // <-- ADDED\n    };\n  }\n\n  // Add the current item's metrics to the running total\n  combined[key].clicks += d.clicks || 0;\n  combined[key].impressions += d.impressions || 0;\n  combined[key].costMicros += d.costMicros || 0;\n  combined[key].costUSD += d.costUSD || 0;\n  combined[key].conversions += d.conversions || 0;          // <-- ADDED\n  combined[key].allConversions += d.allConversions || 0;    // <-- ADDED\n}\n\n// Convert the combined object back into an array for n8n\nreturn Object.values(combined).map(x => ({ json: x }));"
      },
      "typeVersion": 2
    },
    {
      "id": "532453d8-38ef-4f90-8408-769abd3aa077",
      "name": "Aggregating Items To Prepare For LLM To Analyze",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        2080,
        -16
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "21457c4c-83e2-445e-b156-7470d92042b3",
      "name": "Transforming It It To Be Ready For Slack Block",
      "type": "n8n-nodes-base.code",
      "position": [
        2880,
        -16
      ],
      "parameters": {
        "jsCode": "// Input: items[0].json.output (your LLM structured output)\n// This version uses the robust \"POST\" method for Slack interactivity.\nconst data = items[0].json.output;\n\nconst blocks = [];\n\n// Header\nblocks.push({\n  type: \"header\",\n  text: {\n    type: \"plain_text\",\n    text: `📊 ${data.reportTitle}`,\n    emoji: true\n  }\n});\n\n// Date and Period\nblocks.push({\n  type: \"section\",\n  fields: [\n    { type: \"mrkdwn\", text: `*Date:*\\n${data.date}` },\n    { type: \"mrkdwn\", text: `*Period:*\\n${data.period}` }\n  ]\n});\n\n// Summary\nblocks.push({\n  type: \"section\",\n  text: { type: \"mrkdwn\", text: `${data.summary}` }\n});\n\n// Total Ad Wastage\nblocks.push({\n  type: \"section\",\n  text: { type: \"mrkdwn\", text: `💸 *Total Ad Wastage:* *$${data.totalAdWastageUSD} USD*` }\n});\n\nblocks.push({ type: \"divider\" });\n\n// --- SECTION 1: Recommended Negative Keywords ---\nif (data.recommendations && data.recommendations.length > 0) {\n  blocks.push({\n    type: \"section\",\n    text: { type: \"mrkdwn\", text: \"✅ *Recommended Negative Keywords to Add*\" }\n  });\n\n  for (const rec of data.recommendations) {\n    const keywordList = rec.negativeKeywords.map(k => `• \\`${k}\\``).join('\\n');\n    blocks.push({\n      type: \"section\",\n      text: {\n        type: \"mrkdwn\",\n        text: `*📍 Campaign:* ${rec.campaign} (ID: ${rec.campaignId})\\n*Ad Group:* ${rec.adGroup} (ID: ${rec.adGroupId})\\n\\n${keywordList}`\n      }\n    });\n  }\n  blocks.push({ type: \"divider\" });\n}\n\n// --- SECTION 2: Keywords for Manual Review (CONDITIONAL) ---\nif (data.keywordsForReview && data.keywordsForReview.length > 0) {\n  blocks.push({\n    type: \"section\",\n    text: { type: \"mrkdwn\", text: \"⚠️ *Keywords for Manual Review (Generated Conversions)*\" }\n  });\n\n  for (const reviewItem of data.keywordsForReview) {\n    const reviewText = `*Search Term:* \\`${reviewItem.searchTerm}\\`\\n*Cost:* $${reviewItem.costUSD} USD\\n*Conversions:* ${reviewItem.conversions}\\n*Reason:* _${reviewItem.reason}_`;\n    blocks.push({\n      type: \"section\",\n      text: { type: \"mrkdwn\", text: reviewText }\n    });\n  }\n  blocks.push({ type: \"divider\" });\n}\n\n// --- SECTION 3: Confirmation and Action Buttons (POST Method) ---\n// This section now builds a JSON payload to send in the body of a POST request.\nif (data.recommendations && data.recommendations.length > 0) {\n  \n  // Create a clean JSON payload. This is the data your next workflow will receive.\n  // We stringify it to pass it in the button's 'value' field.\n  const payload = JSON.stringify({\n    // We send the whole recommendations array to handle multiple ad groups in the future\n    recommendations: data.recommendations \n  });\n\n  blocks.push({\n    type: \"section\",\n    text: { type: \"mrkdwn\", text: \"🚀 *Should I proceed to exclude the recommended keywords?*\" }\n  });\n\n  blocks.push({\n    type: \"actions\",\n    elements: [\n      {\n        type: \"button\",\n        text: { type: \"plain_text\", text: \"✅ Yes, exclude them\" },\n        style: \"primary\",\n        action_id: \"approve_exclusions\", // An identifier for this action\n        value: payload // <-- The rich JSON data is passed here\n      },\n      {\n        type: \"button\",\n        text: { type: \"plain_text\", text: \"❌ No, ignore\" },\n        style: \"danger\",\n        action_id: \"reject_exclusions\"\n        // No 'value' is needed for the reject action\n      }\n    ]\n  });\n}\n\nreturn [\n  {\n    json: {\n      blocksUi: { blocks }\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "ee892386-213b-428b-8d1b-cde4f9849e95",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1440,
        -352
      ],
      "parameters": {
        "width": 1008,
        "height": 896,
        "content": "## Automate Google Ads Search Term Analysis and Send Insights to Slack\n\nThis workflow is your AI assistant for managing Google Ads brand campaigns. It automatically fetches search term data, uses AI to find non-brand keywords that are wasting your budget, and sends a clear, actionable report to Slack.\n\n## ⚠️ This is an advanced workflow\nThis template uses a direct HTTP Request to the Google Ads API for maximum data retrieval. This requires you to have a Google Ads Developer Token.\n\n## What it does\n1.  **Fetch Data:** Connects to your Google Ads account and pulls all search term data from a specific brand campaign over the last 14 days.\n2.  **Clean & Aggregate:** The workflow cleans up the raw API data, removes your known brand terms, and aggregates the performance metrics for each unique search term.\n3.  **AI Analysis:** It sends the cleaned data to Google Gemini, which acts as an SEM expert to identify two key groups:\n    *   **Wastage:** Non-brand keywords with 0 conversions that should be added as negatives.\n    *   **For Review:** Non-brand keywords that *are* converting and need a human decision.\n4.  **Report to Slack:** The AI's findings are formatted into a professional Slack message, including the total wasted ad spend and interactive buttons to approve adding the negative keywords in a follow-up workflow.\n\n## How to set up\n1.  **Connect Google Ads:**\n    *   In the \"Getting Google Campaigns\" node, connect your Google Ads account and enter your **Manager Customer ID** and **Client Customer ID**.\n    *   In the \"Filtering For A Specific Search Google Campaign\" node, enter the name of the brand campaign you want to analyze.\n2.  **Configure the API Call:**\n    *   In the \"Extracting Search Terms...\" (HTTP Request) node, you **must** update four things:\n        *   The Google Ads Account ID in the **URL**.\n        *   Your **Developer Token** in the headers.\n        *   Your **MCC Account ID** in the headers.\n        *   Connect your Google Ads account in the **Authentication** tab.\n3.  **Define Your Brand:**\n    *   In the \"Removing Brand Terms...\" node, enter your core brand name to exclude it from the analysis.\n    *   In the \"Edit Fields\" node, provide a short `brand_info` description for the AI's context.\n4.  **Connect AI & Slack:**\n    *   In the \"Google Gemini Chat Model\" node, connect your Gemini API credentials.\n    *   In the \"Send a message\" (Slack) node, connect your Slack account and select the channel where you want to receive the report."
      },
      "typeVersion": 1
    },
    {
      "id": "0188bbaa-ca69-4304-942d-b236e3606bd2",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        -192
      ],
      "parameters": {
        "color": 4,
        "width": 784,
        "height": 176,
        "content": "**👇 STEP 1: GOOGLE ADS SETUP**\n\nConfigure your Google Ads connection here.\n\n**Action Required:**\n1.  In **\"Getting Google Campaigns\"**, connect your credentials and add your Manager & Client IDs.\n2.  In **\"Filtering For A Specific Search...\"**, enter the name of the brand campaign you want to analyze."
      },
      "typeVersion": 1
    },
    {
      "id": "c7c42021-fd3b-4218-a49d-a0fb49cf8e53",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        736,
        192
      ],
      "parameters": {
        "width": 304,
        "height": 272,
        "content": "**⚠️ STEP 2: ADVANCED API CALL**\n\nThis is the most critical step. This node calls the Google Ads API directly.\n\n**Action Required:**\n1.  Update the Google Ads Account ID in the **URL**.\n2.  In **Headers**, paste your **Developer Token** and **MCC Account ID**.\n3.  In **Authentication**, select your Google Ads credential."
      },
      "typeVersion": 1
    },
    {
      "id": "a019f9ed-8629-4ee7-a5fa-9e8ea7b33394",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1504,
        -224
      ],
      "parameters": {
        "color": 7,
        "width": 832,
        "height": 176,
        "content": "**🎯 STEP 3: DEFINE YOUR BRAND**\n\nTell the workflow what to ignore and give the AI context.\n\n**Action Required:**\n1.  In the **\"Removing Brand Terms...\"** node, enter your brand name so it's excluded from the analysis.\n2.  In the **\"Brand Info\"** node, write a short sentence about your company for the `brand_info` field."
      },
      "typeVersion": 1
    },
    {
      "id": "fe818f89-607c-43e3-9fc3-3103d2b47fe0",
      "name": "Brand Info Node",
      "type": "n8n-nodes-base.set",
      "position": [
        2288,
        -16
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "6ea57e24-e61e-404f-9241-f30a944e7adc",
              "name": "data",
              "type": "array",
              "value": "={{ $json.data }}"
            },
            {
              "id": "bdd5d2e8-83fa-4177-8ca6-543b0e36687e",
              "name": "brand_info",
              "type": "string",
              "value": "INSERT YOUR BRAND_INFO HERE"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f6977287-3dec-4c5e-8913-07d8c9eb2191",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2480,
        -272
      ],
      "parameters": {
        "width": 752,
        "height": 224,
        "content": "**🚀 STEP 4: CONFIGURE OUTPUT**\n\nSet up your AI and Slack connections to finish.\n\n**Action Required:**\n1.  Connect your **Google Gemini** credentials in the model node.\n2.  In the **\"Send a message\"** node, connect your Slack account and choose the channel for the report."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "f0648c66-70e0-466f-87f5-9b2f3a611c0f",
  "connections": {
    "Brand Info Node": {
      "main": [
        [
          {
            "node": "Search Terms Checker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Terms Checker": {
      "main": [
        [
          {
            "node": "Transforming It It To Be Ready For Slack Block",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Getting Google Campaigns": {
      "main": [
        [
          {
            "node": "Filtering Only for 'Enabled' Campaigns",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Search Terms Checker",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "Search Terms Checker",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Aggregating Performance Metrics": {
      "main": [
        [
          {
            "node": "Aggregating Items To Prepare For LLM To Analyze",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking ‘Test workflow’": {
      "main": [
        [
          {
            "node": "Getting Google Campaigns",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Removing Brand Terms + EXCLUDED Terms": {
      "main": [
        [
          {
            "node": "Aggregating Performance Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filtering Only for 'Enabled' Campaigns": {
      "main": [
        [
          {
            "node": "Filtering For A Specific Search Google Campaign",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extracting Search Terms In The Past 14 Days": {
      "main": [
        [
          {
            "node": "Split Out HTTP Request Into Individual Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out HTTP Request Into Individual Items": {
      "main": [
        [
          {
            "node": "Consolidating Search Terms Data (Removing Dates)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transforming It It To Be Ready For Slack Block": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregating Items To Prepare For LLM To Analyze": {
      "main": [
        [
          {
            "node": "Brand Info Node",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filtering For A Specific Search Google Campaign": {
      "main": [
        [
          {
            "node": "Extracting Search Terms In The Past 14 Days",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Consolidating Search Terms Data (Removing Dates)": {
      "main": [
        [
          {
            "node": "Removing Brand Terms + EXCLUDED Terms",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

相关工作流