Updated on June 24, 2026
- An appointment chatbot automates scheduling using natural language conversations.
- It understands user intent, such as booking or rescheduling, and triggers backend workflows.
- It integrates with tools like Google Calendar to check availability and confirm appointment slots.
- It reduces manual coordination, missed calls, and response delays.
- It is especially effective in healthcare for handling patient scheduling at scale.
- It can manage appointment booking, cancellations, reminders, and basic patient intake.
- It can be deployed without heavy engineering using platforms like Kommunicate.
An appointment chatbot is an AI agent that books, reschedules, and cancels appointments inside a chat conversation. It reads what a customer wants in plain language, checks real time availability against a connected calendar, and confirms a slot without a human agent stepping in.
Instead of relying on static forms or manual scheduling, businesses can use OpenAI to create conversational booking experiences across chat, web, and messaging channels.
While this technology can be used across industries such as SaaS demos and salon bookings, it is especially impactful in healthcare, where missed calls, long wait times, and manual scheduling often lead to delayed care and poor patient experience. An appointment chatbot can streamline patient intake, reduce front desk workload, and ensure faster access to care.
In this guide, you will learn how to build an appointment chatbot step by step using OpenAI, backend workflows, and calendar integrations like Google Calendar. We will also cover how the system works, the key components required, and how to deploy it without complex engineering.
Booking delays are one of the clearest examples of how slow response times erode patient trust. Our guide on digital response time expectations covers why response speed is the metric healthcare providers should prioritize above most others.
Table of Content
- Why use an appointment chatbot
- You don’t need an engineering team
- How it works
- Pre-Requisites
- Part 1: Set up Google OAuth credentials
- Part 2: Build an AI Agent in Kompose
- Part 3: Enable OpenAI as the conversational model
- Part 4: Add agent-to-human handoff
- How to Use
- Customizing this workflow
- Troubleshooting
- Where an appointment chatbot can run, and what it connects to
- One appointment chatbot, many industries
- Wrapping Up
- Frequently asked questions
Why use an appointment chatbot
For most teams, the booking desk has the same problem: requests arrive at all hours, but staff only work some of them. An appointment chatbot closes that gap. It handles the booking conversation from start to finish, so a customer can find a slot and confirm it late on a Sunday evening without anyone on your side being awake.
What that buys you in practice:
- It captures appointments around the clock, including the after-hours requests that would otherwise wait until morning.
- Automated confirmations and reminders cut down on no-shows and quietly empty slots.
- Customers reschedule and cancel themselves in chat instead of calling, which keeps your calendar accurate and your team free.
- Routine scheduling stops eating front-desk time, so staff focus on the conversations that actually need a person.
- Every booking is captured the same way, with the right details, straight into your connected calendar.
You don’t need an engineering team
One reason scheduling automation stalls is the assumption that it requires a developer project. It doesn’t. The AI agent in this guide is built in Kommunicate’s Kompose, a visual no-code builder, so you assemble the booking flow by configuring it rather than writing it. The only place code appears is the inline snippet that talks to the calendar, and you can copy that as is. If you have engineering help, you can extend the agent further. If you don’t, you can still ship the core booking flow yourself.
How it works

This workflow is built entirely inside the Kompose Bot Builder using Inline Code without any external webhook server. Here is the high-level flow:
- The user opens the chat widget and is greeted with a welcome message.
- The AI agent asks the user to pick a specialist (Cardiology, Dermatology, etc.).
- A first Inline Code function queries the Google Calendar Freebusy API for that doctor’s calendar and calculates open 1-hour windows for the next 7 days.
- Available slots are shown as clickable buttons: the user simply taps one to select it.
- A form collects the remaining patient details: name and email.
- On form submission, a second Inline Code function books the chosen slot on Google Calendar and returns a confirmation link to the user.
- Optionally, the AI agent hands off to a human agent if the user needs further help.
This is what makes the appointment chatbot action-oriented: it can check availability, pass booking details to the right function, and confirm a slot through Google Calendar, which follows the same backend action pattern used in OpenAI function calling.
Now, before we start implementing this, you need some things.
Pre-Requisites
- A Kommunicate account (sign up free)
- A Google Cloud project with the Calendar API enabled
- OAuth 2.0 credentials (Client ID + Client Secret)
- An OpenAI API key (optional, we’ll give one to you if you don’t have one)
Next up, we will start setting up the AI agent.
Part 1: Set up Google OAuth credentials
Before writing any agent logic, you need two sets of credentials from Google.
Step 1: Create an OAuth Client ID
- Go to Google Cloud Console → Credentials
- Click Create Credentials → OAuth Client ID
- Select Web Application as the application type
- Add https://developers.google.com/oauthplayground as an Authorized Redirect URL
Save and note down your Client ID and Client Secret.
Step 2: Enable the Google Calendar API

