Best Boilerplates for Appointment Scheduling SaaS 2026
Scheduling SaaS Is a Proven Niche
Appointment scheduling software — Calendly, Cal.com, Acuity — is a large, proven market. Every professional service business needs booking software: coaches, consultants, doctors, lawyers, tutors, barbers, personal trainers.
Building a scheduling SaaS product means:
- Availability management — providers set their available hours
- Calendar integration — sync with Google Calendar, Outlook, Apple Calendar
- Booking flows — customers pick a time, receive confirmation
- Buffer time — automatic gaps between appointments
- Timezone handling — every interaction must be timezone-aware
- Payment at booking — Stripe integration for paid consultations
- Reminders — email and SMS before appointments
The hardest parts: timezone handling, calendar sync, and availability conflict detection.
TL;DR
Best starting points for appointment scheduling SaaS in 2026:
- Cal.com (open source, fork) — The most complete open-source scheduling system. MIT licensed, fork and white-label.
- Calcom atoms + Next.js — Embed Cal.com's scheduling UI as React components in your own SaaS.
- Nylas API + any SaaS boilerplate — Managed calendar integration (Google, Outlook, iCal).
- Google Calendar API + OpenSaaS — DIY scheduling with Google Calendar sync.
- Custom: Next.js + Prisma + date-fns-tz — Full control, timezone-safe implementation.
Key Takeaways
- Cal.com is MIT licensed — you can fork it, white-label it, and sell it as your own product
- Cal.com Atoms (their embeddable React components) let you add scheduling UI to any app
- Nylas handles Google Calendar, Outlook, and Apple Calendar sync under one API
- All scheduling logic must use UTC storage with timezone-aware display — never store local times
- Buffer time prevents back-to-back bookings — implement as a constraint in availability checking
- The average custom scheduling implementation takes 3-4 weeks; Cal.com fork is 1-2 weeks
Option 1: Fork Cal.com
Cal.com is the open-source Calendly alternative. Its stack: Next.js 15, Prisma, tRPC, Turborepo. MIT licensed.
git clone https://github.com/calcom/cal.com.git my-scheduler
cd my-scheduler
# Install dependencies:
yarn install
# Setup database:
cp .env.example .env
# Fill in: DATABASE_URL, NEXTAUTH_SECRET, GOOGLE_CLIENT_ID, etc.
yarn workspace @calcom/prisma db:push
yarn dev
Cal.com includes out of the box:
- Availability management with recurring schedules
- Google Calendar, Outlook, Apple Calendar bidirectional sync
- Booking page with timezone detection
- Email confirmations and reminders
- Stripe payment integration at booking
- Team scheduling (round robin, collective)
- Event types (one-on-one, group, recurring)
- Webhooks for booking events
- API for programmatic booking
White-labeling: Replace Cal.com branding in packages/ui and deploy under your domain.
Customization effort: 2-4 weeks to white-label and customize for a specific vertical.
Option 2: Cal.com Atoms (Embed Scheduling in Your SaaS)
Cal.com Atoms are React components that embed scheduling UI into your application:
npm install @calcom/atoms
// Embed the scheduler in your Next.js app:
import { CalProvider, Booker } from '@calcom/atoms';
export function SchedulingPage({ userId }: { userId: string }) {
return (
<CalProvider
clientId={process.env.NEXT_PUBLIC_CAL_CLIENT_ID!}
options={{
apiUrl: 'https://api.cal.com/v2',
refreshUrl: '/api/cal/refresh',
}}
>
<Booker
username="your-cal-username"
eventSlug="30min"
onCreateBooking={(booking) => {
console.log('Booking created:', booking);
// Update your database, send custom email, etc.
}}
/>
</CalProvider>
);
}
This approach: use any SaaS boilerplate (ShipFast, OpenSaaS) for auth/billing, embed Cal.com Atoms for the scheduling UI.
Trade-off: You are tied to Cal.com's infrastructure. Works well for adding scheduling to an existing SaaS; less appropriate for a scheduling-first product.
Option 3: Nylas API (Calendar Integration Layer)
Nylas provides a unified API for Google Calendar, Microsoft Outlook, and Apple iCloud:
npm install nylas
import Nylas from 'nylas';
const nylas = new Nylas({ apiKey: process.env.NYLAS_API_KEY! });
// Get user's availability:
export async function getAvailability(
nylasGrantId: string, // User's connected calendar grant
startTime: Date,
endTime: Date,
durationMinutes: number
) {
const availability = await nylas.calendars.getFreeBusy({
identifier: nylasGrantId,
requestBody: {
start_time: Math.floor(startTime.getTime() / 1000),
end_time: Math.floor(endTime.getTime() / 1000),
emails: [userEmail],
},
});
return availability;
}
// Create a calendar event when booking is confirmed:
export async function createBookingEvent(
nylasGrantId: string,
booking: {
title: string;
startTime: Date;
endTime: Date;
attendeeEmail: string;
meetingUrl: string;
}
) {
const event = await nylas.events.create({
identifier: nylasGrantId,
requestBody: {
title: booking.title,
when: {
start_time: Math.floor(booking.startTime.getTime() / 1000),
end_time: Math.floor(booking.endTime.getTime() / 1000),
},
participants: [
{ email: booking.attendeeEmail, status: 'noreply' },
],
conferencing: { details: { url: booking.meetingUrl } },
},
queryParams: { calendarId: 'primary', notify_participants: true },
});
return event;
}
Nylas pricing starts at $0 for development; $25/mo for production. It removes the calendar integration complexity entirely.
Building Custom: Availability Algorithm
If building from scratch, the availability algorithm:
// lib/availability.ts
import { addMinutes, isWithinInterval, startOfDay, endOfDay } from 'date-fns';
import { fromZonedTime, toZonedTime, format } from 'date-fns-tz';
interface WorkingHours {
dayOfWeek: number; // 0 = Sunday, 6 = Saturday
startTime: string; // "09:00"
endTime: string; // "17:00"
timezone: string; // "America/New_York"
}
interface ExistingBooking {
startTime: Date;
endTime: Date;
}
export function getAvailableSlots(
workingHours: WorkingHours[],
existingBookings: ExistingBooking[],
date: Date,
slotDurationMinutes: number,
bufferMinutes: number,
viewerTimezone: string
): Date[] {
const dayOfWeek = date.getDay();
const scheduleForDay = workingHours.find((wh) => wh.dayOfWeek === dayOfWeek);
if (!scheduleForDay) return [];
// Convert working hours to UTC:
const [startHour, startMin] = scheduleForDay.startTime.split(':').map(Number);
const [endHour, endMin] = scheduleForDay.endTime.split(':').map(Number);
const localDate = toZonedTime(date, scheduleForDay.timezone);
const workStart = fromZonedTime(
new Date(localDate.getFullYear(), localDate.getMonth(), localDate.getDate(), startHour, startMin),
scheduleForDay.timezone
);
const workEnd = fromZonedTime(
new Date(localDate.getFullYear(), localDate.getMonth(), localDate.getDate(), endHour, endMin),
scheduleForDay.timezone
);
const slots: Date[] = [];
let currentSlot = workStart;
while (addMinutes(currentSlot, slotDurationMinutes) <= workEnd) {
const slotEnd = addMinutes(currentSlot, slotDurationMinutes);
const slotWithBuffer = addMinutes(currentSlot, slotDurationMinutes + bufferMinutes);
// Check if slot overlaps with any existing booking:
const isBooked = existingBookings.some((booking) =>
isWithinInterval(currentSlot, {
start: addMinutes(booking.startTime, -bufferMinutes),
end: booking.endTime,
}) ||
isWithinInterval(slotEnd, {
start: booking.startTime,
end: addMinutes(booking.endTime, bufferMinutes),
})
);
if (!isBooked) {
slots.push(currentSlot);
}
currentSlot = addMinutes(currentSlot, slotDurationMinutes + bufferMinutes);
}
return slots;
}
Timezone-Safe Storage
The critical rule: always store times in UTC, display in user timezone:
// API route: create booking
export async function POST(req: Request) {
const { startTimeUTC, durationMinutes, attendeeTimezone } = await req.json();
const startTime = new Date(startTimeUTC); // UTC from client
const endTime = addMinutes(startTime, durationMinutes);
const booking = await db.booking.create({
data: {
startTime, // UTC in database
endTime, // UTC in database
attendeeTimezone, // Store for display purposes
},
});
// Send confirmation email with timezone-correct time:
const displayTime = format(
toZonedTime(startTime, attendeeTimezone),
"EEEE, MMMM d 'at' h:mm a zzz",
{ timeZone: attendeeTimezone }
);
await sendConfirmationEmail({ time: displayTime });
}
Payment at Booking (Stripe)
// Create payment intent when booking is initiated:
export async function initiateBooking(
serviceId: string,
slotTime: Date,
attendeeEmail: string
) {
const service = await db.service.findUnique({ where: { id: serviceId } });
if (service.price === 0) {
// Free booking — skip payment:
return createConfirmedBooking(serviceId, slotTime, attendeeEmail);
}
// Paid booking — create payment intent:
const paymentIntent = await stripe.paymentIntents.create({
amount: service.price,
currency: 'usd',
metadata: { serviceId, slotTime: slotTime.toISOString(), attendeeEmail },
});
// Create pending booking (confirmed on payment success):
const booking = await db.booking.create({
data: {
serviceId,
startTime: slotTime,
attendeeEmail,
status: 'PENDING_PAYMENT',
stripePaymentIntentId: paymentIntent.id,
},
});
return { clientSecret: paymentIntent.client_secret, bookingId: booking.id };
}
Recommended Starting Point by Product Type
| Scheduling Product | Starting Point |
|---|---|
| Calendly clone | Fork Cal.com |
| Scheduling feature in SaaS | Cal.com Atoms |
| Multi-calendar sync | Nylas API + ShipFast |
| Niche vertical scheduler | OpenSaaS + custom availability |
| Healthcare scheduling | Custom (HIPAA compliance required) |
The Monetization Layer for Scheduling SaaS
Most scheduling products start with a free tier and add paid features. The subscription model for scheduling SaaS differs from typical SaaS subscription models because value scales with provider count (the people taking appointments), not with end-user count (the people making appointments).
Calendly's pricing model — charge per calendar, free for one user — is the industry reference. For a scheduling SaaS, this maps to charging per team member who has a booking page, not per person who books an appointment. Your Stripe subscription should reflect this: the line item is "booking pages" or "team members," not "users."
Implementing usage-based pricing for scheduling features: gate the number of event types, custom availability windows, and integration syncs by plan. Free plan: one event type (30-minute meeting), one calendar sync, no buffer time configuration. Pro plan: unlimited event types, three calendar syncs, buffer time, payment at booking. Business plan: team scheduling, round-robin assignment, analytics.
For payment at booking (as opposed to subscription billing), Stripe's Payment Intents API handles the flow: initiate a Payment Intent when the slot is selected, capture it after the booking is confirmed. The Stripe Checkout flow is simpler to implement but redirects the user off-page. For an embedded booking experience where the user never leaves your page, use Stripe Elements with a custom payment form. Cal.com's implementation uses a pending booking state — the appointment time is held but marked pending until payment completes, preventing double-booking of paid slots.
Evaluating Existing Scheduling Boilerplates
Before building a scheduling product from scratch or forking Cal.com, evaluate whether an existing boilerplate covers your specific vertical requirements. The generic scheduling infrastructure (availability management, calendar sync, booking flows, reminders) is solved. The vertical-specific requirements (healthcare consent forms, legal matter types, salon deposit rules) are where you add value.
If you're building a healthcare scheduling product, start with a HIPAA-compliant boilerplate as your base — not a generic SaaS boilerplate and not Cal.com, which doesn't have HIPAA certification. Your scheduling logic needs to be built on top of infrastructure that satisfies BAA requirements from day one. Adding HIPAA compliance retroactively to a codebase is far more work than building on a compliant foundation.
For salon, fitness studio, and professional services scheduling, Cal.com's fork is likely the fastest path. The existing resource management (service types, provider profiles, availability), booking page, and reminder infrastructure covers 80% of what most vertical scheduling products need. Your differentiation is in the industry-specific workflow — specific intake forms, deposit handling, cancellation policies — not in the base scheduling infrastructure.
For developer tools and B2B scheduling (booking software for APIs, customer success calls, demo scheduling embedded in SaaS products), Cal.com Atoms provides the most integration flexibility. The embeddable components let you drop scheduling UI into your existing product without a separate app or redirect.
Reminders, Notifications, and No-Show Reduction
Appointment no-shows are a significant business problem for scheduling SaaS customers. Reducing no-show rates is one of the most direct ways to demonstrate your product's value. Building effective reminder infrastructure into your scheduling product from the start differentiates it from basic calendar tools.
The reminder cadence that reduces no-shows most effectively (based on scheduling platform data): a confirmation email immediately after booking, a reminder 24 hours before the appointment, and an SMS reminder 2 hours before. SMS has significantly higher open rates for time-sensitive reminders than email — 98% open rate for SMS vs 20% for email. For appointment-critical SaaS, integrating Twilio or a similar SMS provider pays for itself in reduced no-shows within weeks.
Appointment reminders require timezone-aware scheduling. If an appointment is at 3pm Pacific and you send a reminder "1 hour before," you need to schedule that notification for 2pm Pacific, not 2pm UTC. The same availability algorithm that converts working hours to UTC for storage must be applied in reverse to convert stored UTC appointment times to the attendee's local timezone for reminder scheduling.
Building reminders as background jobs rather than scheduled database queries is important for reliability. Store the reminder schedule as job entries (Inngest or Trigger.dev work well here), and send reminders through your email/SMS provider from those jobs. This separates the reminder logic from the main application and gives you observability into which reminders were sent, which failed, and which need retrying.
Cancellation and rescheduling workflows complete the reminder picture. When an appointment is cancelled, cancel all pending reminders. When it's rescheduled, cancel the old reminders and create new ones for the new time. This state management is easy to get wrong — cancelling reminders for the wrong appointment, or forgetting to cancel them when appointments change — and is worth thorough testing. The reminder system is also where no-show prediction can add value: if a provider's historical data shows that clients who don't open the 24-hour reminder email have a 40% no-show rate, you can trigger an additional SMS reminder for that segment specifically, reducing no-shows without adding friction for reliable clients.
Choosing the Right Scheduling Architecture for Your SaaS Stage
Appointment scheduling infrastructure is one of the areas where premature optimization costs the most. The right architecture for a booking SaaS with 10 customers is fundamentally different from the right architecture for one with 10,000 customers, and building the latter too early means maintaining complexity you don't need while you're still figuring out if the product will work.
Stage 1: Validation (0-100 customers). At this stage, the highest-ROI approach is using Cal.com's hosted product as your scheduling layer and embedding it in your app with the Atoms SDK, or redirecting users to a Cal.com booking page. You avoid building any calendar infrastructure, and your customers get a polished scheduling experience immediately. The downside — that your scheduling UI lives in someone else's product — doesn't matter when you're validating whether customers will pay for your product at all.
Stage 2: Product-market fit (100-1,000 customers). At this stage, you're likely customizing the Cal.com experience using Cal.com Atoms (embeddable React components) or considering a direct Nylas integration for enterprise customers who need Google Workspace or Microsoft Exchange connectivity. The key architectural decision here is where availability data lives: in Cal.com's database (simpler, less control) or in your own PostgreSQL (more work, more flexibility). For most products, Cal.com-hosted data is the right answer until you have specific requirements that Cal.com can't meet.
Stage 3: Scale (1,000+ customers). At scale, the limitations of hosted scheduling infrastructure become real: Cal.com API rate limits affect high-volume booking products, and the per-event costs of third-party providers accumulate meaningfully. Building your own availability engine — timezone-aware PostgreSQL queries, conflict detection, and buffer logic — becomes justified when the cost savings offset the engineering investment. The transition is gradual: migrate availability storage to your own database first, then calendar sync, then the booking UI.
For most SaaS products, the relevant architecture question is "Cal.com Atoms or direct Nylas integration?" not "build vs buy." The build-vs-buy analysis only becomes relevant at meaningful scale, and most scheduling SaaS products never reach the scale where building scheduling infrastructure from scratch generates positive ROI compared to ongoing API costs.
The boilerplate choice follows this same logic. A Next.js boilerplate that integrates Cal.com's REST API is sufficient for the first two stages. If you're at Stage 3, you're making scheduling infrastructure a core competency and a boilerplate's opinions about scheduling become less relevant than your team's specific requirements. The engineering investment in custom availability infrastructure — typically 4-6 weeks for a complete implementation — only makes financial sense when the API costs you're replacing exceed what that engineering time is worth at scale. Most scheduling SaaS products should defer this decision until they have clear, consistent, recurring data showing the cost crossover point precisely.
Methodology
Based on publicly available information from Cal.com documentation, Nylas API documentation, and scheduling builder community resources as of March 2026.
Building a scheduling SaaS? StarterPick helps you find the right SaaS boilerplate to build your booking product on top of.
Reminder deliverability is a final consideration that affects scheduling SaaS product quality: email providers have different spam filter behaviors for reminder emails, and SMS delivery rates vary by carrier and geography. Building reminder infrastructure on proven transactional email (Resend, Postmark) and SMS (Twilio) providers reduces the operational risk of reminders silently failing.
Find multi-tenant boilerplates that support scheduling vertical apps: Best boilerplates for multi-tenant SaaS 2026.
See how Stripe integrates with paid booking flows: Ideal tech stack for SaaS in 2026.
Compare the best SaaS boilerplates for building vertical products: Best SaaS boilerplates 2026.