AutomationFlowsEmail & Gmail › Form to Meeting: Google Calendar, Zoom, Gmail & Slack Booking Automation

Form to Meeting: Google Calendar, Zoom, Gmail & Slack Booking Automation

ByTakuya Ojima @takuya on n8n.io

Who’s it for

Webhook trigger★★★★☆ complexity23 nodesGoogle CalendarZoomGmailSlack
Email & Gmail Trigger: Webhook Nodes: 23 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #10104 — 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
{
  "id": "f86tpZtlEF8Fxt6s",
  "name": "Automated Booking Confirmation and Meeting Scheduler",
  "tags": [],
  "nodes": [
    {
      "id": "3dd27b86-6e03-4595-a9fa-2fe3d1210611",
      "name": "Google Forms Webhook1",
      "type": "n8n-nodes-base.webhook",
      "position": [
        176,
        208
      ],
      "parameters": {
        "path": "google-forms-booking",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "lastNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "957b6711-281f-4599-9962-84ebf221691a",
      "name": "Merge Form Data1",
      "type": "n8n-nodes-base.merge",
      "position": [
        496,
        224
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "3b63fe73-a5e7-4f3e-bf61-e7bd0a9aa63c",
      "name": "Workflow Configuration1",
      "type": "n8n-nodes-base.set",
      "position": [
        848,
        224
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "calendarId",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "id-2",
              "name": "slackChannel",
              "type": "string",
              "value": "C09GN4NCTAP"
            },
            {
              "id": "id-3",
              "name": "teammateEmail",
              "type": "string",
              "value": "user@example.com"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "66b2895b-8260-4e40-b4ec-39b100b89f77",
      "name": "Extract Booking Details1",
      "type": "n8n-nodes-base.set",
      "position": [
        1168,
        224
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "customerName",
              "type": "string",
              "value": "={{ $json.body.name }}"
            },
            {
              "id": "id-2",
              "name": "customerEmail",
              "type": "string",
              "value": "={{ $json.body.email }}"
            },
            {
              "id": "id-3",
              "name": "bookingDate",
              "type": "string",
              "value": "={{ $json.body.date }}"
            },
            {
              "id": "id-4",
              "name": "bookingTime",
              "type": "string",
              "value": "={{ $json.body.time }}"
            },
            {
              "id": "34865dea-9db2-4f35-b91b-322572680869",
              "name": "bookingStartISO",
              "type": "string",
              "value": "={{ new Date($json.body.date + 'T' + $json.body.time + ':00+09:00').toISOString() }}"
            },
            {
              "id": "941d5f18-54fd-41ad-8f46-c9f9f13fb32f",
              "name": "bookingEndISO",
              "type": "string",
              "value": "={{ new Date(new Date($json.body.date + 'T' + $json.body.time + ':00+09:00').getTime() + 60*60*1000).toISOString() }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "6591a8f7-2400-4de8-9fbe-b35271faf1f8",
      "name": "Check Calendar Availability1",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        1472,
        224
      ],
      "parameters": {
        "options": {},
        "timeMax": "={{ $json.bookingEndISO }}",
        "timeMin": "={{ $json.bookingStartISO }}",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Workflow Configuration1').first().json.calendarId }}"
        },
        "operation": "getAll"
      },
      "typeVersion": 1.3,
      "alwaysOutputData": true
    },
    {
      "id": "d80641b0-9da6-491c-922f-d278c0f030c3",
      "name": "Is Time Slot Available?1",
      "type": "n8n-nodes-base.if",
      "position": [
        1824,
        224
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "14e9293a-f96c-45f1-b216-2c8aaf2fb3e2",
              "operator": {
                "type": "object",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "76a27cb5-7196-4564-a89a-85d5d0a41d52",
      "name": "Create Calendar Event1",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        2144,
        208
      ],
      "parameters": {
        "end": "={{ $('Extract Booking Details1').first().json.bookingEndISO }}",
        "start": "={{ $('Extract Booking Details1').first().json.bookingStartISO }}",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Workflow Configuration1').first().json.calendarId }}"
        },
        "additionalFields": {
          "summary": "=Booking with {{ $('Extract Booking Details1').first().json.customerName }}",
          "attendees": [
            "={{ $('Extract Booking Details1').first().json.customerEmail }}"
          ],
          "description": "=Customer: {{ $('Extract Booking Details1').first().json.customerName }} ({{ $('Extract Booking Details1').first().json.customerEmail }})"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "9f4ca36f-8f02-43ee-8630-222958201959",
      "name": "Create Zoom Meeting1",
      "type": "n8n-nodes-base.zoom",
      "position": [
        2416,
        208
      ],
      "parameters": {
        "topic": "=Booking with {{ $('Extract Booking Details1').first().json.customerName }}",
        "authentication": "oAuth2",
        "additionalFields": {
          "duration": 60,
          "timeZone": "UTC",
          "startTime": "={{ $('Extract Booking Details1').first().json.bookingStartISO }}"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a31d67fa-38b4-4276-a0ad-56d1069f95dd",
      "name": "Send Confirmation Email1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2720,
        208
      ],
      "parameters": {
        "sendTo": "={{ $('Extract Booking Details1').first().json.customerEmail }}",
        "message": "=Hi {{ $('Extract Booking Details1').first().json.customerName }},\n\nYour booking has been confirmed!\n\nDate & Time: {{ new Date($('Extract Booking Details1').first().json.bookingStartISO).toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' }) }}\n\nZoom Meeting Link: {{ $json.join_url }}\n\nLooking forward to meeting you!\n\nBest regards",
        "options": {},
        "subject": "=Booking Confirmed: {{ new Date($('Extract Booking Details1').first().json.bookingStartISO).toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' }) }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "b456c4f9-dcca-48a4-b750-f39e95df493f",
      "name": "Notify Teammate on Slack1",
      "type": "n8n-nodes-base.slack",
      "position": [
        3040,
        208
      ],
      "parameters": {
        "text": "=\ud83c\udf89 New booking confirmed!\n\n*Customer:* {{ $('Extract Booking Details1').first().json.customerName }} ({{ $('Extract Booking Details1').first().json.customerEmail }})\n*Date & Time:* {{ new Date($('Extract Booking Details1').first().json.bookingStartISO).toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' }) }}\n*Zoom Link:* {{ $json.join_url }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Workflow Configuration1').first().json.slackChannel }}"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    },
    {
      "id": "78d4ae92-b136-4bbe-ae52-a73a60c47a6c",
      "name": "Send Unavailable Email1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2144,
        512
      ],
      "parameters": {
        "sendTo": "={{ $('Extract Booking Details1').first().json.customerEmail }}",
        "message": "=Hi {{ $('Extract Booking Details1').first().json.customerName }},\n\nThank you for your booking request. Unfortunately, the requested time slot on {{ new Date($('Extract Booking Details1').first().json.bookingStartISO).toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' }) }} is not available.\n\nPlease submit a new booking request with an alternative time.",
        "options": {},
        "subject": "Time Slot Unavailable"
      },
      "typeVersion": 2.1
    },
    {
      "id": "9f8af7ba-09f1-46bb-9bc6-bb2d0b26d744",
      "name": "Sticky: Google Forms Webhook1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        96,
        -400
      ],
      "parameters": {
        "color": "white",
        "width": 768,
        "height": 528,
        "content": "Title: Automated Booking \u2192 Calendar + Zoom + Email + Slack\nWhat this workflow does\n\nCaptures booking submissions from a form and parses date/time.\n\nChecks Google Calendar for conflicts.\n\nIf free: creates a Calendar event \u2192 creates a Zoom meeting \u2192 emails the guest \u2192 notifies your team on Slack.\n\nIf busy: emails the guest to pick another time.\n\nFlow\nWebhook \u2192 Merge \u2192 Config \u2192 Extract details \u2192 Check availability \u2192 IF (free/busy) \u2192\n[Free] Create Calendar Event \u2192 Create Zoom \u2192 Email confirmation \u2192 Slack notify\n[Busy] Email unavailability\n\nNotes\n\nKeep all user-editable variables in the Set nodes for easy maintenance.\n\nDate handling assumes Asia/Tokyo input and converts to ISO for API calls.\n\nNo hardcoded credentials inside HTTP nodes; store secrets via Credentials."
      },
      "typeVersion": 1
    },
    {
      "id": "5eb1dc05-5c0f-44a0-9000-d4abdd74a981",
      "name": "Sticky: Merge Form Data1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        384
      ],
      "parameters": {
        "color": "white",
        "width": 256,
        "height": 176,
        "content": "Purpose: Entry point for bookings. Receives a POST payload from your form (name, email, date, time).\nOutput: Raw form fields under body.* used downstream."
      },
      "typeVersion": 1
    },
    {
      "id": "967bcc58-fc07-4e4c-8b17-5f2a99f9287f",
      "name": "Sticky: Workflow Configuration1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        400,
        384
      ],
      "parameters": {
        "color": "white",
        "width": 272,
        "height": 176,
        "content": "Purpose: Normalizes/forwards webhook data to keep a single item moving through the flow.\nTip: Use this as a \u201cbuffer\u201d if you ever add parallel inputs later."
      },
      "typeVersion": 1
    },
    {
      "id": "663ecdf9-6f9d-481c-abe6-e7d8b3c34021",
      "name": "Sticky: Extract Booking Details1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        736,
        384
      ],
      "parameters": {
        "color": "white",
        "width": 288,
        "height": 176,
        "content": "Purpose: Central place for editable constants (e.g., calendarId, slackChannel, teammateEmail).\nWhy: Makes the template plug-and-play\u2014update values here instead of hunting through nodes."
      },
      "typeVersion": 1
    },
    {
      "id": "c12bee38-9494-43b7-9961-2e4971a92316",
      "name": "Sticky: Check Calendar Availability1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1088,
        384
      ],
      "parameters": {
        "color": "white",
        "width": 272,
        "height": 176,
        "content": "Purpose: Maps form fields to clean variables: customerName, customerEmail, bookingDate, bookingTime.\nAlso: Builds bookingStartISO and bookingEndISO (60-min duration) using the submitted local time (JST) \u2192 ISO strings."
      },
      "typeVersion": 1
    },
    {
      "id": "b58f33ab-ad60-4a98-9354-3ed1a1aeab72",
      "name": "Sticky: Is Time Slot Available?1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1424,
        384
      ],
      "parameters": {
        "color": "white",
        "width": 256,
        "height": 176,
        "content": "Purpose: Queries the target calendar for events overlapping bookingStartISO\u2013bookingEndISO.\nBehavior: Returns an empty list when the slot is free; non-empty when it\u2019s taken."
      },
      "typeVersion": 1
    },
    {
      "id": "b0b7376c-f552-4ce9-bd31-33919ba04618",
      "name": "Sticky: Create Calendar Event1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1744,
        384
      ],
      "parameters": {
        "color": "white",
        "width": 256,
        "height": 176,
        "content": "Purpose: Branches on availability.\nTrue (empty result): Slot is free \u2192 proceed to create the event.\nFalse (has events): Slot is busy \u2192 send \u201cunavailable\u201d email."
      },
      "typeVersion": 1
    },
    {
      "id": "bb03b52c-ddc4-4896-9016-6d01ab6b68ec",
      "name": "Sticky: Create Zoom Meeting1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2000,
        16
      ],
      "parameters": {
        "color": "white",
        "width": 272,
        "content": "Purpose: Books the time on Google Calendar with the guest as an attendee.\nFields: Summary includes guest name; Description includes name + email for quick context."
      },
      "typeVersion": 1
    },
    {
      "id": "73736740-501f-4814-9722-e8576e2dd21f",
      "name": "Sticky: Send Confirmation Email1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2336,
        16
      ],
      "parameters": {
        "color": "white",
        "content": "Purpose: Creates a Zoom meeting for the confirmed slot.\nTiming: Uses the booking start time and a 60-minute duration.\nOutput: Provides join_url used by Email/Slack steps."
      },
      "typeVersion": 1
    },
    {
      "id": "6e9c22c3-0b31-47f9-ab96-4476e501175d",
      "name": "Sticky: Notify Teammate on Slack1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2640,
        16
      ],
      "parameters": {
        "color": "white",
        "width": 256,
        "content": "Purpose: Sends a confirmation to the guest with local (Japan) date/time and the Zoom link.\nSubject/Body: Human-readable datetime via toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' })."
      },
      "typeVersion": 1
    },
    {
      "id": "b9aa000c-63a2-44b1-800a-c041a1582f64",
      "name": "Sticky: Send Unavailable Email1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2064,
        672
      ],
      "parameters": {
        "color": "white",
        "width": 256,
        "content": "Purpose: Notifies the guest when the requested slot is already taken and asks them to rebook another time."
      },
      "typeVersion": 1
    },
    {
      "id": "3904238d-39eb-40f0-879f-3f17e1afd303",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2960,
        16
      ],
      "parameters": {
        "content": "Purpose: Posts a concise booking summary to your specified Slack channel.\nIncludes: Guest name/email, localized date/time, and Zoom join_url."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "31e08b0e-50a9-485b-b16c-856d2311d0f5",
  "connections": {
    "Merge Form Data1": {
      "main": [
        [
          {
            "node": "Workflow Configuration1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Zoom Meeting1": {
      "main": [
        [
          {
            "node": "Send Confirmation Email1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Forms Webhook1": {
      "main": [
        [
          {
            "node": "Merge Form Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Calendar Event1": {
      "main": [
        [
          {
            "node": "Create Zoom Meeting1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Configuration1": {
      "main": [
        [
          {
            "node": "Extract Booking Details1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Booking Details1": {
      "main": [
        [
          {
            "node": "Check Calendar Availability1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Time Slot Available?1": {
      "main": [
        [
          {
            "node": "Create Calendar Event1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Unavailable Email1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Confirmation Email1": {
      "main": [
        [
          {
            "node": "Notify Teammate on Slack1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Calendar Availability1": {
      "main": [
        [
          {
            "node": "Is Time Slot Available?1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

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

About this workflow

Who’s it for

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

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

Receive booking requests via webhook with automatic validation, duplicate detection, availability checking, confirmation emails, Google Calendar sync, and Slack notifications.

HTTP Request, Gmail, Google Calendar +2
Email & Gmail

This template is designed for freelancers, small businesses, and finance teams who need automated invoice management with intelligent payment follow-ups. Perfect for service providers, agencies, or an

Google Sheets, Gmail, Slack
Email & Gmail

Automate short-term trading research by generating high-quality trade ideas using MCP (Market Context Protocol) signals and AI-powered analysis. 📈🤖 This workflow evaluates market context, catalysts, m

Slack, Asana, HTTP Request +4
Email & Gmail

Automate building visitor management with secure verification, digital entry passes, and real-time security notifications.

N8N Nodes Verifiemail, HTTP Request, N8N Nodes Htmlcsstopdf +2
Email & Gmail

This workflow monitors partner API usage in real time and triggers alerts based on usage thresholds. It validates incoming data, calculates usage percentage and routes actions using a Switch node. Sla

Jira, Slack, Gmail