In the same Google Cloud project, navigate to APIs & Services → Library and enable the Google Calendar API.
Step 3: Generate access & refresh tokens
- Open Google OAuth Playground.
- Click the ⚙️ settings icon and check “Use your own OAuth credentials“. Enter your Client ID and Secret.
- In Step 1, find and select https://www.googleapis.com/auth/calendar, then click Authorize APIs. This scope covers both reading free/busy data and creating events.
- In Step 2, click Exchange authorization code for tokens.
- Copy the Access Token and Refresh Token — you’ll use both in the inline code.
Part 2: Build an AI agent in Kompose
Step 1: Create a new agent

- Log in to your Kommunicate Dashboard.
- Navigate to Agent Integrations → Create AI Agent → Kompose.
- Name it something like Appointment Agent.
- Under Custom Instructions, add guidance like: “You are a friendly medical appointment assistant. Be concise, empathetic, and professional. Always guide users toward booking an appointment or talking to an agent.“
Step 2: Create a welcome message

In the Welcome Message, set a greeting message and add two rich message buttons using a Custom Payload:
Step 3: Create the specialist selection function
When the user taps “Book an Appointment”, this intent fires and asks them to pick a specialist, whose Google Calendar will then be checked for free slots.

- Click Intents (Q&A) and name it Book Appointment.
- Add training phrases: Book Appointment, Book a slot, Schedule a visit, I need to see a doctor.
- In Agent Says, add this Custom Payload to show specialty buttons:

For each button here, we will send a message like “Show slots for Cardiology”.

The next intent catches this, runs the availability check, and replies with free time slots as buttons. When the user taps a slot, the booking intent fires.
Step 4: Create the inline code to fetch available slots
This is the core of the availability feature. The function authenticates with the Google Calendar API, calls the Freebusy API to find blocked times for the selected doctor’s calendar over the next 7 days, then calculates free 1-hour windows during clinic hours (9 AM – 5 PM) and returns them as clickable buttons.

