{
  "id": "lJ55B9JA2gEldRw9",
  "meta": {
    "builderVariant": "mcp",
    "aiBuilderAssisted": true
  },
  "name": "Send Weekly Search Console Report",
  "tags": [],
  "nodes": [
    {
      "id": "244b17ae-abca-4b2c-acc1-53f5cd5549ac",
      "name": "Weekly Mon 8AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -80,
        416
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "4cb3bdc1-75be-4bc9-bd70-c0ab2604d1c0",
      "name": "Build Date Ranges",
      "type": "n8n-nodes-base.set",
      "notes": "Two 7-day windows ending 3 days ago (Search Console finalizes data with a ~2-3 day lag).",
      "position": [
        192,
        416
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f0",
              "name": "siteUrl",
              "type": "string",
              "value": "https://pocketfmmodapk.com/"
            },
            {
              "id": "f1",
              "name": "currentStart",
              "type": "string",
              "value": "={{ $today.minus({ days: 9 }).toFormat(\"yyyy-MM-dd\") }}"
            },
            {
              "id": "f2",
              "name": "currentEnd",
              "type": "string",
              "value": "={{ $today.minus({ days: 3 }).toFormat(\"yyyy-MM-dd\") }}"
            },
            {
              "id": "f3",
              "name": "previousStart",
              "type": "string",
              "value": "={{ $today.minus({ days: 16 }).toFormat(\"yyyy-MM-dd\") }}"
            },
            {
              "id": "f4",
              "name": "previousEnd",
              "type": "string",
              "value": "={{ $today.minus({ days: 10 }).toFormat(\"yyyy-MM-dd\") }}"
            },
            {
              "id": "f5",
              "name": "currentLabel",
              "type": "string",
              "value": "={{ $today.minus({ days: 9 }).toFormat(\"MMM d\") }} to {{ $today.minus({ days: 3 }).toFormat(\"MMM d, yyyy\") }}"
            },
            {
              "id": "f6",
              "name": "previousLabel",
              "type": "string",
              "value": "={{ $today.minus({ days: 16 }).toFormat(\"MMM d\") }} to {{ $today.minus({ days: 10 }).toFormat(\"MMM d, yyyy\") }}"
            },
            {
              "id": "f7",
              "name": "generatedAt",
              "type": "string",
              "value": "={{ $now.toFormat(\"MMMM d, yyyy\") }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "da0192d6-9451-45b2-b14b-bdf587420f5b",
      "name": "Totals This Week",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        448,
        416
      ],
      "parameters": {
        "url": "=https://www.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($('Build Date Ranges').item.json.siteUrl) }}/searchAnalytics/query",
        "method": "POST",
        "options": {},
        "jsonBody": "={ \"startDate\": \"{{ $(\"Build Date Ranges\").item.json.currentStart }}\", \"endDate\": \"{{ $(\"Build Date Ranges\").item.json.currentEnd }}\", \"dataState\": \"final\" }",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.4,
      "waitBetweenTries": 5000
    },
    {
      "id": "67c42fea-000a-4c5c-9f6f-34a51b4330a4",
      "name": "Error Alert",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1840,
        416
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=<div style=\"font-family:-apple-system,Segoe UI,Roboto,Arial,sans-serif;max-width:560px;\"><h2 style=\"color:#b91c1c;margin:0 0 8px;\">Weekly Search Console report failed</h2><p style=\"color:#374151;\">Workflow <b>{{ $workflow.name }}</b> errored while pulling data from Google Search Console, so no report was sent this run.</p><p style=\"color:#374151;\"><b>Error:</b> {{ $json.error?.message || $json.message || \"Unknown error\" }}</p><p style=\"color:#6b7280;font-size:13px;\"><b>Execution ID:</b> {{ $execution.id }}</p></div>",
        "options": {
          "appendAttribution": false
        },
        "subject": "=GSC Weekly Report FAILED - {{ $now.toFormat(\"MMM d, yyyy\") }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "c5149bbd-921b-42c3-a827-c76d33acc431",
      "name": "Totals Last Week",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        672,
        336
      ],
      "parameters": {
        "url": "=https://www.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($('Build Date Ranges').item.json.siteUrl) }}/searchAnalytics/query",
        "method": "POST",
        "options": {},
        "jsonBody": "={ \"startDate\": \"{{ $(\"Build Date Ranges\").item.json.previousStart }}\", \"endDate\": \"{{ $(\"Build Date Ranges\").item.json.previousEnd }}\", \"dataState\": \"final\" }",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.4,
      "waitBetweenTries": 5000
    },
    {
      "id": "ad1c2264-0baa-4c82-b70b-9b7c61c9fafc",
      "name": "Queries This Week",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        896,
        272
      ],
      "parameters": {
        "url": "=https://www.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($('Build Date Ranges').item.json.siteUrl) }}/searchAnalytics/query",
        "method": "POST",
        "options": {},
        "jsonBody": "={ \"startDate\": \"{{ $(\"Build Date Ranges\").item.json.currentStart }}\", \"endDate\": \"{{ $(\"Build Date Ranges\").item.json.currentEnd }}\", \"dimensions\": [\"query\"], \"rowLimit\": 10, \"dataState\": \"final\" }",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.4,
      "waitBetweenTries": 5000
    },
    {
      "id": "e385ae45-58ca-488e-8c21-d391fa1126c1",
      "name": "Queries Last Week",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        1120,
        192
      ],
      "parameters": {
        "url": "=https://www.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($('Build Date Ranges').item.json.siteUrl) }}/searchAnalytics/query",
        "method": "POST",
        "options": {},
        "jsonBody": "={ \"startDate\": \"{{ $(\"Build Date Ranges\").item.json.previousStart }}\", \"endDate\": \"{{ $(\"Build Date Ranges\").item.json.previousEnd }}\", \"dimensions\": [\"query\"], \"rowLimit\": 250, \"dataState\": \"final\" }",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.4,
      "waitBetweenTries": 5000
    },
    {
      "id": "6609c7ba-bc99-43a1-8715-b076267fcee3",
      "name": "Pages This Week",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        1344,
        128
      ],
      "parameters": {
        "url": "=https://www.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($('Build Date Ranges').item.json.siteUrl) }}/searchAnalytics/query",
        "method": "POST",
        "options": {},
        "jsonBody": "={ \"startDate\": \"{{ $(\"Build Date Ranges\").item.json.currentStart }}\", \"endDate\": \"{{ $(\"Build Date Ranges\").item.json.currentEnd }}\", \"dimensions\": [\"page\"], \"rowLimit\": 10, \"dataState\": \"final\" }",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.4,
      "waitBetweenTries": 5000
    },
    {
      "id": "5ff3dce6-7f24-4b57-8d4d-45b0c7781831",
      "name": "Pages Last Week",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        1568,
        48
      ],
      "parameters": {
        "url": "=https://www.googleapis.com/webmasters/v3/sites/{{ encodeURIComponent($('Build Date Ranges').item.json.siteUrl) }}/searchAnalytics/query",
        "method": "POST",
        "options": {},
        "jsonBody": "={ \"startDate\": \"{{ $(\"Build Date Ranges\").item.json.previousStart }}\", \"endDate\": \"{{ $(\"Build Date Ranges\").item.json.previousEnd }}\", \"dimensions\": [\"page\"], \"rowLimit\": 250, \"dataState\": \"final\" }",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.4,
      "waitBetweenTries": 5000
    },
    {
      "id": "e5519332-5a18-44fa-8daa-932927c2ecca",
      "name": "Build Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1792,
        0
      ],
      "parameters": {
        "jsCode": "const d = $('Build Date Ranges').first().json;\nconst rowsOf = (name) => { const r = $(name).first().json; return (r && r.rows) ? r.rows : []; };\nconst totalsOf = (name) => rowsOf(name)[0] || {};\nconst num = (v) => (typeof v === 'number' && isFinite(v)) ? v : 0;\n\nconst curT = totalsOf('Totals This Week');\nconst prevT = totalsOf('Totals Last Week');\nconst curQ = rowsOf('Queries This Week');\nconst curP = rowsOf('Pages This Week');\nconst mapBy = (rows) => { const m = {}; rows.forEach((r) => { const k = (r.keys && r.keys[0] !== undefined) ? r.keys[0] : ''; m[k] = r; }); return m; };\nconst prevQMap = mapBy(rowsOf('Queries Last Week'));\nconst prevPMap = mapBy(rowsOf('Pages Last Week'));\n\nconst fmtInt = (x) => Math.round(x).toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\nconst fmtCtr = (x) => (x * 100).toFixed(1) + '%';\nconst fmtPos = (x) => x.toFixed(1);\n\nconst GREEN = '#16a34a'; const RED = '#dc2626'; const GRAY = '#94a3b8';\nconst chip = (raw, higherBetter, fmt) => {\n  const rounded = Math.round(raw * 100) / 100;\n  if (Math.abs(rounded) < 0.05) return '';\n  const better = higherBetter ? rounded > 0 : rounded < 0;\n  const color = better ? GREEN : RED;\n  const arrow = better ? '\\u25B2' : '\\u25BC';\n  return '<div style=\"font-size:10px;font-weight:600;color:' + color + ';\">' + arrow + fmt(Math.abs(rounded)) + '</div>';\n};\nconst newChip = '<div style=\"font-size:10px;font-weight:600;color:' + GRAY + ';\">new</div>';\nconst mTd = (valStr, chipHtml, primary) => {\n  const vStyle = primary ? 'font-size:14px;font-weight:600;color:#0f172a;' : 'font-size:13px;color:#475569;';\n  return '<td style=\"padding:10px 12px;text-align:right;border-bottom:1px solid #f2f4f8;\"><div style=\"' + vStyle + '\">' + valStr + '</div>' + (chipHtml || '') + '</td>';\n};\nconst dataRow = (label, cur, prev) => {\n  const c = { clicks: num(cur.clicks), impr: num(cur.impressions), ctr: num(cur.ctr), pos: num(cur.position) };\n  let cc, ci, cr, cp;\n  if (prev) {\n    const p = { clicks: num(prev.clicks), impr: num(prev.impressions), ctr: num(prev.ctr), pos: num(prev.position) };\n    cc = chip(c.clicks - p.clicks, true, (i) => Math.round(i).toString());\n    ci = chip(c.impr - p.impr, true, (i) => Math.round(i).toString());\n    cr = chip((c.ctr - p.ctr) * 100, true, (i) => i.toFixed(1));\n    cp = chip(c.pos - p.pos, false, (i) => i.toFixed(1));\n  } else { cc = newChip; ci = ''; cr = ''; cp = ''; }\n  return '<tr>' +\n    '<td style=\"padding:10px 12px;font-size:13px;color:#334155;border-bottom:1px solid #f2f4f8;\">' + label + '</td>' +\n    mTd(fmtInt(c.clicks), cc, true) + mTd(fmtInt(c.impr), ci, false) + mTd(fmtCtr(c.ctr), cr, false) + mTd(fmtPos(c.pos), cp, false) +\n    '</tr>';\n};\nconst stripUrl = (u) => ((u || '').replace(/^https?:\\/\\/[^/]+/, '') || '/');\nconst emptyRow = '<tr><td colspan=\"5\" style=\"padding:14px;text-align:center;color:#94a3b8;font-size:13px;\">No data for this period.</td></tr>';\nconst qRows = curQ.length ? curQ.map((r) => { const k = (r.keys && r.keys[0]) ? r.keys[0] : '(unknown)'; return dataRow(k, r, prevQMap[k]); }).join('') : emptyRow;\nconst pRows = curP.length ? curP.map((r) => { const k = (r.keys && r.keys[0]) ? r.keys[0] : ''; return dataRow(stripUrl(k), r, prevPMap[k]); }).join('') : emptyRow;\n\nconst kpiPill = (cur, prev, higherBetter, kind) => {\n  const raw = kind === 'pct' ? (prev === 0 ? (cur === 0 ? 0 : 100) : ((cur - prev) / prev) * 100) : (cur - prev);\n  const rounded = Math.round(raw * 10) / 10;\n  const flat = Math.abs(rounded) < 0.05;\n  const better = higherBetter ? raw > 0 : raw < 0;\n  const color = flat ? '#64748b' : (better ? '#15803d' : '#b91c1c');\n  const bg = flat ? '#f1f5f9' : (better ? '#dcfce7' : '#fee2e2');\n  const arrow = flat ? '' : (better ? '\\u25B2 ' : '\\u25BC ');\n  const mag = kind === 'pct' ? Math.abs(rounded).toFixed(1) + '%' : Math.abs(rounded).toFixed(1);\n  return '<span style=\"font-size:11px;font-weight:600;color:' + color + ';background:' + bg + ';border-radius:999px;padding:2px 9px;\">' + arrow + mag + '</span>';\n};\nconst kpiCard = (label, valStr, pill, fromStr) =>\n  '<td style=\"width:25%;vertical-align:top;padding:6px;\">' +\n  '<div style=\"background:#f8fafc;border:1px solid #eef1f5;border-radius:12px;padding:16px 12px;text-align:center;\">' +\n  '<div style=\"font-size:11px;color:#94a3b8;text-transform:uppercase;letter-spacing:.05em;\">' + label + '</div>' +\n  '<div style=\"font-size:26px;font-weight:700;color:#0f172a;margin:7px 0;letter-spacing:-.02em;\">' + valStr + '</div>' +\n  '<div>' + pill + '</div>' +\n  '<div style=\"font-size:11px;color:#b0bac9;margin-top:7px;\">from ' + fromStr + '</div>' +\n  '</div></td>';\nconst cT = { clicks: num(curT.clicks), impr: num(curT.impressions), ctr: num(curT.ctr), pos: num(curT.position) };\nconst pT = { clicks: num(prevT.clicks), impr: num(prevT.impressions), ctr: num(prevT.ctr), pos: num(prevT.position) };\nconst cards =\n  kpiCard('Clicks', fmtInt(cT.clicks), kpiPill(cT.clicks, pT.clicks, true, 'pct'), fmtInt(pT.clicks)) +\n  kpiCard('Impressions', fmtInt(cT.impr), kpiPill(cT.impr, pT.impr, true, 'pct'), fmtInt(pT.impr)) +\n  kpiCard('Avg CTR', fmtCtr(cT.ctr), kpiPill(cT.ctr, pT.ctr, true, 'pct'), fmtCtr(pT.ctr)) +\n  kpiCard('Avg Position', fmtPos(cT.pos), kpiPill(cT.pos, pT.pos, false, 'pos'), fmtPos(pT.pos));\n\nconst th = (label, align) => '<th style=\"padding:10px 12px;text-align:' + align + ';font-size:10px;color:#e2e8f0;text-transform:uppercase;letter-spacing:.05em;font-weight:600;\">' + label + '</th>';\nconst table = (firstCol, rowsHtml) => '<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-collapse:collapse;border:1px solid #eef1f5;border-radius:10px;overflow:hidden;\"><tr style=\"background:#0f172a;\">' + th(firstCol, 'left') + th('Clicks', 'right') + th('Impr', 'right') + th('CTR', 'right') + th('Pos', 'right') + '</tr>' + rowsHtml + '</table>';\n\nconst html =\n'<div style=\"margin:0;padding:0;background:#eef1f5;\">' +\n'<div style=\"max-width:600px;margin:0 auto;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:#ffffff;border-radius:16px;overflow:hidden;\">' +\n  '<div style=\"background:#0f172a;padding:30px 32px 26px;\">' +\n    '<div style=\"font-size:20px;font-weight:600;color:#ffffff;letter-spacing:-.01em;\">Search Console Weekly Report</div>' +\n    '<div style=\"font-size:14px;color:#8ea0b8;margin-top:5px;\">pocketfmmodapk.com</div>' +\n  '</div>' +\n  '<div style=\"padding:20px 32px 4px;text-align:center;font-size:13px;color:#475569;font-weight:600;\">' + d.currentLabel + ' <span style=\"color:#94a3b8;font-weight:400;\">vs</span> ' + d.previousLabel + '</div>' +\n  '<div style=\"padding:12px 24px 22px;\"><table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>' + cards + '</tr></table></div>' +\n  '<div style=\"padding:0 32px 6px;font-size:13px;font-weight:600;color:#0f172a;\">Top queries</div>' +\n  '<div style=\"padding:0 32px 24px;\">' + table('Query', qRows) + '</div>' +\n  '<div style=\"padding:0 32px 6px;font-size:13px;font-weight:600;color:#0f172a;\">Top pages</div>' +\n  '<div style=\"padding:0 32px 26px;\">' + table('Page', pRows) + '</div>' +\n  '<div style=\"background:#fbfcfd;padding:18px 32px;border-top:1px solid #eef1f5;font-size:11px;color:#a8b2c1;line-height:1.6;\">Compares two 7 day windows ending 3 days before send, so the data is final. Green up means better than last week, red down means worse. Lower position is better. Generated ' + d.generatedAt + ' by n8n.</div>' +\n'</div></div>';\n\nconst cp = pT.clicks === 0 ? 0 : Math.round(((cT.clicks - pT.clicks) / pT.clicks) * 1000) / 10;\nconst arrow = cp > 0 ? '\\u25B2' : (cp < 0 ? '\\u25BC' : '');\nconst subject = 'GSC Weekly - pocketfmmodapk.com - ' + fmtInt(cT.clicks) + ' clicks (' + arrow + ' ' + Math.abs(cp).toFixed(1) + '%) - ' + d.currentLabel;\nreturn [{ json: { subject: subject, html: html } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "2d4867f1-32c4-4d0f-8b98-fc0f8fda18c8",
      "name": "Send Email",
      "type": "n8n-nodes-base.gmail",
      "maxTries": 3,
      "position": [
        2016,
        0
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $json.subject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "bb51b7dd-a21b-4c72-89fa-ad9ac0d92db5",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -656,
        -144
      ],
      "parameters": {
        "width": 480,
        "height": 880,
        "content": "## Send Weekly Search Console Report\n\n### How it works\n\n1. Trigger fires every Monday at 8 AM to run the weekly SEO report workflow\n2. Date ranges for current and previous week are calculated and stored as variables\n3. Six sequential Google Search Console API calls fetch totals, queries, and pages for both weeks\n4. Custom code aggregates the data and formats it into a readable report\n5. Final report is sent via email to configured recipients\n6. Any API errors during data collection automatically trigger an error alert email\n\n### Setup steps\n\n- - [ ] Configure Google account Service Account credentials with access to Google Search Console API\n- [ ] Set up Gmail credentials for sending both the report and error alert emails\n- [ ] Update the recipient email address in the Send Email node\n- [ ] Verify the Google Search Console property URL is correct in all HTTP Request nodes\n\n### Customization\n\nModify the email recipients or report format in Build Report and Send Email nodes. You can also adjust date range calculations in Build Date Ranges if you need custom reporting periods. "
      },
      "typeVersion": 1
    },
    {
      "id": "d17551d3-9956-4492-9407-21804dfcaff3",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -144,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 416,
        "content": "## Weekly Schedule Trigger\n\nScheduled trigger that initiates the workflow every Monday at 8 AM to generate weekly SEO performance reports."
      },
      "typeVersion": 1
    },
    {
      "id": "37e48a17-8ad8-41f7-8b72-4f20c3cc00cc",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        192
      ],
      "parameters": {
        "color": 7,
        "height": 416,
        "content": "## Build Date Ranges\n\nSets current and previous week date ranges for the Google Search Console API queries."
      },
      "typeVersion": 1
    },
    {
      "id": "401e5f65-4cdc-4c70-9221-aee41447d654",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        384,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 1344,
        "height": 688,
        "content": "## Search Console Data Collection\n\nSequential API requests to Google Search Console for search analytics data: totals, queries, and pages for both this week and last week."
      },
      "typeVersion": 1
    },
    {
      "id": "e1e17152-52df-439b-a513-648e6f0f88e8",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1744,
        -192
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 368,
        "content": "## Build and Send Report\n\nCustom code that aggregates all search analytics data and builds a formatted report, then sends it via email."
      },
      "typeVersion": 1
    },
    {
      "id": "bddb9662-c0c1-4aaf-b046-2f76079701ab",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1744,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 368,
        "content": "## Error Alert Handler\n\nGmail node that receives error signals from all data collection nodes and sends alerts when API requests fail."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": true,
    "executionOrder": "v1"
  },
  "versionId": "776a6556-1e88-49c6-9bf2-1f74935f83b7",
  "nodeGroups": [],
  "connections": {
    "Build Report": {
      "main": [
        [
          {
            "node": "Send Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Weekly Mon 8AM": {
      "main": [
        [
          {
            "node": "Build Date Ranges",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pages Last Week": {
      "main": [
        [
          {
            "node": "Build Report",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pages This Week": {
      "main": [
        [
          {
            "node": "Pages Last Week",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Totals Last Week": {
      "main": [
        [
          {
            "node": "Queries This Week",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Totals This Week": {
      "main": [
        [
          {
            "node": "Totals Last Week",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Date Ranges": {
      "main": [
        [
          {
            "node": "Totals This Week",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Queries Last Week": {
      "main": [
        [
          {
            "node": "Pages This Week",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Queries This Week": {
      "main": [
        [
          {
            "node": "Queries Last Week",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}