This workflow corresponds to n8n.io template #12419 — 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 →
{
"id": "VDszM3EhszJTQHuk",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Schedule appointments using Google Calendar and Gmail",
"tags": [],
"nodes": [
{
"id": "29aab469-1f9f-452d-8141-5059af69fffc",
"name": "Receive appointment request",
"type": "n8n-nodes-base.webhook",
"position": [
160,
352
],
"parameters": {
"path": "appointment-request",
"options": {
"rawBody": false,
"ignoreBots": true,
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "2bf0d6da-e840-4599-b793-8fbf23babcfd",
"name": "Format appointment data",
"type": "n8n-nodes-base.set",
"position": [
384,
352
],
"parameters": {
"include": "selected",
"options": {
"dotNotation": false
},
"assignments": {
"assignments": [
{
"id": "0e263997-2422-4470-a2d8-75e948d9263b",
"name": "name",
"type": "string",
"value": "={{ $json.body.name }}"
},
{
"id": "7ada9c8f-f6bb-43c7-9f85-2a0f0a647dc4",
"name": "company",
"type": "string",
"value": "={{ $json.body.organization }}"
},
{
"id": "ef270c99-ac86-4f4e-b0e0-ab90954081d9",
"name": "email",
"type": "string",
"value": "={{ $json.body.email }}"
},
{
"id": "7dd61002-b455-4e00-997c-8bcdc6fb55c4",
"name": "phone",
"type": "string",
"value": "={{ $json.body.phone }}"
},
{
"id": "5273013e-ad6f-4ae0-ac2e-4a5de27d8bf0",
"name": "appointmentStartDate",
"type": "string",
"value": "={{ $json.body.appointmentDateTime }}"
},
{
"id": "0ee4cc66-e4d5-429a-81f6-fe14371589be",
"name": "appointmentEndDate",
"type": "string",
"value": "={{ DateTime.fromISO($json.body.appointmentDateTime).setZone('Asia/Kolkata').plus(1, 'hours') }}"
},
{
"id": "da4a897b-96f5-4008-a7cc-68f5dfa6d4a5",
"name": "message",
"type": "string",
"value": "={{$json.body.optionalMessage }}"
}
]
},
"includeFields": "body",
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "a0c4e8f2-ed59-466e-b020-13b4c763894d",
"name": "Check Availability",
"type": "n8n-nodes-base.googleCalendar",
"onError": "continueRegularOutput",
"position": [
608,
352
],
"parameters": {
"options": {
"outputFormat": "availability"
},
"timeMax": "={{ $json.appointmentEndDate }}",
"timeMin": "={{ $json.appointmentStartDate }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "user@example.com",
"cachedResultName": "Primary"
},
"resource": "calendar"
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"notesInFlow": false,
"typeVersion": 1.3,
"alwaysOutputData": true
},
{
"id": "8ae03768-acf8-4748-9d5e-804d2cc7fad3",
"name": "Is approved?",
"type": "n8n-nodes-base.if",
"position": [
1504,
256
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "2b5cc36c-b8b5-4a20-9fa7-499fa8c4a939",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{$json.data.approved }}",
"rightValue": "true"
}
]
}
},
"executeOnce": false,
"typeVersion": 2.2,
"alwaysOutputData": false
},
{
"id": "16e45971-fc70-4478-ac00-c5ce0eb66f48",
"name": "Appointment Failed",
"type": "n8n-nodes-base.gmail",
"position": [
1728,
352
],
"parameters": {
"sendTo": "={{ $('Format appointment data').item.json.body.email}}",
"message": "=Hi,{{ $('Format appointment data').item.json.name }}\n\nThe appointment could not be scheduled for the requested date and time.\n\nReason: The appointment was rejected by the approver.\n\nPlease reschedule your appointment or contact us directly by replying to this email.\n\nApologies for the inconvenience caused.\n\nRegards,\n<insert-your-org-name>\n",
"options": {
"bccList": "info@example.com",
"appendAttribution": false
},
"subject": "=RE: Appointment request with <insert-your-name> at {{ DateTime.fromISO($('Format appointment data').item.json.appointmentStartDate).toFormat('cccc, MMMM dd @ hh:mm a') }}\n\n",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "22a1d4e8-f83a-4e46-a883-7880c5968bbb",
"name": "Book Appointment",
"type": "n8n-nodes-base.googleCalendar",
"position": [
1728,
160
],
"parameters": {
"end": "={{ $('Format appointment data').item.json.appointmentEndDate }}",
"start": "={{ $('Format appointment data').item.json.appointmentStartDate }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "user@example.com",
"cachedResultName": "Primary"
},
"additionalFields": {
"color": "11",
"summary": "=Project discovery meet with {{ $('Format appointment data').item.json.body.name }} at {{ DateTime.fromISO($('Format appointment data').item.json.appointmentStartDate).toLocaleString(DateTime.DATETIME_MED) }}",
"attendees": [
"={{ $('Format appointment data').item.json.email }}",
"info@example.com"
],
"description": "=Hi {{$('Format appointment data').item.json.name}}\n\nYour appointment is confirmed, please find the attached event/meeting details.\n\nRegards,\n<insert-your-org-name>",
"sendUpdates": "all",
"conferenceDataUi": {
"conferenceDataValues": {
"conferenceSolution": "hangoutsMeet"
}
},
"guestsCanSeeOtherGuests": true
}
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "a3c0ede0-ee2a-4ef8-a8d4-4badeb6ee97d",
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
832,
352
],
"parameters": {
"options": {
"responseCode": 200,
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"respondWith": "json",
"responseBody": "={\n \"success\": \"{{ $json.available }}\",\n \"message\": \"{{ $json.available ? 'Thank you for your interest, Meeting details will be emailed shortly once approved.' : 'We cannot confirm the appointment at this time, Please try again with a different day/time-slot.' }}\"\n}"
},
"typeVersion": 1.4
},
{
"id": "a05231fc-9c65-4732-8e73-ecb5fb51cf19",
"name": "Is Slot Available?",
"type": "n8n-nodes-base.if",
"position": [
1056,
352
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "ca33df49-543c-4fc8-b357-47c83348871d",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $('Check Availability').item.json?.available }}",
"rightValue": "true"
}
]
}
},
"typeVersion": 2.2,
"alwaysOutputData": false
},
{
"id": "b7ec4e94-48e8-41d0-ae3f-3cfce5c88388",
"name": "Get Approval (HR) [1 hour wait]",
"type": "n8n-nodes-base.gmail",
"onError": "continueRegularOutput",
"position": [
1280,
256
],
"parameters": {
"sendTo": "info@example.com",
"message": "=Hi <insert-your-name>,\n{{ $('Format appointment data').item.json.body.name }} has requested to schedule an appointment with you at {{ DateTime.fromISO($('Format appointment data').item.json.appointmentStartDate).toFormat('cccc, MMMM dd @ hh:mm a') }}. \nHere are the customer information:\nName: {{ $('Format appointment data').item.json.name }}\nCompany: {{ $('Format appointment data').item.json.company }}\nContact Email: {{ $('Format appointment data').item.json.email }}\nMessage: {{ $('Format appointment data').item.json.message }}\n",
"options": {
"appendAttribution": false
},
"subject": "Approval Required",
"operation": "sendAndWait",
"approvalOptions": {
"values": {
"approvalType": "double"
}
}
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1,
"alwaysOutputData": false
},
{
"id": "42b3486c-f2d7-4336-839d-1789f7bae261",
"name": "End - Slot Busy",
"type": "n8n-nodes-base.noOp",
"position": [
1280,
448
],
"parameters": {},
"typeVersion": 1
},
{
"id": "fb697393-26eb-4bd2-8fe9-69566d1eb088",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
112,
-160
],
"parameters": {
"width": 672,
"height": 360,
"content": "## Smart Appointment Scheduler\n\n## How it works\n1. Receives appointment request via Webhook.\n2. Checks Google Calendar availability.\n3. If free, requests HR approval via Gmail.\n4. Sends confirmation or rejection email.\n\n## Setup steps\n1. Configure Google Calendar and Gmail credentials.\n2. Update Timezone in workflow settings.\n3. Send POST request with JSON body."
},
"typeVersion": 1
},
{
"id": "277da4b1-0ee9-49d9-8be6-eabfbc4aec17",
"name": "Sticky Note Group 1",
"type": "n8n-nodes-base.stickyNote",
"position": [
105,
248
],
"parameters": {
"color": 7,
"width": 430,
"height": 264,
"content": "## Step 1: Ingestion\nReceives and formats the request."
},
"typeVersion": 1
},
{
"id": "e9898219-fcaa-4c86-bd80-d28bbc1864a0",
"name": "Sticky Note Group 2",
"type": "n8n-nodes-base.stickyNote",
"position": [
572,
248
],
"parameters": {
"color": 7,
"width": 616,
"height": 264,
"content": "## Step 2: Availability\nChecks calendar availability and returns immediate response."
},
"typeVersion": 1
},
{
"id": "ac7ca2be-1159-4311-bc12-e495d4a5fea3",
"name": "Sticky Note Group 3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1244,
126
],
"parameters": {
"color": 7,
"width": 616,
"height": 482,
"content": "## Step 3: Approval & Booking\nHandles manual approval and booking."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"timezone": "Asia/Kolkata",
"callerPolicy": "workflowsFromSameOwner",
"timeSavedMode": "fixed",
"availableInMCP": false,
"executionOrder": "v1",
"executionTimeout": -1
},
"versionId": "480e31a5-f443-4925-b0ed-70ec7f2c80d8",
"connections": {
"Is approved?": {
"main": [
[
{
"node": "Book Appointment",
"type": "main",
"index": 0
}
],
[
{
"node": "Appointment Failed",
"type": "main",
"index": 0
}
]
]
},
"Check Availability": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Is Slot Available?": {
"main": [
[
{
"node": "Get Approval (HR) [1 hour wait]",
"type": "main",
"index": 0
}
],
[
{
"node": "End - Slot Busy",
"type": "main",
"index": 0
}
]
]
},
"Respond to Webhook": {
"main": [
[
{
"node": "Is Slot Available?",
"type": "main",
"index": 0
}
]
]
},
"Format appointment data": {
"main": [
[
{
"node": "Check Availability",
"type": "main",
"index": 0
}
]
]
},
"Receive appointment request": {
"main": [
[
{
"node": "Format appointment data",
"type": "main",
"index": 0
}
]
]
},
"Get Approval (HR) [1 hour wait]": {
"main": [
[
{
"node": "Is approved?",
"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.
gmailOAuth2googleCalendarOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Tired of automated booking tools cluttering your calendar with spam or overlapping meetings? SyncPoint is a “Smart Gatekeeper” that checks your availability and asks for your manual approval via email before a single event is booked. Once approved, it schedules the meeting…
Source: https://n8n.io/workflows/12419/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Complete Calendly automation that handles confirmations, cancellations and reschedules in a single workflow. WHAT IT DOES:
Automate legal appointment booking end-to-end. Clients request a slot via webhook, Google Calendar checks availability, Stripe collects payment, and a confirmed calendar event is created automatically
Automate your GoHighLevel (GHL) client onboarding process from the moment a deal is marked as “Won.” This workflow seamlessly generates client folders in Google Drive, duplicates contract and kickoff
Tg Leaves Webhook. Uses notion, googleCalendar, gmail. Webhook trigger; 21 nodes.
Automate clinic appointment booking end to end. Patients submit a request via webhook, the workflow generates a unique Booking ID, routes online consultations to Google Meet and offline ones to the cl