- Go to Agent Settings → Inline Code → Create Inline Code.
- Name it ShowAvailableSlots and paste the following:
- Click Deploy once code is pasted
exports.responseHandler = async (input, callback) => {
const ACCESS_TOKEN = "YOUR_ACCESS_TOKEN";
const REFRESH_TOKEN = "YOUR_REFRESH_TOKEN";
const CLIENT_ID = "YOUR_CLIENT_ID";
const CLIENT_SECRET = "YOUR_CLIENT_SECRET";
// Map each specialty to the doctor's Google Calendar ID
// Replace these with actual calendar IDs from your Google Workspace
const DOCTOR_CALENDARS = {
"Cardiology": "dr.cardio@yourclinic.com",
"Dermatology": "dr.derm@yourclinic.com",
"Orthopedics": "dr.ortho@yourclinic.com",
"General Checkup": "dr.general@yourclinic.com"
};
// Clinic working hours (24h format, in the time zone below)
const CLINIC_START_HOUR = 9; // 9 AM
const CLINIC_END_HOUR = 17; // 5 PM
const SLOT_DURATION_MS = 60 * 60 * 1000; // 1 hour
const TIME_ZONE = "Asia/Kolkata";
const MAX_SLOTS_TO_SHOW = 6; // Max buttons displayed
try {
// Extract the specialty from the user's message
// User message arrives as e.g. "Show slots for Cardiology"
const userMessage = input.message || "";
const specialtyMatch = userMessage.match(/Show slots for (.+)/i);
const specialty = specialtyMatch ? specialtyMatch[1].trim() : "General Checkup";
const calendarId = DOCTOR_CALENDARS[specialty] || DOCTOR_CALENDARS["General Checkup"];
const oauth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET);
oauth2Client.setCredentials({
access_token: ACCESS_TOKEN,
refresh_token: REFRESH_TOKEN,
scope: "https://www.googleapis.com/auth/calendar",
token_type: "Bearer"
});
const calendar = google.calendar({ version: "v3", auth: oauth2Client });
// Query freebusy for the next 7 days
const now = new Date();
const weekLater = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
const freebusyResponse = await new Promise((resolve, reject) => {
calendar.freebusy.query(
{
requestBody: {
timeMin: now.toISOString(),
timeMax: weekLater.toISOString(),
timeZone: TIME_ZONE,
items: [{ id: calendarId }]
}
},
(err, res) => {
if (err) reject(err);
else resolve(res);
}
);
});
const busyPeriods = freebusyResponse.data.calendars[calendarId]?.busy || [];
// Build a set of busy period timestamps for quick lookup
const busyRanges = busyPeriods.map(b => ({
start: new Date(b.start).getTime(),
end: new Date(b.end).getTime()
}));
// Walk through clinic hours day by day and find free 1-hour slots
const freeSlots = [];
const cursor = new Date(now);
cursor.setMinutes(0, 0, 0); // snap to the top of the hour
while (freeSlots.length < MAX_SLOTS_TO_SHOW && cursor < weekLater) {
const hour = cursor.getHours();
// Skip outside clinic hours
if (hour < CLINIC_START_HOUR || hour >= CLINIC_END_HOUR) {
cursor.setHours(hour < CLINIC_START_HOUR ? CLINIC_START_HOUR : CLINIC_START_HOUR + 24, 0, 0, 0);
continue;
}
const slotStart = cursor.getTime();
const slotEnd = slotStart + SLOT_DURATION_MS;
// Check if this slot overlaps with any busy period
const isBlocked = busyRanges.some(b => slotStart < b.end && slotEnd > b.start);
if (!isBlocked) {
// Format for display: "Mon 23 Apr · 10:00 AM"
const label = cursor.toLocaleString("en-IN", {
weekday: "short",
day: "numeric",
month: "short",
hour: "numeric",
minute: "2-digit",
hour12: true,
timeZone: TIME_ZONE
});
freeSlots.push({ label, isoStart: cursor.toISOString() });
}
cursor.setTime(cursor.getTime() + SLOT_DURATION_MS);
}
if (freeSlots.length === 0) {
callback([{ message: `😔 No available slots found for ${specialty} in the next 7 days. Please try another specialist or contact us directly.` }]);
return;
}
// Return free slots as clickable buttons
// Each button's message carries the ISO timestamp so the next intent can read it
const slotButtons = freeSlots.map(slot => ({
type: "link",
name: `🕐 ${slot.label}`,
message: `Book slot: ${specialty} | ${slot.isoStart}`
}));
callback([{
message: `Here are the available slots for ${specialty}. Tap one to book it:`,
platform: "kommunicate",
metadata: {
contentType: "300",
templateId: "3",
payload: slotButtons
}
}]);
} catch (error) {
callback([{ message: `❌ Could not fetch availability: ${error.message}` }]);
}
};
Step 5: Create the Show Slots intent
This intent catches the user’s specialty button tap and triggers ShowAvailableSlots.

- Add a new intent named Show Available Slots.
- Add training phrases: Show slots for Cardiology, Show slots for Dermatology, Show slots for Orthopedics, Show slots for General Checkup.
- In Agent Says, enable Dynamic Message → Inline Code → select ShowAvailableSlots.
- Click Train Bot.

Step 6: Create the inline code for calendar booking
When the user taps a time slot button, its message is formatted as “Book slot: Cardiology | 2025-04-25T09:00:00.000Z”. This inline code parses that, collects remaining details via a quick form, and creates the Google Calendar event.

