AutomationFlowsAI & RAG › Voice Appointment Booking with Vapi & Google Calendar

Voice Appointment Booking with Vapi & Google Calendar

Original n8n title: Voice Appointment Booking & Confirmation System with Vapi, Gemini & Google Calendar

ByHabeeb Mohammed @faiz on n8n.io

Build an intelligent appointment booking system that lets customers schedule appointments through natural voice conversations. This workflow connects Vapi AI assistant with Google Calendar to check availability, create appointments, and send professional confirmation emails…

Webhook trigger★★★★☆ complexityAI-powered13 nodesGoogle CalendarGoogle Gemini ChatAgentGmail
AI & RAG Trigger: Webhook Nodes: 13 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → Gmail 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": "pKu0Dq4aV8M4p86u",
  "name": "AI Voice Appointment Booking with Vapi and Google Calendar",
  "tags": [],
  "nodes": [
    {
      "id": "5b940f57-6821-4a66-8d41-de3af5ba3090",
      "name": "Webhook - Availability Checker",
      "type": "n8n-nodes-base.webhook",
      "position": [
        528,
        176
      ],
      "parameters": {
        "path": "availability-checker",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "9b3fec85-88a3-484e-b479-1e989347ee02",
      "name": "Return Availability to Vapi",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1552,
        176
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={\n    \"results\": [\n        {\n            \"toolCallId\": \"{{ $('Webhook - Availability Checker').item.json.body.message.toolCalls[0].id }}\",\n            \"result\": \"{{ $json.output }}\"\n        }\n    ]\n}\n "
      },
      "typeVersion": 1.4
    },
    {
      "id": "c3f98bab-d796-4778-a059-53a03c19fc87",
      "name": "Get Busy Slots from Calendar",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        752,
        176
      ],
      "parameters": {
        "options": {
          "timezone": {
            "__rl": true,
            "mode": "list",
            "value": "America/New_York",
            "cachedResultName": "America/New_York"
          },
          "outputFormat": "raw"
        },
        "timeMax": "={{ $json.body.message.toolCalls[0].function.arguments.date.toDateTime().endOf('day') }}",
        "timeMin": "={{ $json.body.message.toolCalls[0].function.arguments.date.toDateTime().startOf('day') }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Select your calendar"
        },
        "resource": "calendar"
      },
      "typeVersion": 1.3
    },
    {
      "id": "697fff8f-401c-4d51-a552-4e1391781e82",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        1280,
        288
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "8aa95da9-bef3-495c-97aa-596e646197f8",
      "name": "Format Busy Slots",
      "type": "n8n-nodes-base.code",
      "position": [
        976,
        176
      ],
      "parameters": {
        "jsCode": "// Get the busy array from Google Calendar\nconst calendarId = Object.keys($json.calendars)[0];\nconst busySlots = $json.calendars[calendarId].busy || [];\n\n// Convert each slot into a readable text line\nconst formattedSlots = busySlots\n  .map(slot => `\u2022 ${slot.start} \u2192 ${slot.end}`)\n  .join(\"\\n\");\n\n// Return clean text to feed the AI Agent\nreturn [\n  {\n    json: {\n      busySlots: formattedSlots,\n      rawBusy: busySlots\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "dd8c1c3a-8693-453a-8422-b046b804e2ca",
      "name": "Webhook - Create Appointment",
      "type": "n8n-nodes-base.webhook",
      "position": [
        528,
        560
      ],
      "parameters": {
        "path": "create-appointment",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "5b076b01-15dd-46d6-9a52-2b90a7f37b10",
      "name": "Return Confirmation to Vapi",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1200,
        560
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={\n    \"results\": [\n        {\n            \"toolCallId\": \"{{ $('Webhook - Create Appointment').item.json.body.message.toolCallList[0].id }}\",\n            \"result\": \"Appointment confirmed and confirmation email sent to {{ $('Webhook - Create Appointment').item.json.body.message.toolCalls[0].function.arguments.Email }}\"\n        }\n    ]\n} "
      },
      "typeVersion": 1.4
    },
    {
      "id": "d33ff9f7-c7ff-4efe-b6ef-8c87fc93944a",
      "name": "Create Calendar Event",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        752,
        560
      ],
      "parameters": {
        "end": "={{ $json.body.message.toolCalls[0].function.arguments[\"date and time\"].toDateTime().plus(30, 'mins') }}",
        "start": "={{ $json.body.message.toolCalls[0].function.arguments[\"date and time\"].toDateTime() }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Select your calendar"
        },
        "additionalFields": {
          "summary": "={{ $json.body.message.toolCalls[0].function.arguments[\"Appointment type\"] }} Appointment for {{ $json.body.message.toolCalls[0].function.arguments.Name }}",
          "description": "=Appointment type: {{ $json.body.message.toolCalls[0].function.arguments[\"Appointment type\"] }}\nEmail: {{ $json.body.message.toolCalls[0].function.arguments.Email }}"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "998f66d4-dbf5-4e23-b39f-c54492ad0387",
      "name": "AI Availability Analyzer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1200,
        64
      ],
      "parameters": {
        "text": "=These are the busy slots of the day: {{ $json.busySlots }}\nHere is the day for which the user is looking to book: {{ $('Webhook - Availability Checker').item.json.body.message.toolCalls[0].function.arguments.date }}",
        "options": {
          "systemMessage": "You are a helpful assistant. Your job is to take the busy/booked time slots of the day and return the available time slots for booking. We are open from 9:00am to 6:00pm. You just return the available times in plain language and nothing else.\n\nExample: \"Available from 9:00am to 1:00pm and 3:00pm to 6:00pm\""
        },
        "promptType": "define"
      },
      "typeVersion": 2.2
    },
    {
      "id": "455139f4-1ff1-443c-bf87-6426cb59e49f",
      "name": "Send Confirmation Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        976,
        560
      ],
      "parameters": {
        "sendTo": "={{ $('Webhook - Create Appointment').item.json.body.message.toolCalls[0].function.arguments.Email }}",
        "message": "={{ '<!doctype html><html lang=\"en\" style=\"margin:0;padding:0;\"><head><meta charset=\"utf-8\"/><meta name=\"x-apple-disable-message-reformatting\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/><title>Appointment Confirmation</title><style>@media (max-width: 600px){.container{width:100%!important;}.px-24{padding-left:16px!important;padding-right:16px!important;}.py-24{padding-top:16px!important;padding-bottom:16px!important;}}</style></head><body style=\"margin:0;padding:0;background:#f5f7fb;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;\"><div style=\"display:none;max-height:0;overflow:hidden;opacity:0;mso-hide:all;\">Your appointment is confirmed.</div><table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"background:#f5f7fb;\"><tr><td align=\"center\" style=\"padding:24px;\"><table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" width=\"600\" class=\"container\" style=\"width:600px;max-width:100%;background:#ffffff;border-radius:12px;overflow:hidden;box-shadow:0 2px 10px rgba(16,24,40,0.08);\"><tr><td align=\"left\" class=\"px-24 py-24\" style=\"padding:24px;background:#0ea5e9;color:#ffffff;font-family:Segoe UI,Roboto,Helvetica,Arial,sans-serif;\"><h1 style=\"margin:0;font-size:22px;line-height:28px;font-weight:700;\">Your Business Name</h1><p style=\"margin:8px 0 0;font-size:14px;line-height:20px;opacity:0.95;\">Appointment Confirmation</p></td></tr><tr><td class=\"px-24 py-24\" style=\"padding:24px;font-family:Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#101828;\"><p style=\"margin:0 0 16px;font-size:16px;line-height:24px;\">Hello, <strong>' + $('Webhook - Create Appointment').item.json.body.message.toolCalls[0].function.arguments.Name + '</strong> \u2014 thank you for choosing us.</p><p style=\"margin:0 0 16px;font-size:16px;line-height:24px;\">Your <strong>' + $('Webhook - Create Appointment').item.json.body.message.toolCalls[0].function.arguments['Appointment type'] + '</strong> appointment for <strong>' + $json.start.dateTime.toDateTime().toFormat('DDDD') + ' at ' + $json.start.dateTime.toDateTime().toFormat('t ZZZZ') + '</strong> is confirmed!</p><table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"margin:16px 0 0;border:1px solid #e5e7eb;border-radius:10px;\"><tr><td style=\"padding:16px;\"><p style=\"margin:0;font-size:14px;line-height:20px;color:#475467;\">If you need to reschedule or have any questions, just reply to this email.</p></td></tr></table><table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:24px 0 0;\"><tr><td><a href=\"#\" style=\"display:inline-block;text-decoration:none;background:#0ea5e9;color:#ffffff;padding:12px 18px;border-radius:8px;font-size:14px;line-height:20px;font-weight:600;\">View Appointment</a></td></tr></table></td></tr><tr><td class=\"px-24 py-24\" style=\"padding:20px 24px 28px;font-family:Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#475467;font-size:12px;line-height:18px;background:#ffffff;\"><p style=\"margin:0 0 8px;\">\u00a9 Your Business Name. All rights reserved.</p><p style=\"margin:0;\">This email was sent regarding your confirmed appointment.</p></td></tr></table></td></tr></table></body></html>' }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "Appointment Confirmation"
      },
      "typeVersion": 2.1
    },
    {
      "id": "bbc8d5f0-45ff-402e-805a-d977125486e7",
      "name": "Setup Instructions",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -16,
        16
      ],
      "parameters": {
        "width": 477,
        "height": 1443,
        "content": "## AI Voice Appointment Booking with Vapi and Google Calendar\n\nThis workflow enables voice-based appointment scheduling through Vapi AI assistant integration with Google Calendar.\n\n### What This Workflow Does\n\n**Availability Checker Tool** (Top Section):\n- Receives date from Vapi when user asks about availability\n- Fetches busy slots from Google Calendar for that day\n- Formats the data and sends it to an AI agent\n- AI agent analyzes busy times and returns available booking slots\n- Returns formatted availability back to Vapi\n\n**Appointment Creator Tool** (Bottom Section):\n- Receives confirmed appointment details from Vapi\n- Creates calendar event in Google Calendar\n- Sends professional confirmation email to customer\n- Returns success confirmation to Vapi\n\n### How to Set Up\n\n1. **Configure Vapi Tools**: In your Vapi assistant, add two server tools:\n   - `availability_checker` - Point to the first webhook URL\n   - `Creating_the_appointment_and_sending_the_confirmation_email` - Point to the second webhook URL\n\n2. **Connect Google Calendar**: \n   - Select your booking calendar in \"Get Busy Slots from Calendar\" node\n   - Update the same calendar in \"Create Calendar Event\" node\n   - Set your timezone in both nodes\n\n3. **Configure Email Settings**:\n   - Connect your Gmail account\n   - Customize the confirmation email template in \"Send Confirmation Email\" node\n   - Update business name and branding\n\n4. **Update Business Hours**: In \"AI Availability Analyzer\" node, modify the system message to match your operating hours (default: 9:00am to 6:00pm)\n\n5. **Test the Workflow**: Activate both webhooks and test with your Vapi assistant\n\n### Requirements\n\n- **Vapi Account** (for AI voice assistant)\n- **Google Calendar** (for appointment management)\n- **Gmail Account** (for sending confirmations)\n- **Google Gemini API** (for availability analysis)\n\n### Customization Tips\n\n- Modify email template HTML in the Gmail node to match your branding\n- Adjust appointment duration (default: 30 minutes) in \"Create Calendar Event\" node\n- Change business hours in the AI agent's system prompt\n- Update timezone to match your location"
      },
      "typeVersion": 1
    },
    {
      "id": "278309c2-5829-402d-8851-982fefb5c33c",
      "name": "Availability Section Label",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        16
      ],
      "parameters": {
        "color": 3,
        "width": 1248,
        "height": 416,
        "content": "## Availability Checker Tool\n\nThis section checks calendar availability and returns open time slots to Vapi."
      },
      "typeVersion": 1
    },
    {
      "id": "cdff5485-d504-4f34-9cbc-c5c3ee876c02",
      "name": "Booking Section Label",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        448
      ],
      "parameters": {
        "color": 4,
        "width": 960,
        "height": 272,
        "content": "## Appointment Creator Tool\n\nThis section creates the calendar event and sends confirmation email to the customer."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "543c8457-bcaa-45d7-a377-b96b5dbfd3e0",
  "connections": {
    "Format Busy Slots": {
      "main": [
        [
          {
            "node": "AI Availability Analyzer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Calendar Event": {
      "main": [
        [
          {
            "node": "Send Confirmation Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Confirmation Email": {
      "main": [
        [
          {
            "node": "Return Confirmation to Vapi",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Availability Analyzer": {
      "main": [
        [
          {
            "node": "Return Availability to Vapi",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Availability Analyzer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get Busy Slots from Calendar": {
      "main": [
        [
          {
            "node": "Format Busy Slots",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Create Appointment": {
      "main": [
        [
          {
            "node": "Create Calendar Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Availability Checker": {
      "main": [
        [
          {
            "node": "Get Busy Slots from Calendar",
            "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

Build an intelligent appointment booking system that lets customers schedule appointments through natural voice conversations. This workflow connects Vapi AI assistant with Google Calendar to check availability, create appointments, and send professional confirmation emails…

Source: https://n8n.io/workflows/10905/ — 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

Zoho CRM - Smart Meeting Scheduler. Uses zohoCrm, googleCalendar, httpRequest, agent. Webhook trigger; 23 nodes.

Zoho Crm, Google Calendar, HTTP Request +3
AI & RAG

Transform your salon/service business with this streamlined WhatsApp automation system featuring Claude integration, zero-setup database management, and intelligent conversation handling. Claude MCP I

WhatsApp Trigger, WhatsApp, Redis +11
AI & RAG

leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.

Supabase, Gmail, Form Trigger +13
AI & RAG

This workflow is an AI-powered Dental Appointment Assistant that automates appointment booking, rescheduling, and cancellations through Telegram or a Webhook. It uses intelligent agents to understand

Memory Buffer Window, Output Parser Structured, Mcp Client Tool +12
AI & RAG

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

Agent, @Apify/N8N Nodes Apify, HTTP Request +3