Cal.com Sandbox

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.