- Go to Agent Settings → Inline Code → Create Inline Code.
- Name it ShowBookingForm and paste the code provided below.
- Click Deploy.
exports.responseHandler = async (input, callback) => {
// Parse specialty and chosen ISO time from the button message
// Format: "Book slot: <Specialty> | <isoStart>"
const userMessage = input.message || "";
const match = userMessage.match(/Book slot: (.+?) \| (.+)/);
const specialty = match ? match[1] : "General Checkup";
const isoStart = match ? match[2] : new Date().toISOString();
// Format the slot time for display in the form heading
const slotDate = new Date(isoStart);
const displayTime = slotDate.toLocaleString("en-IN", {
weekday: "long", day: "numeric", month: "long",
hour: "numeric", minute: "2-digit", hour12: true,
timeZone: "Asia/Kolkata"
});
callback([{
message: `Great! Confirm your details for the ${specialty} appointment on ${displayTime}:`,
platform: "kommunicate",
metadata: {
contentType: "300",
templateId: "12",
payload: [
{
type: "text",
data: { label: "Full Name", placeholder: "Enter your full name" }
},
{
type: "text",
data: { label: "Email", placeholder: "Enter your email address" }
},
// Hidden fields carry the slot data through to the booking step
{
type: "hidden",
data: { name: "specialty", value: specialty }
},
{
type: "hidden",
data: { name: "slotStart", value: isoStart }
},
{
type: "submit",
data: {
name: "Confirm Booking",
action: {
requestType: "postBackToBotPlatform",
message: "Confirming appointment..."
},
type: "submit"
}
}
]
}
}]);
};
Step 7: Create the Select Slot intent

- Add a new intent named Select Slot.
- Add training phrases: Book slot: Cardiology, Book slot: Dermatology, Book slot: Orthopedics, Book slot: General Checkup.
- In Agent Says, enable Dynamic Message → Inline Code → select Show Booking Form.
- Click Train Agent.
Step 8: Create the final booking inline code
This function reads the confirmed form data (name, email, hidden specialty and slot time) and creates the Google Calendar event.
- Go to Agent Settings → Inline Code → Create Inline Code.
- Name it Book Appointment and paste the code provided below.
- Click Deploy.
exports.responseHandler = async (input, callback) => {
const ACCESS_TOKEN = "YOUR_ACCESS_TOKEN";
const REFRESH_TOKEN = "YOUR_REFRESH_TOKEN";
const CLIENT_ID = "YOUR_CLIENT_ID";
const CLIENT_SECRET = "YOUR_CLIENT_SECRET";
const DOCTOR_CALENDARS = {
"Cardiology": "dr.cardio@yourclinic.com",
"Dermatology": "dr.derm@yourclinic.com",
"Orthopedics": "dr.ortho@yourclinic.com",
"General Checkup": "dr.general@yourclinic.com"
};
try {
const formData = input.metadata?.KM_CHAT_CONTEXT?.formData || {};
const patientName = formData["Full Name"] || "Patient";
const patientEmail = formData["Email"] || "";
const specialty = formData["specialty"] || "General Checkup";
const isoStart = formData["slotStart"] || new Date().toISOString();
const doctorEmail = DOCTOR_CALENDARS[specialty] || DOCTOR_CALENDARS["General Checkup"];
const startTime = new Date(isoStart);
const endTime = new Date(startTime.getTime() + 60 * 60 * 1000);
const oauth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET);
oauth2Client.setCredentials({
access_token: ACCESS_TOKEN,
refresh_token: REFRESH_TOKEN,
scope: "https://www.googleapis.com/auth/calendar",
token_type: "Bearer"
});
const calendar = google.calendar({ version: "v3", auth: oauth2Client });
const event = {
summary: `Appointment: ${specialty} -- ${patientName}`,
location: "Kommunicate Health Clinic, 123 Main St",
description: `Patient: ${patientName}\nSpecialty: ${specialty}`,
start: { dateTime: startTime.toISOString(), timeZone: "Asia/Kolkata" },
end: { dateTime: endTime.toISOString(), timeZone: "Asia/Kolkata" },
attendees: [
{ email: doctorEmail },
...(patientEmail ? [{ email: patientEmail }] : [])
],
reminders: { useDefault: true }
};
calendar.events.insert(
{ calendarId: "primary", resource: event },
(err, res) => {
if (err) {
callback([{ message: `❌ Could not book your appointment: ${err.message}` }]);
return;
}
const displayTime = startTime.toLocaleString("en-IN", {
weekday: "long", day: "numeric", month: "long",
hour: "numeric", minute: "2-digit", hour12: true,
timeZone: "Asia/Kolkata"
});
callback([{
message: `✅ Appointment confirmed for ${patientName}!\n\n🩺 Specialist: ${specialty}\n📅 Date & Time: ${displayTime}\n🔗 View your event: ${res.data.htmlLink}`
}]);
}
);
} catch (error) {
callback([{ message: `Something went wrong: ${error.message}` }]);
}
};
Step 9: Create the Submit intent
- Add a new intent named Submit Appointment
- Add training phrases: Confirming appointment…, Submit, Confirm Booking.
- In Agent Says, enable Dynamic Message → Inline Code → select Book Appointment.
- Click Train Agent.
Part 3: Enable OpenAI as the conversational model
OpenAI is what makes the appointment chatbot feel like a real conversation rather than a button tree. The booking actions run on the inline code you set up above, while OpenAI handles the language: understanding how a patient phrases a request, answering follow-up questions, and giving sensible fallback responses when someone goes off-script. For a production booking experience, we recommend enabling it.

