N
n8n Store
Workflow Market
AI Voice Appointment Booking with Vapi and Google Calendar

AI Voice Appointment Booking with Vapi and Google Calendar

by faiz0 views

Description

Categories

🤖 AI & Machine Learning

Nodes Used

n8n-nodes-base.coden8n-nodes-base.gmailn8n-nodes-base.webhookn8n-nodes-base.webhookn8n-nodes-base.stickyNoten8n-nodes-base.stickyNoten8n-nodes-base.stickyNoten8n-nodes-base.googleCalendarn8n-nodes-base.googleCalendar@n8n/n8n-nodes-langchain.agent
PriceGratis
Views0
Last Updated11/28/2025
workflow.json
{
  "id": "pKu0Dq4aV8M4p86u",
  "meta": {
    "instanceId": "9e21c57b707e5c24171b1abfd115a39b17f74c3b8fa84a686338beb2ce000f65"
  },
  "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
      ],
      "webhookId": "15b8ac29-2fba-434d-b73a-061a351150f1",
      "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 => `• ${slot.start} → ${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
      ],
      "webhookId": "5e0e31b2-0cb4-4899-8395-5624a7eed3b0",
      "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
      ],
      "webhookId": "8addb0a1-5b88-44fa-9234-27f8d7a7ad8c",
      "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> — 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;\">© 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,
  "pinData": {},
  "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
          }
        ]
      ]
    }
  }
}

相关工作流