{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "ef00edf2-80ac-4b81-a346-91f96e1cac4a",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1952,
        784
      ],
      "parameters": {
        "width": 508,
        "height": 880,
        "content": "## Weekly SEO Keyword Report \u2014 GSC + GPT-4o-mini + Gmail\n\nFor SEO agencies and consultants who want to automatically send clients a professional weekly keyword performance digest every Monday morning. The workflow runs on a schedule, pulls the top 10 keywords from Google Search Console for the past 7 days, formats the data, and passes it to a GPT-4o-mini agent that writes a professional email report. Gmail delivers it directly to the client.\n\n## How it works\n- **1. Schedule \u2014 Every Monday 8AM** triggers the workflow automatically each week\n- **2. Set \u2014 Config Values** stores the site URL, client name, recipient email, and agency name\n- **3. HTTP \u2014 Fetch GSC Top Keywords** queries the Google Search Console API for the top 10 keywords\n- **4. Set \u2014 Extract Fields** pulls config values and raw GSC rows into clean named fields\n- **5. Code \u2014 Format Data for GPT** converts raw GSC API rows into readable text for the AI\n- **6. AI Agent \u2014 Write SEO Report Email** uses GPT-4o-mini to write a professional email body\n- **8. Set \u2014 Prepare Final Email** assembles the subject line, recipient, and body into one item\n- **9. Gmail \u2014 Send Weekly Report** delivers the final email to the client\n\n## Set up steps\n1. In **2. Set \u2014 Config Values** \u2014 replace YOUR-WEBSITE.com, YOUR CLIENT NAME, client@example.com, and YOUR AGENCY NAME with real values\n2. In **3. HTTP \u2014 Fetch GSC Top Keywords** \u2014 connect your Google Search Console OAuth2 credential\n3. In **7. OpenAI \u2014 GPT-4o-mini Model** \u2014 connect your OpenAI credential\n4. In **9. Gmail \u2014 Send Weekly Report** \u2014 connect your Gmail OAuth2 credential\n5. Activate the workflow \u2014 it will run every Monday at 8AM automatically"
      },
      "typeVersion": 1
    },
    {
      "id": "f4a603e1-91ea-43c8-b8c8-5cbf038ed878",
      "name": "Section \u2014 Schedule and Config",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2528,
        1008
      ],
      "parameters": {
        "color": 5,
        "width": 404,
        "height": 292,
        "content": "## Schedule and Config\nWorkflow triggers every Monday at 8AM. Config values \u2014 site URL, client name, recipient email, and agency name \u2014 are set here once and used throughout."
      },
      "typeVersion": 1
    },
    {
      "id": "b44763f3-c310-4915-ae4c-eae80c76b098",
      "name": "Section \u2014 GSC Data Collection",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2960,
        1008
      ],
      "parameters": {
        "color": 6,
        "width": 612,
        "height": 292,
        "content": "## GSC Data Collection\nFetches the top 10 keywords from Google Search Console for the past 7 days. Extracts and formats the raw API response into clean readable text for the AI."
      },
      "typeVersion": 1
    },
    {
      "id": "cf0e0d81-ceef-4763-a981-91ca9bfb95f5",
      "name": "Section \u2014 AI Report Writing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3600,
        1008
      ],
      "parameters": {
        "color": 6,
        "width": 292,
        "height": 516,
        "content": "## AI Report Writing\nGPT-4o-mini reads the keyword data and writes a professional 150\u2013200 word SEO digest email body with highlights and one actionable tip."
      },
      "typeVersion": 1
    },
    {
      "id": "c8809f44-2ef6-4fa2-b258-4a6e3095e694",
      "name": "Section \u2014 Email Delivery",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3952,
        1008
      ],
      "parameters": {
        "color": 4,
        "width": 420,
        "height": 372,
        "content": "## Email Delivery\nAssembles the subject line, recipient address, and email body into one item. Gmail sends the final report to the client."
      },
      "typeVersion": 1
    },
    {
      "id": "0b8ed751-7c28-4fb3-be23-f075c931dfb2",
      "name": "Note \u2014 Edit Config Before Activating",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2704,
        1424
      ],
      "parameters": {
        "color": 3,
        "width": 604,
        "content": "## \u26a0\ufe0f Edit This Node Before Activating\nThis is the only node you need to change. Replace all four values before running: siteUrl must match your exact GSC property URL, clientName is the client business name, recipientEmail is where the report goes, and agencyName appears in the email footer."
      },
      "typeVersion": 1
    },
    {
      "id": "664a732f-0f77-4d3e-816c-1547a5131208",
      "name": "1. Schedule \u2014 Every Monday 8AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        2560,
        1152
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "760f4f3d-1c7d-489c-8e65-d59d1dfe22f5",
      "name": "2. Set \u2014 Config Values",
      "type": "n8n-nodes-base.set",
      "position": [
        2784,
        1152
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-001",
              "name": "siteUrl",
              "type": "string",
              "value": "https://www.YOUR-WEBSITE.com/"
            },
            {
              "id": "cfg-002",
              "name": "clientName",
              "type": "string",
              "value": "YOUR CLIENT NAME"
            },
            {
              "id": "cfg-003",
              "name": "recipientEmail",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "cfg-004",
              "name": "agencyName",
              "type": "string",
              "value": "YOUR AGENCY NAME"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f402ba28-4931-4230-b425-35a3d6c7ef4a",
      "name": "3. HTTP \u2014 Fetch GSC Top Keywords",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3008,
        1152
      ],
      "parameters": {
        "url": "=https://www.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($json.siteUrl) }}/searchAnalytics/query",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"startDate\": \"{{ $now.minus({days: 7}).toFormat('yyyy-MM-dd') }}\",\n  \"endDate\": \"{{ $now.minus({days: 1}).toFormat('yyyy-MM-dd') }}\",\n  \"dimensions\": [\"query\"],\n  \"rowLimit\": 10,\n  \"orderby\": [{\"fieldName\": \"clicks\", \"sortOrder\": \"DESCENDING\"}]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "oAuth2",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "0385d709-010c-446d-b059-ff565ad1bf86",
      "name": "4. Set \u2014 Extract Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        3232,
        1152
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "fld-001",
              "name": "siteUrl",
              "type": "string",
              "value": "={{ $('2. Set \u2014 Config Values').item.json.siteUrl }}"
            },
            {
              "id": "fld-002",
              "name": "clientName",
              "type": "string",
              "value": "={{ $('2. Set \u2014 Config Values').item.json.clientName }}"
            },
            {
              "id": "fld-003",
              "name": "recipientEmail",
              "type": "string",
              "value": "={{ $('2. Set \u2014 Config Values').item.json.recipientEmail }}"
            },
            {
              "id": "fld-004",
              "name": "agencyName",
              "type": "string",
              "value": "={{ $('2. Set \u2014 Config Values').item.json.agencyName }}"
            },
            {
              "id": "fld-005",
              "name": "weekStart",
              "type": "string",
              "value": "={{ $now.minus({days: 7}).toFormat('dd MMM yyyy') }}"
            },
            {
              "id": "fld-006",
              "name": "weekEnd",
              "type": "string",
              "value": "={{ $now.minus({days: 1}).toFormat('dd MMM yyyy') }}"
            },
            {
              "id": "fld-007",
              "name": "rawRows",
              "type": "string",
              "value": "={{ JSON.stringify($json.rows || []) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "3d98555c-216c-4652-8613-728ee8734e9f",
      "name": "5. Code \u2014 Format Data for GPT",
      "type": "n8n-nodes-base.code",
      "position": [
        3440,
        1152
      ],
      "parameters": {
        "jsCode": "const item = $input.first().json;\n\nlet rows = [];\ntry {\n  rows = JSON.parse(item.rawRows || '[]');\n} catch(e) {\n  rows = [];\n}\n\nlet keywordText = '';\nif (rows.length === 0) {\n  keywordText = 'No keyword data found for this week. Please check your GSC property URL and credentials.';\n} else {\n  rows.forEach((row, i) => {\n    const query = (row.keys && row.keys[0]) ? row.keys[0] : 'unknown';\n    const clicks = row.clicks || 0;\n    const impressions = row.impressions || 0;\n    const ctr = ((row.ctr || 0) * 100).toFixed(1);\n    const pos = (row.position || 0).toFixed(1);\n    keywordText += `${i + 1}. \"${query}\" | Clicks: ${clicks} | Impressions: ${impressions} | CTR: ${ctr}% | Position: ${pos}\\n`;\n  });\n}\n\nreturn [{\n  json: {\n    clientName: item.clientName,\n    recipientEmail: item.recipientEmail,\n    agencyName: item.agencyName,\n    weekStart: item.weekStart,\n    weekEnd: item.weekEnd,\n    keywordText: keywordText,\n    totalKeywords: rows.length\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "a35d0f79-8e0c-454e-817f-670a3deea04a",
      "name": "6. AI Agent \u2014 Write SEO Report Email",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        3648,
        1152
      ],
      "parameters": {
        "text": "=You are a professional SEO report writer working for {{ $json.agencyName }}.\n\nYour job is to write a weekly SEO performance digest email for the client.\n\nCLIENT NAME: {{ $json.clientName }}\nREPORT PERIOD: {{ $json.weekStart }} to {{ $json.weekEnd }}\n\nTOP KEYWORDS DATA (Last 7 Days from Google Search Console):\n{{ $json.keywordText }}\n\nWRITING INSTRUCTIONS:\n- Write the full email body only. Do NOT include subject line.\n- Use plain text only. No markdown, no asterisks, no bullet symbols, no hashtags.\n- Keep total length between 150 to 200 words.\n- Paragraph 1: Warm one-line greeting mentioning the client name and the report week.\n- Paragraph 2: Highlight the top 3 keywords by name. Mention their click count and average position. Keep it conversational.\n- Paragraph 3: One positive observation about overall performance this week.\n- Paragraph 4: One clear and actionable SEO tip the client should focus on next week.\n- Closing line: Professional sign-off. Do not write agency name \u2014 just end with 'Best regards,'",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "de0cb10e-3318-40d7-be59-c04fb8692c88",
      "name": "7. OpenAI \u2014 GPT-4o-mini Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        3648,
        1344
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {
          "maxTokens": 500,
          "temperature": 0.6
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c468c676-e253-4b2c-9360-3c24f4b57d4a",
      "name": "8. Set \u2014 Prepare Final Email",
      "type": "n8n-nodes-base.set",
      "position": [
        4000,
        1152
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "email-001",
              "name": "toEmail",
              "type": "string",
              "value": "={{ $('5. Code \u2014 Format Data for GPT').item.json.recipientEmail }}"
            },
            {
              "id": "email-002",
              "name": "emailSubject",
              "type": "string",
              "value": "=Weekly SEO Report \u2014 {{ $('5. Code \u2014 Format Data for GPT').item.json.weekStart }} to {{ $('5. Code \u2014 Format Data for GPT').item.json.weekEnd }} | {{ $('5. Code \u2014 Format Data for GPT').item.json.clientName }}"
            },
            {
              "id": "email-003",
              "name": "emailBody",
              "type": "string",
              "value": "={{ $json.output || 'Email content could not be generated. Please check OpenAI credentials.' }}"
            },
            {
              "id": "email-004",
              "name": "agencyName",
              "type": "string",
              "value": "={{ $('5. Code \u2014 Format Data for GPT').item.json.agencyName }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "57afe42f-7a8d-4418-b5ee-2204cce645c1",
      "name": "9. Gmail \u2014 Send Weekly Report",
      "type": "n8n-nodes-base.gmail",
      "position": [
        4224,
        1152
      ],
      "parameters": {
        "sendTo": "={{ $json.toEmail }}",
        "message": "={{ $json.emailBody }}\n\n--\nThis report was auto-generated by {{ $json.agencyName }}.\nData source: Google Search Console | Powered by n8n + GPT-4o-mini",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $json.emailSubject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    }
  ],
  "connections": {
    "2. Set \u2014 Config Values": {
      "main": [
        [
          {
            "node": "3. HTTP \u2014 Fetch GSC Top Keywords",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4. Set \u2014 Extract Fields": {
      "main": [
        [
          {
            "node": "5. Code \u2014 Format Data for GPT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "8. Set \u2014 Prepare Final Email": {
      "main": [
        [
          {
            "node": "9. Gmail \u2014 Send Weekly Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "5. Code \u2014 Format Data for GPT": {
      "main": [
        [
          {
            "node": "6. AI Agent \u2014 Write SEO Report Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7. OpenAI \u2014 GPT-4o-mini Model": {
      "ai_languageModel": [
        [
          {
            "node": "6. AI Agent \u2014 Write SEO Report Email",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "1. Schedule \u2014 Every Monday 8AM": {
      "main": [
        [
          {
            "node": "2. Set \u2014 Config Values",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3. HTTP \u2014 Fetch GSC Top Keywords": {
      "main": [
        [
          {
            "node": "4. Set \u2014 Extract Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "6. AI Agent \u2014 Write SEO Report Email": {
      "main": [
        [
          {
            "node": "8. Set \u2014 Prepare Final Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}