- In the Agent Builder, go to Agent Settings → Agent AI Model
- Select OpenAI from the model list
- Enable Contextual Understanding to allow multi-turn conversations (e.g., if a user asks follow-up questions about their booking)
Part 4: Add agent-to-human handoff
If a user needs to speak with a real agent, configure the fallback:
- Open the Default Fallback Intent
- In Agent Says, click More → Handover
How to Use
Once your bot is set up:
- Test in the agent preview — Click Test your Agent in the Agent Builder and walk through the full flow: pick a specialist → view slots → tap a slot → fill details → confirm
- Verify your doctor calendar IDs — In ShowAvailableSlots and BookAppointment, make sure DOCTOR_CALENDARS maps each specialty to a real Google Calendar ID (found in each calendar’s Settings page)
- Activate the AI Agent — Assign it to your Kommunicate widget from the Agent Integration panel
- Embed the widget on your website using the Kommunicate install script
- Message your AI Agent — It should greet users, show specialty options, display real free slots as buttons, collect patient details, and book the event on Google Calendar
Customizing this workflow
- Change the slot duration — Update SLOT_DURATION_MS in ShowAvailableSlots to 30 * 60 * 1000 for 30-minute appointments
- Adjust clinic hours — Change CLINIC_START_HOUR and CLINIC_END_HOUR to match your actual opening times
- Show more or fewer slots — Increase or decrease MAX_SLOTS_TO_SHOW (default 6) to control how many buttons appear
- Send confirmation emails — After the calendar.events.insert call in BookAppointment, call an email API (e.g., SendGrid) using patientEmail to send a booking receipt
- Persist bookings to a Google Sheet — Append a row to a spreadsheet inside BookAppointment using the Google Sheets API to maintain a front-desk log
- Add WhatsApp support — Connect your Kommunicate bot to WhatsApp Cloud API. The slot button flow works identically on WhatsApp; extract the user’s phone number via input.metadata.KM_CHAT_CONTEXT.from for SMS reminders
- Multi-doctor per specialty — Extend DOCTOR_CALENDARS to an array of calendar IDs per specialty and query freebusy for all of them, merging results into a unified list of free slots
Troubleshooting
Slots are showing times that are already booked — Double-check that the calendar IDs in DOCTOR_CALENDARS exactly match the doctor’s Google Calendar ID (found under Calendar Settings → Integrate Calendar → Calendar ID). A typo will cause the freebusy query to return an empty busy list, making all slots appear free.
No slots are returned even though the calendar is free — The freebusy API only reports busy times; an empty busy list means the calendar is entirely free. If you’re getting zero slots, the issue is likely in the working-hours filter. Confirm that CLINIC_START_HOUR, CLINIC_END_HOUR, and TIME_ZONE are set correctly.
The bot creates an event but the time zone is wrong — Update timeZone in both ShowAvailableSlots (for display formatting) and BookAppointment (for the calendar event) to match your clinic’s location, e.g., “America/New_York” or “Europe/London”.
OAuth token expired — Access tokens expire after ~1 hour. The refresh_token handles automatic renewal as long as it’s included. If both expire, re-run the OAuth Playground flow to generate fresh tokens.
Hidden form fields (specialty, slotStart) are arriving as undefined — Make sure the form’s submit action uses “requestType”: “postBackToBotPlatform” and that the Submit intent training phrase exactly matches the form’s submit message value (“Confirming appointment…”).
Inline code deployment fails — Do not rename the responseHandler function or its arguments. Kompose requires this exact signature to execute the function correctly.
Where an appointment chatbot can run, and what it connects to
The build in this guide puts the AI agent on your website and connects it to Google Calendar, but neither of those is a hard boundary. The same agent can run wherever your customers already are, and it draws on the systems you already use to manage time and contacts.
On channels, the booking experience is not tied to a website widget. With Kommunicate, the same agent can be deployed across surfaces like WhatsApp and Instagram, so a customer can book from the place they already message you. The conversation logic stays the same; only the surface changes.
On connections, the calendar is the core link, because availability is the one thing the agent has to get exactly right. Beyond that, booking details can feed the tools your team already relies on, such as your CRM, so a new appointment does not sit in isolation from the rest of the customer record.
The version you build here is a starting point, not a ceiling. Once the core booking flow works, extending it is a matter of where you want the agent to appear and what you want it to stay in sync with.
One appointment chatbot, many industries
This guide uses a healthcare example, a patient booking time with a clinic, because the scheduling pressure there is acute. But nothing about the build is healthcare specific. The same pattern fits any business where customers book time:
- Dental and medical practices booking patient visits
- Salons, spas, and barbershops filling the daily calendar
- Fitness studios and personal trainers scheduling sessions
- Consultants, coaches, and advisors taking discovery calls
- Home services like cleaning, repair, and inspection arranging site visits
- Tutors and education providers setting up classes
In every case the mechanics are identical: understand the request, check availability, confirm the slot. Only the language and the calendar behind it change.
Wrapping Up
And you now have a fully functional appointment chatbot that talks to Google Calendar in real time, all without spinning up a single backend server. By combining Kompose’s Inline Code with the Calendar API, you have built something that would typically require a developer, a hosting provider, and a webhook handler. Your patients get a smooth, conversational booking experience, and your front desk gets fewer phone calls to juggle.
Once you are comfortable with this foundation, the customization ideas above are a great place to push it further: confirmation emails, WhatsApp delivery, multi-doctor pooling, or front-desk logging in Google Sheets.
If you want to build your own appointment-booking AI agent, feel free to sign up for Kommunicate. If you would rather see it in action first, book a demo and our team will walk you through what is possible with Kompose and AI-powered chat.
Frequently asked questions
An appointment chatbot is an AI agent that handles booking inside a chat conversation. It interprets what a customer wants in plain language, checks real time availability against a connected calendar, and books, reschedules, or cancels the slot without a human agent involved.
You build the booking flow in a no-code builder like Kommunicate’s Kompose, connect it to your calendar so it can read real availability, and use OpenAI as the conversational layer that understands natural language, handles follow-up questions, and manages fallbacks. This guide walks through each step.
Yes. In this build, the AI agent checks open slots using the Google Calendar Freebusy API and writes confirmed bookings with the Events API, so the calendar your team already uses stays the single source of truth.
It can run anywhere your customers message you. The version here lives on a website, but the same agent can be deployed across channels like WhatsApp and Instagram through Kommunicate, with the same booking logic behind each one.
No. The agent is built visually in Kompose, and the only code involved is a calendar snippet you can copy directly. Engineering help lets you extend it, but it is not required to launch the core booking flow.


