Concepts
SMS opt-in confirmations
Confirm or cancel pending bookings with a YES/NO SMS exchange. Webhook-driven service that handles the SMS round-trip and calls the Cal.com confirm or cancel endpoint.
Cal.com
Patient books a slotEvent type set to ‘Requires confirmation’.
triggerEvent: BOOKING_REQUESTED
Yours
Your confirmation servicePOST /api/webhooks/cal — booking is pending.
Send SMS via Twilio / Vonage / etc.
Yours
Patient receives SMS‘Reply YES to confirm or NO to cancel.’
Inbound reply → POST /api/webhooks/sms
Yours
Your reply handlerParse the patient’s reply, branch on YES / NO.
YES
Cal.com
Confirm the bookingPATCH /v2/bookings/:uid/confirm
NO
Cal.com
Cancel the bookingDELETE /v2/bookings/:uid
Cal.com sends the final confirmation or cancellation email
Cal.com
Patient & doctor notifiedCal.com handles the final calendar invite or cancellation.
+1 (555) 0102 · Sarahconfirmed
Cal.com Clinic: Hi Sarah! Confirm your appointment with Dr. Lee on Tue 12 May at 10:30 am? Reply YES to confirm, NO to cancel.
YES
Confirmed. See you Tuesday!
→ PATCH /v2/bookings/:uid/confirm
+1 (555) 0102 · Sarahcancelled
Cal.com Clinic: Hi Sarah! Confirm your appointment with Dr. Lee on Tue 12 May at 10:30 am? Reply YES to confirm, NO to cancel.
NO
No problem — your appointment has been cancelled.
→ DELETE /v2/bookings/:uid
Inbound SMS handlerPOST /api/webhooks/sms
// Twilio (or your SMS provider) hits this when the patient replies
app.post("/api/webhooks/sms", async (req, res) => {
const { From, Body } = req.body;
const reply = Body.trim().toUpperCase();
// Look up the pending booking we sent this number an SMS for
const bookingUid = await findPendingBookingFor(From);
if (reply === "YES") {
await callCal("PATCH", `/v2/bookings/${bookingUid}/confirm`);
} else if (reply === "NO") {
await callCal("DELETE", `/v2/bookings/${bookingUid}`, {
cancellationReason: "Patient declined via SMS",
});
} else {
sendSms(From, "Sorry — please reply YES or NO.");
}
res.sendStatus(200);
});
// Thin wrapper around the Cal.com v2 API
function callCal(method, path, body) {
return fetch(`https://api.cal.com${path}`, {
method,
headers: {
"Authorization": `Bearer ${CAL_API_KEY}`,
"cal-api-version": "2024-08-13",
"Content-Type": "application/json",
},
body: body ? JSON.stringify(body) : undefined,
});
}Use a ‘Requires confirmation’ event type so the booking sits in a pending state until your service confirms it. Cal.com sends its own confirmation/cancellation emails — your service just decides which.