{
  "name": "Google Calendar to Notion Daily Agenda",
  "nodes": [
    {
      "parameters": {
        "content": "## Google Calendar to Notion Daily Agenda: Sync Events Automatically\n\nAutomatically create a daily agenda page in Notion from your Google Calendar events, helping you plan and visualize your day ahead.\n\n**Setup time:** 15 minutes  ·  **Level:** Intermediate\n\n**You'll need accounts/credentials for:** Google Calendar, Notion\n\n**Replace these before running:**\n- `REPLACE_WITH_DATABASE_ID`\n\n**Setup steps:**\n1. Connect Google Calendar and Notion in their respective nodes.\n2. Replace REPLACE_WITH_DATABASE_ID with your target Notion database ID.\n3. Customize the event formatting in the Code node to match your preferences.\n4. Test by running the workflow manually and checking the result in Notion.\n5. Activate the schedule to run automatically each morning.\n\nFree templates and updates: navkhan103.github.io",
        "height": 300,
        "width": 400,
        "color": 4
      },
      "id": "sticky-setup",
      "name": "Setup — read me first",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -440,
        -120
      ]
    },
    {
      "parameters": {
        "content": "### How it runs\n\nStarts from **Schedule Trigger**.\n\n1. A Schedule Trigger runs every morning to fetch today's events.\n2. A Google Calendar node retrieves all events for the current day.\n3. A Code node formats the events into Notion blocks with proper structure.\n4. A Notion node creates a new daily agenda page with the formatted events.\n5. The resulting page shows your complete day's schedule in Notion format.",
        "height": 300,
        "width": 400,
        "color": 7
      },
      "id": "sticky-how",
      "name": "How it runs",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -440,
        220
      ]
    },
    {
      "parameters": {
        "content": "### If something breaks\n\nThe read and AI nodes here **retry 3× automatically** (5s apart), which absorbs rate limits and brief timeouts.\n\nNodes that **send or write deliberately do not retry** — a send that reached the far end but lost its response would otherwise fire twice, so you'd get duplicate messages or duplicate rows.\n\nTo be told when one of those fails, build a workflow starting with an **Error Trigger** node that posts to Slack or email, then set it under **three-dot menu > Settings > Error Workflow**. Do it once and point every workflow at it.",
        "height": 380,
        "width": 400,
        "color": 3
      },
      "id": "sticky-fail",
      "name": "If something breaks",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -440,
        560
      ]
    },
    {
      "parameters": {
        "rule": {
          "times": [
            {
              "hour": 6,
              "minute": 0
            }
          ],
          "daysOfMonth": "*",
          "months": "*",
          "weekdays": "*"
        }
      },
      "id": "f9a0b1c2-0001-4000-8000-000000000001",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "calendarId": "primary",
        "options": {
          "timeMin": "={{ new Date(new Date().setHours(0,0,0,0)).toISOString() }}",
          "timeMax": "={{ new Date(new Date().setHours(23,59,59,999)).toISOString() }}",
          "showDeleted": false
        }
      },
      "id": "f9a0b1c2-0002-4000-8000-000000000002",
      "name": "Get Today's Events",
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [
        220,
        0
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "jsCode": "// Format today's date for the page title\nconst today = new Date();\ntoday.setHours(0, 0, 0, 0);\nconst dateStr = today.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });\n\n// Prepare Notion blocks for each event\nconst events = $input.all().map(item => item.json);\nconst blocks = [];\n\n// Add title block\nblocks.push({\n  object: 'block',\n  type: 'heading_1',\n  heading_1: {\n    rich_text: [{\n      type: 'text',\n      text: { content: `📅 ${dateStr}` }\n    }]\n  }\n});\n\n// Sort events by start time\nevents.sort((a, b) => new Date(a.start.dateTime || a.start.date) - new Date(b.start.dateTime || b.start.date));\n\n// Add each event as a block\nfor (const event of events) {\n  const start = new Date(event.start.dateTime || event.start.date);\n  const end = new Date(event.end.dateTime || event.end.date);\n  \n  // Format time\n  const timeStr = `${start.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - ${end.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`;\n  \n  // Add event block\n  blocks.push({\n    object: 'block',\n    type: 'paragraph',\n    paragraph: {\n      rich_text: [\n        {\n          type: 'text',\n          text: { content: `${timeStr} | ${event.summary}` },\n          annotations: { bold: true }\n        }\n      ]\n    }\n  });\n  \n  // Add description if available\n  if (event.description) {\n    blocks.push({\n      object: 'block',\n      type: 'bulleted_list_item',\n      bulleted_list_item: {\n        rich_text: [{\n          type: 'text',\n          text: { content: event.description.substring(0, 100) + (event.description.length > 100 ? '...' : '') }\n        }]\n      }\n    });\n  }\n}\n\n// If no events, add a note\nif (events.length === 0) {\n  blocks.push({\n    object: 'block',\n    type: 'paragraph',\n    paragraph: {\n      rich_text: [{\n        type: 'text',\n        text: { content: 'No events scheduled for today' }\n      }]\n    }\n  });\n}\n\nreturn [{ json: {\n  title: dateStr,\n  blocks: blocks\n} }];"
      },
      "id": "f9a0b1c2-0003-4000-8000-000000000003",
      "name": "Format for Notion",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "databaseId": "REPLACE_WITH_DATABASE_ID",
        "properties": {
          "title": "={{ $json.title }}"
        },
        "content": "={{ $json.blocks }}"
      },
      "id": "f9a0b1c2-0004-4000-8000-000000000004",
      "name": "Create Daily Page",
      "type": "n8n-nodes-base.notion",
      "typeVersion": 2.2,
      "position": [
        660,
        0
      ]
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get Today's Events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Today's Events": {
      "main": [
        [
          {
            "node": "Format for Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format for Notion": {
      "main": [
        [
          {
            "node": "Create Daily Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}