Calendar & booking integration
This tutorial shows how to build an appointment booking flow where customers schedule meetings via WhatsApp and events appear in your team's calendar.
Architecture overview
The integration connects three systems:
- Watsi — receives WhatsApp messages and sends replies via the API
- Your backend — processes booking requests and manages availability
- Calendar provider — Google Calendar, Outlook, Cal.com, or any provider with a booking API
Customer → WhatsApp → Watsi webhook → Your backend → Calendar APISet up a webhook listener
Register a webhook subscription to receive message.received events. See the webhook tutorial for the full setup walkthrough.
curl -X POST https://api.watsi.ai/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/watsi",
"events": ["message.received"]
}'Detect booking intent
When a message arrives, check whether the customer is asking to book an appointment. You can use keyword matching or an LLM to detect intent.
app.post('/webhooks/watsi', async (req, res) => {
const event = req.body
const message = event.data.message
// Simple keyword detection
const bookingKeywords = ['book', 'appointment', 'schedule', 'reserve', 'agendar', 'cita']
const wantsBooking = bookingKeywords.some(kw =>
message.body.toLowerCase().includes(kw)
)
if (wantsBooking) {
await handleBookingFlow(event.data)
}
res.sendStatus(200)
})Fetch available slots
Query your calendar provider for available time slots. This example uses the Google Calendar FreeBusy API, but the same pattern works with any provider.
async function getAvailableSlots(date) {
const calendar = google.calendar({ version: 'v3', auth })
const busy = await calendar.freebusy.query({
requestBody: {
timeMin: startOfDay(date).toISOString(),
timeMax: endOfDay(date).toISOString(),
items: [{ id: CALENDAR_ID }],
},
})
// Compute free 30-minute windows from the busy blocks
return computeFreeWindows(busy.data, 30)
}Send available times via WhatsApp
Reply to the customer with the available slots. Format times in the customer's timezone when possible.
async function sendAvailableSlots(conversationId, slots) {
const formatted = slots
.map((s, i) => `${i + 1}. ${formatTime(s.start)} - ${formatTime(s.end)}`)
.join('\n')
await fetch('https://api.watsi.ai/api/v1/messages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
conversation_id: conversationId,
body: `Here are the available times:\n\n${formatted}\n\nReply with the number to book.`,
}),
})
}Confirm the booking
When the customer replies with their selection, create the calendar event and send a confirmation message.
async function confirmBooking(conversationId, slot, customer) {
// Create the calendar event
await calendar.events.insert({
calendarId: CALENDAR_ID,
requestBody: {
summary: `Meeting with ${customer.name}`,
start: { dateTime: slot.start },
end: { dateTime: slot.end },
attendees: [{ email: customer.email }],
},
})
// Send confirmation via Watsi
await fetch('https://api.watsi.ai/api/v1/messages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
conversation_id: conversationId,
body: `Your appointment is confirmed for ${formatTime(slot.start)}. See you then!`,
}),
})
}Production considerations
- Timezone handling — store and display times in the customer's timezone. Use the contact profile or ask during the flow.
- Concurrency — check availability again before confirming to prevent double-booking when multiple customers select the same slot.
- Cancellations — listen for follow-up messages like “cancel” and update the calendar event accordingly.
- Reminders — use WhatsApp message templates to send appointment reminders outside the 24-hour messaging window.
- State management — track booking flow state per conversation (e.g. awaiting date, awaiting slot selection, confirmed) in your database.
Related guides
- Webhooks overview — delivery model, headers, and retry behavior
- Webhook tutorial — step-by-step webhook handler setup
- API reference — full endpoint documentation for messages and conversations