AutomationFlowsWeb Scraping › Smb Demo - Google Calendar to Copilot Suggestion

Smb Demo - Google Calendar to Copilot Suggestion

SMB Demo - Google Calendar to Copilot Suggestion. Uses googleCalendar, httpRequest. Scheduled trigger; 4 nodes.

Cron / scheduled trigger★★★★☆ complexity4 nodesGoogle CalendarHTTP Request
Web Scraping Trigger: Cron / scheduled Nodes: 4 Complexity: ★★★★☆ Added:

This workflow follows the Google Calendar → HTTP Request 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
{
  "name": "SMB Demo - Google Calendar to Copilot Suggestion",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "id": "199d75bb-fd97-461d-a754-ea54124b35aa",
      "name": "Every 15 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -720,
        0
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "calendar": {
          "__rl": true,
          "value": "={{ $env.GOOGLE_CALENDAR_ID }}",
          "mode": "id"
        },
        "returnAll": false,
        "limit": 20,
        "options": {
          "timeMin": "={{ $now.minus({ minutes: 20 }).toISO() }}",
          "timeMax": "={{ $now.plus({ days: 1 }).toISO() }}",
          "singleEvents": true
        }
      },
      "id": "b763d633-c11a-49fe-a888-bd900712bdb5",
      "name": "Read Dedicated Calendar (readonly credential required)",
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [
        -480,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const crypto = require('crypto');\nconst secret = $env.COPILOT_INGEST_HMAC_SECRET;\nif (!secret) throw new Error('COPILOT_INGEST_HMAC_SECRET is required');\nconst integrationId = 'n8n-calendar-v1';\nconst endpointPath = '/api/v1/integrations/work-events:ingest';\nconst sourceAccountRef = $env.GOOGLE_SOURCE_ACCOUNT_REF;\nconst calendarRef = $env.GOOGLE_CALENDAR_REF;\nif (!sourceAccountRef || !calendarRef) throw new Error('Google source references are required');\nreturn items.filter(({json:e}) => e.extendedProperties?.private?.acme_work_event === 'v1').map(({json:e}) => {\n  const p = e.extendedProperties.private;\n  const start = new Date(e.start?.dateTime || '');\n  const end = new Date(e.end?.dateTime || '');\n  const duration = Math.round((end - start) / 60000);\n  const event = {schema_version:'1.0',source:'google_calendar',source_account_ref:sourceAccountRef,calendar_id:calendarRef,event_id:String(e.id || ''),event_updated_at:String(e.updated || ''),person_ref:String(p.person_ref || ''),project_code:String(p.project_code || ''),work_date:start.toISOString().slice(0,10),duration_minutes:duration,description:String(p.work_description || '')};\n  if (!/^[a-z0-9-]{3,64}$/.test(event.person_ref) || !/^[A-Z0-9_-]{2,32}$/.test(event.project_code) || !event.event_id || !/^\\d{4}-\\d{2}-\\d{2}T/.test(event.event_updated_at) || duration < 15 || duration > 1440 || duration % 15 || !event.description || event.description.length > 200) throw new Error('Calendar event failed bounded WorkEvent validation');\n  const body = JSON.stringify(event);\n  const timestamp = Math.floor(Date.now()/1000).toString();\n  const nonce = crypto.randomUUID();\n  const bodyHash = crypto.createHash('sha256').update(body).digest('hex');\n  const base = ['v1',timestamp,nonce,'POST',endpointPath,bodyHash].join('\\n');\n  const signature = 'v1=' + crypto.createHmac('sha256',secret).update(base).digest('hex');\n  const revision = crypto.createHash('sha256').update(['gcal:v1',event.calendar_id,event.event_id,event.event_updated_at].join('\\0')).digest('hex');\n  return {json:{body,headers:{'Content-Type':'application/json','X-Acme-Integration-Id':integrationId,'X-Acme-Timestamp':timestamp,'X-Acme-Nonce':nonce,'X-Acme-Idempotency-Key':'sha256:'+revision,'X-Acme-Signature':signature}}};\n});"
      },
      "id": "11f28a65-8101-45c6-99d8-d10ad3794305",
      "name": "Filter Map Validate and Sign WorkEvent",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -208,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.COPILOT_INGEST_URL }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "={{ $json.headers['Content-Type'] }}"
            },
            {
              "name": "X-Acme-Integration-Id",
              "value": "={{ $json.headers['X-Acme-Integration-Id'] }}"
            },
            {
              "name": "X-Acme-Timestamp",
              "value": "={{ $json.headers['X-Acme-Timestamp'] }}"
            },
            {
              "name": "X-Acme-Nonce",
              "value": "={{ $json.headers['X-Acme-Nonce'] }}"
            },
            {
              "name": "X-Acme-Idempotency-Key",
              "value": "={{ $json.headers['X-Acme-Idempotency-Key'] }}"
            },
            {
              "name": "X-Acme-Signature",
              "value": "={{ $json.headers['X-Acme-Signature'] }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ $json.body }}",
        "options": {
          "timeout": 10000
        }
      },
      "id": "98a23263-43d0-4f05-9776-a3501c2740d0",
      "name": "Create Suggestion Only",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        96,
        0
      ]
    }
  ],
  "connections": {
    "Every 15 Minutes": {
      "main": [
        [
          {
            "node": "Read Dedicated Calendar (readonly credential required)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Dedicated Calendar (readonly credential required)": {
      "main": [
        [
          {
            "node": "Filter Map Validate and Sign WorkEvent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Map Validate and Sign WorkEvent": {
      "main": [
        [
          {
            "node": "Create Suggestion Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "b97567bd-b8cd-4a8e-a1d8-2f4509be8636",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
Pro

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

About this workflow

SMB Demo - Google Calendar to Copilot Suggestion. Uses googleCalendar, httpRequest. Scheduled trigger; 4 nodes.

Source: https://github.com/lh229470989/workforce-operations-copilot/blob/main/integrations/n8n/workflow-a-calendar-ingest.json — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

[TEMPLATE] Full Class -> Calendar Sync. Uses httpRequest, googleCalendar. Scheduled trigger; 24 nodes.

HTTP Request, Google Calendar
Web Scraping

Teams that track absences in Everhour and want a shared Google Calendar view for quick planning. Ideal for managers, HR/OPS, and teammates who need instant visibility into approved time off. Pulls app

HTTP Request, Google Calendar
Web Scraping

🕌 How it works

Google Calendar, HTTP Request
Web Scraping

This workflow runs every 15 minutes (configurable) to mirror events from an ICS/webcal feed into a Google Calendar by fetching and parsing the ICS file, comparing it with existing calendar events, and

HTTP Request, Google Calendar
Web Scraping

This workflow automates the process of finding local events and adding them directly to your Google Calendar. It eliminates the need for manual event tracking by automatically scraping event informati

HTTP Request, Google Calendar