Build an AI Appointment Booking Agent (Google Calendar + OpenClaw)
How to build an AI agent that checks availability, books appointments, and sends confirmations using Google Calendar — ideal for service businesses, coaches, and consultants.
Most booking tools are clunky. Calendly is fine, but there's no AI — it just shows a grid of slots and the user picks one. An OpenClaw booking agent is different: it has a conversation. It can ask what kind of appointment is needed, check availability for the right service type, handle rescheduling, and send a custom confirmation — all in a natural back-and-forth.
This guide builds a full booking agent integrated with Google Calendar.
What You're Building
- AI agent that understands booking requests naturally ("I need to come in Thursday afternoon")
- Real-time availability checking against Google Calendar
- Appointment creation with description and notifications
- Email confirmation to the customer
- Rescheduling and cancellation handling
Google Calendar API Setup
- Go to console.cloud.google.com
- Create a new project or select an existing one
- Enable the Google Calendar API
- Create a Service Account (recommended for server-to-server auth)
- Download the JSON key file
- Share your calendar with the service account email (give it "Make changes to events" permission)
GOOGLE_SERVICE_ACCOUNT_KEY='{...json key content...}'
[email protected]
Installing the Google Calendar Client
npm install googleapis
Building the Availability Checker Tool
// tools/check_availability.js
import { google } from 'googleapis';
const credentials = JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT_KEY);
const CALENDAR_ID = process.env.GOOGLE_CALENDAR_ID;
const auth = new google.auth.GoogleAuth({
credentials,
scopes: ['https://www.googleapis.com/auth/calendar'],
});
const calendar = google.calendar({ version: 'v3', auth });
export async function checkAvailability({ date, duration = 60 }) {
// Parse the requested date
const targetDate = new Date(date);
const dayStart = new Date(targetDate);
dayStart.setHours(9, 0, 0, 0); // 9am
const dayEnd = new Date(targetDate);
dayEnd.setHours(18, 0, 0, 0); // 6pm
// Get existing events for that day
const events = await calendar.events.list({
calendarId: CALENDAR_ID,
timeMin: dayStart.toISOString(),
timeMax: dayEnd.toISOString(),
singleEvents: true,
orderBy: 'startTime',
});
// Calculate available slots (every 30 min, filter out busy times)
const busyTimes = (events.data.items || []).map(e => ({
start: new Date(e.start.dateTime),
end: new Date(e.end.dateTime),
}));
const slots = [];
let current = new Date(dayStart);
while (current < dayEnd) {
const slotEnd = new Date(current.getTime() + duration * 60000);
if (slotEnd > dayEnd) break;
// Check if this slot conflicts with any existing event
const hasConflict = busyTimes.some(busy =>
current < busy.end && slotEnd > busy.start
);
if (!hasConflict) {
slots.push({
start: current.toISOString(),
end: slotEnd.toISOString(),
display: current.toLocaleTimeString('en-US', {
hour: 'numeric',
minute: '2-digit',
hour12: true
}),
});
}
// Move to next 30-min slot
current = new Date(current.getTime() + 30 * 60000);
}
return {
date: targetDate.toDateString(),
available_slots: slots,
duration_minutes: duration,
};
}
Building the Booking Tool
// tools/book_appointment.js
export async function bookAppointment({
startTime,
endTime,
customerName,
customerEmail,
serviceType,
notes = ''
}) {
const event = {
summary: `${serviceType} — ${customerName}`,
description: `Customer: ${customerName}\nEmail: ${customerEmail}\nNotes: ${notes}`,
start: {
dateTime: startTime,
timeZone: 'America/New_York', // set your timezone
},
end: {
dateTime: endTime,
timeZone: 'America/New_York',
},
attendees: [
{ email: customerEmail },
],
reminders: {
useDefault: false,
overrides: [
{ method: 'email', minutes: 24 * 60 }, // 24h before
{ method: 'popup', minutes: 30 }, // 30 min before
],
},
};
const response = await calendar.events.insert({
calendarId: CALENDAR_ID,
resource: event,
sendUpdates: 'all', // sends email to attendees
});
return {
event_id: response.data.id,
event_link: response.data.htmlLink,
start: response.data.start.dateTime,
end: response.data.end.dateTime,
confirmation_sent: true,
};
}
The SOUL.md
# BookingBot — [Business Name]
You help clients book appointments for [Business Name].
## Services Available
- **Initial Consultation** (30 min, free) — For new clients
- **Standard Appointment** (60 min, $150) — Regular service
- **Extended Session** (90 min, $200) — Complex cases
## Booking Flow
1. Ask what type of appointment they need
2. Ask for their preferred date and time range ("morning", "afternoon",
"Thursday", etc.)
3. Use check_availability to find open slots for that date
4. Present up to 3 available time slots
5. Confirm their choice
6. Ask for their name and email
7. Use book_appointment to create the event
8. Confirm the booking with the date, time, and what to expect
## Important Rules
- Only offer slots during business hours (Mon-Fri, 9am-6pm)
- Allow at least 24 hours advance notice for bookings
- If no slots are available on requested date, suggest the next
available day (check availability for that day)
- Always confirm the timezone with out-of-state clients
## Rescheduling
If someone needs to reschedule:
1. Ask for their current appointment details (name + original date)
2. Find a new slot using check_availability
3. Cancel the old event and create a new one
4. Confirm both changes
## After Booking
Let them know:
- They'll receive a calendar invitation at their email
- A reminder will be sent 24 hours before
- To cancel or reschedule, they can reach us at [phone/email]
Handling Natural Language Dates
The agent will receive things like "Thursday afternoon" or "sometime next week in the morning." Your LLM can interpret these, but make sure your SOUL.md instructs it to resolve ambiguity:
## Date Handling
When a date is ambiguous:
- "Thursday" → assume the nearest upcoming Thursday
- "Next week" → Monday through Friday of next week
- "This weekend" → inform them you're only available weekdays
Always confirm the specific date before checking availability:
"Let me check Thursday March 14th — is that the Thursday you had in mind?"
Integration with Email Confirmations
Google Calendar sends invitations automatically when you include attendees in the event creation, but you can also send a custom email via Resend or SendGrid:
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
async function sendBookingConfirmation({ customerEmail, customerName, startTime, serviceType }) {
await resend.emails.send({
from: '[email protected]',
to: customerEmail,
subject: `Confirmed: Your ${serviceType} appointment`,
html: `
<h2>Booking Confirmed!</h2>
<p>Hi ${customerName},</p>
<p>Your <strong>${serviceType}</strong> is confirmed for
<strong>${new Date(startTime).toLocaleString()}</strong>.</p>
<p>A calendar invitation has also been sent to this email.</p>
<p>Questions? Reply to this email or call [phone].</p>
`
});
}
Self-Hosting This Setup
Running this yourself means managing:
- Google service account credentials (rotation, security)
- HTTPS endpoint for the agent
- Process management and uptime
- Timezone handling across environments
The service account JSON key needs to be available as an environment variable without being committed to version control. On a VPS, this typically means an .env file or secrets manager setup.
Deploy on ClawPort
ClawPort handles environment variable storage securely — you paste your Google service account JSON once in the dashboard settings, and it's encrypted at rest. No dotenv files on a server somewhere.
For a booking agent like this, the combination of ClawPort ($10/month) + Google Calendar (free) + Resend ($0-20/month depending on volume) gives you a fully functional booking system for under $30/month — compared to dedicated booking software at $50-100/month.
Seven-day free trial at clawport.io.
Ready to deploy your AI agent?
Get started with ClawPort in 60 seconds. No credit card required.
Get Started FreeRelated Articles
Add an AI Chatbot to Your Shopify Store (Without Apps)
How to connect an OpenClaw agent to your Shopify store for product recommendations, order tracking, and FAQ automation — without paying $50/month for a chatbot app.
How to Migrate From ChatGPT Assistants API to OpenClaw
Why developers are moving away from the OpenAI Assistants API, a full feature comparison, and step-by-step migration guide — including conversation history, file search, and function calling.
How to Build a Discord Bot With OpenClaw
A complete guide to building and deploying a Discord bot using OpenClaw — covering gateway setup, slash commands, conversation memory, and how it compares to Telegram.
OpenClaw on Docker: Production Setup That Won't Get You Hacked
Most OpenClaw Docker tutorials give you a one-liner that works but leaves your server wide open. Here's the production-grade Docker Compose setup with bridge networking, resource limits, backups, and monitoring.