AutomationFlowsAI & RAG › Weekly Okr Alignment Report with Gmail, Google Calendar, Notion, and Gpt-4.1

Weekly Okr Alignment Report with Gmail, Google Calendar, Notion, and Gpt-4.1

ByMark Ma @mark10667 on n8n.io

This workflow is your personal CEO Brain. Every Saturday night, it automatically collects the past week’s activity across: 📩 Gmail: filters out spam, promos, receipts, etc. 📅 Google Calendar: grabs past week and upcoming month 🗒️ Notion Weekly Plan: pulls and analyzes a photo of…

Cron / scheduled trigger★★★★☆ complexityAI-powered21 nodesGmailGoogle CalendarOpenAIHTTP RequestNotion
AI & RAG Trigger: Cron / scheduled Nodes: 21 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #8341 — we link there as the canonical source.

This workflow follows the Gmail → Google Calendar recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "nodes": [
    {
      "id": "c47729f7-33d0-4276-ac6f-ca67edd179ac",
      "name": "Get many messages",
      "type": "n8n-nodes-base.gmail",
      "position": [
        0,
        0
      ],
      "parameters": {
        "filters": {
          "receivedAfter": "={{ $now.minus({ week: 1 }) }}"
        },
        "operation": "getAll",
        "returnAll": true
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "ae728d35-ccd1-4f09-8efc-f52ff442e236",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        400,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "56ac22c4-56a7-467f-898b-fafa7e233b07",
              "name": "from_display",
              "type": "string",
              "value": "={{$json.From.name || $json.From.email || $json.From}}"
            },
            {
              "id": "04fbfc22-d358-4a2c-aeee-483a1e5372ba",
              "name": "subject",
              "type": "string",
              "value": "={{$json.Subject}}"
            },
            {
              "id": "cad79f7c-cf47-4eb8-a2bd-36b62e70a9ec",
              "name": "date",
              "type": "string",
              "value": "={{$json.internalDate}}"
            },
            {
              "id": "612403fd-8e26-43fa-b49e-fc510d0596f6",
              "name": "body",
              "type": "string",
              "value": "={{$json.textPlain || $json.snippet}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f7ebfc4a-16dd-44ed-b796-17173bc53fec",
      "name": "Get many events",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        0,
        240
      ],
      "parameters": {
        "options": {},
        "timeMax": "={{ $now.plus({ month: 1 }) }}",
        "timeMin": "={{ $now.minus({ week: 1 }) }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "user@example.com",
          "cachedResultName": "user@example.com"
        },
        "operation": "getAll"
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "8a6fa5f2-9d9a-4f2d-a1f8-092bd218fbcc",
      "name": "Edit Fields1",
      "type": "n8n-nodes-base.set",
      "position": [
        192,
        240
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "8da8d232-f2f8-4a22-8ead-5c56f2c12fcb",
              "name": "summary",
              "type": "string",
              "value": "={{ $json.summary }}"
            },
            {
              "id": "b03fb05b-b857-409c-8811-f8b44718bc40",
              "name": "time",
              "type": "string",
              "value": "={{ $json.start.dateTime }}{{ $json.end.dateTime }}"
            },
            {
              "id": "ddc969da-e0d3-45fd-9281-5c0fa213742c",
              "name": "attendees",
              "type": "string",
              "value": "={{ $json.attendees[0].email }}"
            },
            {
              "id": "22760046-2864-4ec1-bab1-fcec17ef370b",
              "name": "description",
              "type": "string",
              "value": "={{ $json.description }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "3ed4818e-2693-492d-b9e0-1898ca470635",
      "name": "Analyze image",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        624,
        512
      ],
      "parameters": {
        "text": "Prompt for Analyze Image\n\nYou are analyzing a weekly plan written in a notebook.\n\nThe page is divided into quadrants: Urgent / Not Urgent (columns) and Important / Not Important (rows).\n\nEach bullet or phrase is an action item.\n\nIf an item is crossed out, mark it as finished = true. Otherwise, finished = false.\n\nReturn all items in structured JSON.\n\nOutput format:\n\n{\n  \"quadrants\": {\n    \"urgent_important\": [\n      {\"task\": \"string\", \"finished\": true|false},\n      {\"task\": \"string\", \"finished\": true|false}\n    ],\n    \"not_urgent_important\": [\n      {\"task\": \"string\", \"finished\": true|false}\n    ],\n    \"urgent_not_important\": [\n      {\"task\": \"string\", \"finished\": true|false}\n    ],\n    \"not_urgent_not_important\": [\n      {\"task\": \"string\", \"finished\": true|false}\n    ]\n  }\n}\n\n\nRules:\n\nPreserve original wording as much as possible.\n\nIf the quadrant placement is unclear, make your best guess.\n\nDo not add commentary \u2014 output only the JSON.",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "resource": "image",
        "inputType": "base64",
        "operation": "analyze"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "abfe489e-2195-4e87-bcc2-1005afdc884a",
      "name": "HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        400,
        512
      ],
      "parameters": {
        "url": "={{ $json.property_files_media[0] }}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b7f5ca68-8a77-4def-9659-b8beb7a57b73",
      "name": "Filter1",
      "type": "n8n-nodes-base.filter",
      "notes": "On the last day of each week, the system grabs the weekly plan of that week. \n\n{{ $now.setZone('Asia/Hong_Kong').toISODate() }}",
      "position": [
        192,
        512
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "4839778a-17c1-47de-a193-9d3c866138d2",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.property_date.end }}",
              "rightValue": "={{ $now.setZone('Asia/Hong_Kong').toISODate() }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "4f6ba339-8b85-4e39-894c-91342f797c56",
      "name": "Filter2",
      "type": "n8n-nodes-base.filter",
      "position": [
        192,
        768
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "f74dc194-a313-4c15-8814-731eb73804cf",
              "operator": {
                "type": "dateTime",
                "operation": "after"
              },
              "leftValue": "={{ $now.setZone('Asia/Hong_Kong').toISODate() }}",
              "rightValue": "={{ $json.property_date.start }}"
            },
            {
              "id": "1271a486-4845-4ec4-992a-bb1f761c503a",
              "operator": {
                "type": "dateTime",
                "operation": "beforeOrEquals"
              },
              "leftValue": "={{ $now.setZone('Asia/Hong_Kong').toISODate() }}",
              "rightValue": "={{ $json.property_date.end }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "c76c7867-e8e3-45c0-9b07-6b3a66958eb7",
      "name": "Gmail Code",
      "type": "n8n-nodes-base.code",
      "position": [
        624,
        0
      ],
      "parameters": {
        "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\n\nconst MAX_CHARS = 2000;\n\nconst blocks = items.map(i => {\n  const j = i.json;\n  const body = (j.body || '')\n    .replace(/\\s+\\n/g, '\\n')\n    .slice(0, MAX_CHARS);\n\n  return [\n    '=== EMAIL ===',\n    `Date: ${j.date}`,\n    `From: ${j.from_display}`,\n    `Subject: ${j.subject}`,\n    '',\n    body\n  ].join('\\n');\n});\n\nreturn [{ json: { emails_joined: blocks.join('\\n\\n') } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "165a03f8-cb2f-495c-a0b1-cb5477572310",
      "name": "Calendar Code",
      "type": "n8n-nodes-base.code",
      "position": [
        400,
        240
      ],
      "parameters": {
        "jsCode": "// Build a compact, LLM-friendly string from Calendar events\nconst MAX_DESC = 600; // trim long descriptions\n\nfunction splitTimeField(t) {\n  if (!t) return [null, null];\n  // Prefer regex for two ISO datetimes with timezone offsets\n  const m = t.match(/\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:\\d{2}/g);\n  if (m && m.length >= 2) return [m[0], m[1]];\n  // Fallback: ISO with offset is 25 chars; split in half\n  if (t.length >= 50) return [t.slice(0, 25), t.slice(25)];\n  return [t, null];\n}\n\nconst blocks = items.map(i => {\n  const j = i.json || {};\n  const [start, end] = splitTimeField(j.time);\n\n  const attendees =\n    j.attendees == null\n      ? []\n      : Array.isArray(j.attendees)\n        ? j.attendees\n        : String(j.attendees).split(',').map(s => s.trim()).filter(Boolean);\n\n  const notes = (j.description || '')\n    .replace(/\\s+\\n/g, '\\n')\n    .trim()\n    .slice(0, MAX_DESC);\n\n  const lines = [\n    '=== EVENT ===',\n    `When: ${start || 'TBD'} \u2192 ${end || 'TBD'}`,\n    `Title: ${j.summary || '(no title)'}`,\n    `Attendees: ${attendees.length ? attendees.join(', ') : '-'}`\n  ];\n  if (notes) {\n    lines.push('', notes);\n  }\n  return lines.join('\\n');\n});\n\nreturn [{ json: { calendar_joined: blocks.join('\\n\\n') } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "c7855bfa-8906-41a9-aefb-d2f13a049e96",
      "name": "Weekly Code",
      "type": "n8n-nodes-base.code",
      "notes": "\n\n\n\n// Parse the JSON from content (sometimes it's stringified with markdown formatting)\nlet parsed;\ntry {\n  const raw = $json.content.trim();\n  const jsonText = raw.startsWith('```') ? raw.replace(/```(json)?/g, '').trim() : raw;\n  parsed = JSON.parse(jsonText);\n} catch (err) {\n  throw new Error(\"Failed to parse 'content' as JSON\");\n}\n\nconst quadrants = parsed.quadrants || {};\n\nfunction formatTasks(title, tasks) {\n  if (!tasks || !tasks.length) return '';\n  const lines = tasks.map(t =>\n    `- [${t.finished ? 'x' : ' '}] ${t.task}`\n  );\n  return [`=== ${title} ===`, ...lines].join('\\n');\n}\n\nconst blocks = [\n  formatTasks('Urgent & Important', quadrants.urgent_important),\n  formatTasks('Not Urgent but Important', quadrants.not_urgent_important),\n  formatTasks('Urgent but Not Important', quadrants.urgent_not_important),\n  formatTasks('Not Urgent & Not Important', quadrants.not_urgent_not_important),\n].filter(Boolean); // remove empty sections\n\nreturn [{ json: { weeklyplan_joined: blocks.join('\\n\\n') } }];\n\n",
      "position": [
        832,
        512
      ],
      "parameters": {
        "jsCode": "// Run Once for All Items\n// Purpose: Safely parse the \"content\" field coming from the LLM (or other nodes),\n// which may be in multiple inconsistent formats:\n//   - Already a JSON object\n//   - A JSON string (valid)\n//   - A JSON string wrapped in ```json ... ``` code fences\n//   - A JSON string prefixed with \"json\\n\"\n//   - A JSON string that's double-encoded (JSON string inside a string)\n//   - Or even invalid, in which case we want to fail gracefully\n\nfunction toObj(val) {\n  if (val == null) return {};                 // Case 1: if content is null/undefined \u2192 return empty object\n  if (typeof val === 'object') return val;    // Case 2: if it's already parsed into an object \u2192 just return it\n\n  // Otherwise, assume it's a string\n  let txt = String(val).trim();               // Convert to string & trim whitespace\n\n  // Remove markdown code fences ```json ... ```\n  txt = txt.replace(/^```(?:json)?\\s*/i, '')  // Remove opening ``` or ```json\n           .replace(/```$/,'')                // Remove closing ```\n           .trim();\n\n  // Some models add \"json\\n{...}\" before the object \u2192 remove the leading \"json\"\n  txt = txt.replace(/^json\\s*/i, '');\n\n  // --- Attempt parsing in several ways ---\n\n  // Attempt #1: Normal JSON.parse\n  try { \n    return JSON.parse(txt); \n  } catch {}\n\n  // Attempt #2: Double-encoded JSON\n  // Example: \"\\\"{ \\\\\\\"key\\\\\\\":123 }\\\"\" \u2192 needs two parses\n  try { \n    return JSON.parse(JSON.parse(txt)); \n  } catch {}\n\n  // Attempt #3: Extract first {...} block and try parsing that\n  // Useful if there\u2019s extra text around the JSON\n  const m = txt.match(/\\{[\\s\\S]*\\}$/);   // greedy match from first { to last }\n  if (m) { \n    try { return JSON.parse(m[0]); } catch {} \n  }\n\n  // If all parsing attempts fail, return a fallback object\n  return { \n    _raw: txt,                         // Keep the raw string so we don't lose information\n    _error: 'could_not_parse_json'     // Add a flag for debugging\n  };\n}\n\n// --- Main execution starts here ---\n// $json is the current item in n8n\n// We check for the field \"content\" (or sometimes LLM outputs live under message.content)\nconst content = $json.content ?? $json.message?.content ?? '';\n\n// Apply the parser to try to normalize content into an object\nconst obj = toObj(content);\n\n// Return the result as n8n output\n// - weeklyplan_joined: always a string version (good for downstream LLM prompts)\n// - parsed_weekly: the actual parsed object (good for structured handling)\nreturn [{\n  json: {\n    weeklyplan_joined: typeof obj === 'string' ? obj : JSON.stringify(obj),\n    parsed_weekly: obj\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "63fc8f6b-5f4a-4ced-9b3a-c202490fa224",
      "name": "Quarterly Code",
      "type": "n8n-nodes-base.code",
      "position": [
        400,
        768
      ],
      "parameters": {
        "jsCode": "const blocks = $items('Filter2').map((i, idx) => {\n  const j = i.json;\n  return [\n    `=== OKR ${idx + 1} ===`,\n    `Objective: ${j.property_objective || '(missing)'}`,\n    `Key Result: ${j.property_key_result || '(missing)'}`,\n    `Status: ${j.property_status || 'Unknown'}`\n  ].join('\\n');\n});\n\nreturn [{ json: { okrs_joined: blocks.join('\\n\\n') } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e129ca79-4521-47ed-9ac1-c6665b8ee026",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        1152,
        160
      ],
      "parameters": {
        "mode": "chooseBranch",
        "output": "empty"
      },
      "typeVersion": 3.2
    },
    {
      "id": "ab0b5344-c5ba-49ed-88a1-2006be862d11",
      "name": "Merge1",
      "type": "n8n-nodes-base.merge",
      "position": [
        1152,
        592
      ],
      "parameters": {
        "mode": "chooseBranch",
        "output": "empty"
      },
      "typeVersion": 3.2
    },
    {
      "id": "a8ca2567-3d89-40a9-9103-0f904fa0f1d6",
      "name": "Merge2",
      "type": "n8n-nodes-base.merge",
      "position": [
        1408,
        384
      ],
      "parameters": {
        "mode": "chooseBranch",
        "output": "empty"
      },
      "typeVersion": 3.2
    },
    {
      "id": "2e23de28-730f-446e-aa18-9710891d1142",
      "name": "Send a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2080,
        384
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "={{$json[\"message\"][\"content\"]}}",
        "options": {},
        "subject": "Weekly OKR Report"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "18ef3024-0679-42cc-9a73-a62a547fdd38",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "notes": "0 */1 * * * *\n\n0 14 10 * * 6",
      "position": [
        -336,
        416
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 30 20 * * 6"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "3ea6bd19-c77b-4285-8732-5ddbb1f3d702",
      "name": "Gmail Filter",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        0
      ],
      "parameters": {
        "jsCode": "const isNoise = (item) => {\n  const labels = item.json.labels?.map(l => l.name.toUpperCase()) || [];\n  const subject = (item.json.Subject || '').toLowerCase();\n\n  // Exclude by labels\n  const hasBadLabel = labels.some(l =>\n    ['CATEGORY_PROMOTIONS', 'CATEGORY_SOCIAL', 'SPAM', 'n8n', 'TRASH'].includes(l)\n  );\n\n  // Exclude by subject (receipts)\n  const isReceipt = subject.includes('receipt') || subject.includes('receipts');\n\n  return hasBadLabel || isReceipt;\n};\n\n// Filter\nconst filtered = items.filter(item => !isNoise(item));\n\n// Fallback: if empty, return dummy\nif (filtered.length === 0) {\n  return [{\n    json: {\n      Subject: '',\n      From: '',\n      body: 'No human-relevant emails this week.',\n    }\n  }];\n}\n\nreturn filtered;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "23a5c0bd-7d15-48ee-8ec6-0115e85b720c",
      "name": "Get quarterly okr database",
      "type": "n8n-nodes-base.notion",
      "position": [
        0,
        768
      ],
      "parameters": {
        "options": {},
        "resource": "databasePage",
        "operation": "getAll",
        "returnAll": true,
        "databaseId": {
          "__rl": true,
          "mode": "list",
          "value": "260741de-415a-80a8-bdc9-d7bb7cf4b66e",
          "cachedResultUrl": "[REDACTED_NOTION_URL]",
          "cachedResultName": "QuarterlyPlan"
        }
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "882db05a-6f91-4554-8196-1fd41e3e1ac2",
      "name": "Get weekly plan database",
      "type": "n8n-nodes-base.notion",
      "position": [
        0,
        512
      ],
      "parameters": {
        "options": {},
        "resource": "databasePage",
        "operation": "getAll",
        "returnAll": true,
        "databaseId": {
          "__rl": true,
          "mode": "list",
          "value": "261741de-415a-8011-9ec5-e9988d4f414e",
          "cachedResultUrl": "[REDACTED_NOTION_URL]",
          "cachedResultName": "WeeklyPlan"
        }
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "207b11e3-0b9b-4df4-92c2-7e9a68b49046",
      "name": "Message a model1",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1680,
        384
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini",
          "cachedResultName": "GPT-4.1-MINI"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=# Inputs\n\n## OKRs\n{{ $node[\"Quarterly Code\"].json[\"okrs_joined\"] }}\n\n## Calendar\n{{ $node[\"Calendar Code\"].json[\"calendar_joined\"] }}\n\n## Weekly Plan\n{{ $node[\"Weekly Code\"].json[\"weeklyplan_joined\"] }}\n\n## Emails\n{{ $node[\"Gmail Code\"].json[\"emails_joined\"] }}\n\n\n# Tasks\n1. Check alignment across OKRs, emails, calendar, and weekly plan.\n    * Treat OKRs as the North Star.\n    * Use emails, calendar events, and weekly plan tasks as evidence of actual work.\n    * Look for alignment (e.g., a weekly task reflected in an email thread).\n    * Also recognize complementary items (e.g., a short task that appears only on weekly plan but not blocked on calendar).\n    * Goal: form a comprehensive view of progress.\n2. Assess execution realism for the week.\n    * Highlight over- or under-scheduling, conflicts, missing preparation, and dependencies.\n    * Identify what advances each OKR, and flag any OKRs falling behind due to lack of attention or communication.\n    * Call out gaps and risks (e.g., a key result with no calendar time or email activity, or a due date approaching).\n3. Extract time-sensitive asks and commitments from emails.\n    * Note replies that are delayed or missing.\n    * Highlight urgent follow-ups to avoid overlooking important threads.\n4. Suggest concrete next steps.\n    * Recommend actions that keep execution realistic and OKRs on track.\n\n\n# Output format\nReturn the entire output as valid HTML (no Markdown). Follow this structure:\n\n<h3>Summary</h3>\n<ul>\n  <li>4\u20136 key themes of the week.</li>\n</ul>\n\n<h3>Alignment Check</h3>\n<table border=\"1\" cellpadding=\"6\" cellspacing=\"0\" style=\"border-collapse:collapse; width:100%;\">\n  <tr>\n    <th>OKR</th>\n    <th>Weekly Plan Tasks</th>\n    <th>Calendar Events</th>\n    <th>Related Emails / Notes</th>\n  </tr>\n  <!-- Add one <tr> row per OKR -->\n</table>\n\n<h3>Email Follow-ups</h3>\n<ul>\n  <li>List urgent or overdue responses</li>\n</ul>\n\n<h3>Next Steps</h3>\n<ol>\n  <li>3\u20135 clear actions for the coming week</li>\n</ol>\n"
            },
            {
              "role": "system",
              "content": "You are an assistant that checks alignment across OKRs, emails, calendar, and weekly plans. \nAlways return valid HTML (no Markdown). \nTreat OKRs as the North Star for evaluation.\nMaintain realistic scope.\n    * Remember you are an assistant tool with limited background context.\n    * Avoid making unsupported assumptions or inventing details.\n    * High-level or abstract insights are still valuable."
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    }
  ],
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge1": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge2": {
      "main": [
        [
          {
            "node": "Message a model1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter1": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter2": {
      "main": [
        [
          {
            "node": "Quarterly Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Code": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Gmail Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Weekly Code": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields1": {
      "main": [
        [
          {
            "node": "Calendar Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Filter": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Analyze image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze image": {
      "main": [
        [
          {
            "node": "Weekly Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calendar Code": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Quarterly Code": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Get many events": {
      "main": [
        [
          {
            "node": "Edit Fields1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Message a model1": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get many messages",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get many events",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get weekly plan database",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get quarterly okr database",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get many messages": {
      "main": [
        [
          {
            "node": "Gmail Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get weekly plan database": {
      "main": [
        [
          {
            "node": "Filter1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get quarterly okr database": {
      "main": [
        [
          {
            "node": "Filter2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow is your personal CEO Brain. Every Saturday night, it automatically collects the past week’s activity across: 📩 Gmail: filters out spam, promos, receipts, etc. 📅 Google Calendar: grabs past week and upcoming month 🗒️ Notion Weekly Plan: pulls and analyzes a photo of…

Source: https://n8n.io/workflows/8341/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Stop wasting billable hours on manual time-tracking. AutoTimesheet Pro uses AI to collect emails, meetings, and GitHub work, then writes a clean timesheet straight into Google Sheets. Perfect for deve

Google Calendar, Gmail, GitHub +3
AI & RAG

Daily trigger scans your Notion database for unpublished blog ideas AI generates complete blog posts + engaging LinkedIn content using OpenAI (Blog Posting is not implemented yet) Creates custom image

Notion, Error Trigger, Gmail +3
AI & RAG

Who is this for This workflow is perfect for busy professionals, consultants, and anyone who frequently travels between meetings. If you want to make the most of your free time between appointments an

Google Calendar, Notion, OpenWeatherMap +3
AI & RAG

Manually monitoring Reddit for relevant discussions can be overwhelming. This automation does all the heavy lifting by automatically searching for keywords across selected subreddits or the entire Red

Gmail, HTTP Request, Notion +1
AI & RAG

This automated n8n workflow generates social media briefs for sales meetings by integrating with Google Calendar, enriching attendee data, and leveraging AI to summarize social media activity. The sys

Clearbit, Google Calendar, HTTP Request +3