How to Build an App Like Calendly (Appointment Scheduling)
Building a Calendly-like scheduling app requires four core systems: OAuth 2.0 calendar sync with Google Calendar API and Microsoft Graph API, an availability rules engine with buffer times and booking limits, UTC-based timezone handling, and email/SMS confirmation flows. The core product takes 8-12 weeks and costs $80K-$140K. Adding round-robin routing extends the timeline to 14-16 weeks.
Key Takeaways
- A 50-person sales team on Calendly's paid plan spends $6K-$12K per year. White-label scheduling software pays for itself within two years.
- Calendar sync is the hardest technical problem. OAuth 2.0 refresh tokens expire silently — you must handle silent re-auth for Google Calendar API and Microsoft Graph API.
- Store all times in UTC. Display in the booker's local timezone. A missed DST transition means missed meetings.
- Use Redis distributed locking during concurrent booking to prevent double-bookings when two people try to claim the same slot simultaneously.
- Round-robin and conditional routing add 4-6 weeks to the timeline. Plan for 14-16 weeks if your use case requires them.
TL;DR
A 50-person sales team on Calendly's team plan pays $6,000 to $12,000 per year. For that money, they get Calendly's brand on their booking pages, Calendly's data policies, and Calendly's feature roadmap. They get no ownership.
Three categories of businesses build their own scheduling software. SaaS products that want scheduling as a native feature inside their existing tool, not an external link that bounces users to another app. Healthcare, legal, and financial services businesses that need compliance controls Calendly won't provide. And multi-location businesses with routing rules too complex for off-the-shelf tools.
This article covers how the software actually works, what breaks if you build it wrong, and what you should budget.
What you are building
At its core, scheduling software solves one problem: find the time when person A is free and person B wants to meet, confirm the booking, and make sure both people show up.
That sounds simple. It is not. The technical complexity lives in three places: reading availability from multiple calendar sources in real time, storing and converting times correctly across timezones, and handling concurrent booking without double-booking anyone.
Core systems in a scheduling app
Calendar sync
FoundationOAuth 2.0 integration with Google Calendar API and Microsoft Graph API. Read existing events to determine availability. Write new events on booking.
Availability rules engine
Core logicWorking hours, buffer times, minimum notice, max bookings per day, date-specific overrides, and event-type-specific rules.
Booking page and widget
User interfaceEmbeddable iframe or JS snippet, public shareable link, timezone detection, custom question collection before booking.
Confirmations and reminders
ReliabilityEmail with .ics calendar attachment, SMS reminders via Twilio at 24h and 1h, cancellation and reschedule links with token auth.
Calendar sync: the hardest part
Calendly's core value is that it reads your Google or Outlook calendar and shows only your genuinely free time. Replicating this requires OAuth 2.0 integration with three calendar providers.
Google Calendar API covers Gmail users and Google Workspace accounts. You request the calendar.readonly scope to read existing events and the calendar.events scope to write booking events. The API returns events in RFC 3339 format with timezone information attached.
Microsoft Graph API covers Outlook and Office 365 accounts. The authentication flow uses Microsoft's OAuth implementation, and the event format is different from Google's. You must normalize both formats into your internal data model before comparing them.
Apple Calendar uses CalDAV, an open protocol, not a proprietary API. Implementation is more complex and requires a different authentication path. Many scheduling products defer Apple Calendar support to a later version and launch with Google and Microsoft only.
The detail that causes the most production failures: OAuth 2.0 access tokens expire. Google access tokens expire after one hour. Microsoft tokens expire after one hour by default. Your backend must silently refresh these tokens using the stored refresh token before making calendar API calls. If you do not handle this, calendar reads fail silently after one hour, and your availability display goes stale without any error visible to the user.
Silent auth failures
When a refresh token expires or a user revokes app permissions, the calendar sync fails without any user-visible error. Build monitoring for calendar sync failures and notify users when their calendar connection needs to be re-authenticated. Users should not discover the failure by missing a meeting.
The implementation sequence:
- User clicks "Connect Google Calendar." Redirect to Google OAuth consent screen.
- Google returns an authorization code. Exchange it for an access token and a refresh token. Store both encrypted in your database.
- Before every availability check, verify the access token is not about to expire. If it is, exchange the refresh token for a new access token silently.
- Call the calendar API to list events in the next 30-60 days. Mark those time ranges as unavailable.
- On new booking, write the event to the user's calendar. Include the attendee's email so they receive a calendar invite.
The availability rules engine
Calendar sync tells you when someone is already busy. The rules engine defines when they are available to be booked.
A fully featured availability rules engine includes:
Working hours per day of week. Monday through Friday, 9am to 5pm. Different hours on Wednesday. No availability on Sunday.
Buffer time between meetings. A 15-minute buffer after every meeting gives the host time to finish the previous call before the next one starts. Without this, a 60-minute meeting that runs 5 minutes over causes a conflict.
Minimum scheduling notice. A rule that prevents anyone from booking a meeting in the next two hours. This is critical for high-volume users who would otherwise find back-to-back same-day bookings on their calendar.
Maximum bookings per day. A hard cap of four meetings per day, regardless of how much free time the calendar shows.
Date-specific overrides. The host is out of office from December 23 to January 2. These overrides block all availability for specific date ranges regardless of working hours rules.
Event-type-specific rules. A 15-minute discovery call has different availability rules than a 90-minute onboarding session. Each event type carries its own duration, buffer requirements, and booking limits.
All of these rules must be checked together when calculating a free/busy list. The calendar API provides the hard blocks (existing events). The rules engine provides the soft constraints. The intersection is the set of slots you display to the booker.
Timezone handling: the second-hardest problem
A meeting booked at 2pm Eastern Standard Time is the same as a meeting booked at 7pm UTC. A meeting booked at 2pm Eastern Daylight Time is the same as 6pm UTC. These are different UTC offsets. If your system stores the time in a localized format instead of UTC, Daylight Saving Time transitions will shift meetings by one hour.
The rule is simple: store all times in UTC. Display in the booker's local timezone.
Your booking page detects the visitor's timezone from Intl.DateTimeFormat().resolvedOptions().timeZone in the browser. You display available slots converted from UTC to that local timezone. When the user confirms a booking, you store the UTC equivalent in the database. When the host's calendar receives the event, their calendar app converts the UTC time to their local timezone.
The tricky cases involve meetings across DST transitions. A recurring weekly meeting scheduled for "every Monday at 10am Eastern" is not "every Monday at 15:00 UTC." In spring and fall, the UTC offset changes by one hour. A naive implementation using fixed UTC offsets will shift the recurring meeting by one hour twice per year.
The correct implementation uses IANA timezone identifiers (like America/New_York, not EST) and a library like date-fns-tz or Luxon to handle all DST logic correctly.
The booking page and widget
The booking experience is what the end user sees. It needs to work in two modes: as a standalone page at a shareable URL, and as an embeddable widget on the host's website.
The standalone page is a React single-page application served from your domain. The URL contains the host's unique slug and optionally the event type. The page loads available slots, detects the visitor's timezone, and displays slots in local time.
The embeddable widget ships as a JavaScript snippet. When pasted into a website, it renders an iframe containing the booking page. The iframe approach keeps the booking experience consistent and isolates your frontend from the host's website's CSS.
Custom fields before booking collect information from the attendee before the slot is confirmed. Common uses: company name and role for sales calls, specific question or topic for advisory calls, dietary restrictions for in-person meetings. These fields are configurable per event type and their answers are included in the confirmation email and stored against the booking record.
Confirmation and reminder flow
The confirmation email goes out immediately after booking. It includes the event name, date and time in both the host's and attendee's timezones, a calendar invite attachment (.ics file), and links to cancel or reschedule.
The .ics file is a plain text format defined by RFC 5545. It contains all event details in a format that every calendar application can import. Generating it is straightforward; the critical detail is that the DTSTART and DTEND fields must be in UTC format with the Z suffix.
SMS reminders reduce no-shows. A reminder at 24 hours before the meeting and another at 1 hour before covers the two most effective reminder windows. Twilio handles SMS delivery. The reminder includes the time in the recipient's local timezone and a link to cancel or reschedule.
Cancel and reschedule links must be token-authenticated. The link contains a signed token tied to the specific booking. Without authentication, anyone who has the link (or who has seen the link in a forwarded email) can cancel the meeting.
Round-robin and collective scheduling
These two features cover team scheduling scenarios and add meaningful complexity.
Round-robin distributes incoming bookings across a team. When a prospect books a sales call, the system assigns it to the next available sales rep rather than requiring the prospect to choose. The simplest implementation cycles through team members in order. A more sophisticated version weights by availability, so reps with more open time receive more bookings.
Collective scheduling requires all specified team members to be free simultaneously. A panel interview with three interviewers is a collective booking. The availability calculation must intersect the free/busy data from all three calendars and find slots where all three are available.
Both features require reading and writing to multiple team members' calendars, which means multiple OAuth connections managed under a single organization account.
Routing with conditional logic
Conditional routing determines which team member or event type a booker lands on based on their answers to qualification questions. A software company routes enterprise prospects (those who answer "more than 500 employees") to the enterprise sales team and routes SMB prospects to the general sales queue.
The routing engine evaluates conditions against form field values and matches each path to a destination booking page. This replaces manual sales triage and is the scheduling feature with the highest direct revenue impact for sales organizations.
Zoom and Google Meet integration
Auto-generating a video conference link on every booking is now a baseline expectation. The implementation requires an OAuth connection to the host's Zoom or Google Workspace account. On booking creation, call the Zoom API or Google Calendar API to create a meeting link, then store the link against the booking record and include it in both the calendar invite and the confirmation email.
Zoom's API requires a server-to-server OAuth app for production use. Google Meet links are generated automatically when creating a Google Calendar event with conferenceData set.
Tech stack
A scheduling application is a data-heavy backend with a simple frontend. The complexity is in the calendar sync layer and the availability calculation, not in the UI.
Backend: Node.js with a framework like Fastify or Express. Handles all calendar API calls, availability calculations, and booking creation. Calendar API operations are async and should be queued when possible.
Database: PostgreSQL. Recurring availability rules are stored as rule records (day of week, start time, end time, timezone), not as individual event records. Individual events are stored as exceptions to the rules. This keeps the data model manageable as rule sets evolve.
Cache and locking: Redis serves two functions. It caches calendar free/busy data (refreshed every 5-10 minutes) to reduce calendar API calls. It also provides distributed locks during booking creation: when a user selects a slot, acquire a Redis lock on that slot key before writing to the database. This prevents two simultaneous bookings for the same slot.
Frontend: React for the booking page. The widget ships as an iframe wrapping the same React app.
Email: SendGrid for transactional email. Template-based for consistency.
SMS: Twilio for reminders. Store opt-in consent per attendee.
Video: Zoom API and Google Calendar API for conference link generation.
Timeline and cost
The core product takes 8-12 weeks. That includes Google and Outlook calendar sync, the availability rules engine, a booking page with timezone handling, embeddable widget, custom fields, video link generation, email confirmations with .ics attachments, and SMS reminders.
Adding round-robin team scheduling and conditional routing logic extends the timeline to 14-16 weeks. These features require additional calendar OAuth management, availability intersection algorithms, and routing rule configuration UI.
Adding HIPAA compliance for healthcare use cases adds 2-4 weeks of security work: encryption at rest and in transit, audit logging for all PHI access, Business Associate Agreements with all vendors, and access control documentation.
The cost range is $80,000 to $140,000 for the full build. The lower end of that range covers a clean core product without round-robin or routing. The upper end covers the full feature set with compliance requirements.
Key Insight
The decision to build is driven by three factors: scale (the licensing cost justifies the build cost), brand (you cannot put a competitor's name on your booking flow), and compliance (HIPAA and similar requirements rule out off-the-shelf tools). If one of those three applies to your business, building is the correct answer.
If you are evaluating a scheduling build and want a scoped estimate for your specific requirements, talk to our team. We will scope the work in two weeks and give you a milestone plan before any code is written.
Frequently asked questions
- The core product, including calendar sync, availability rules, booking pages, and email/SMS confirmations, takes 8-12 weeks. Adding round-robin team scheduling and conditional routing logic extends the timeline to 14-16 weeks. Discovery and scoping add 2 weeks before build starts.
- A custom scheduling application costs $80K-$140K depending on features. Core scheduling with Google and Outlook sync, availability rules, timezone handling, and confirmations lands in the lower range. Round-robin routing, video conferencing integration, and compliance features (HIPAA) push toward the upper end.
- Calendar sync is the hardest problem. OAuth 2.0 refresh tokens expire, re-auth must happen silently without breaking the user session, and calendar data from Google and Microsoft uses different formats. Timezone handling is the second-hardest problem: storing everything in UTC and correctly converting for DST transitions requires careful implementation.
- Use a Redis distributed lock during the booking creation process. When a user selects a slot, acquire a lock on that slot key before writing to the database. Release the lock after the booking is confirmed. Without this, two users selecting the same slot simultaneously can both receive confirmation before either booking is saved.
- Yes. If your scheduling app handles protected health information (PHI), including patient names linked to appointment types or medical specialties, it must comply with HIPAA. This means end-to-end encryption, audit logging, Business Associate Agreements with all vendors, and documented access controls. Build compliance into the architecture from day one — retrofitting it is significantly more expensive.
Ask an AI
Get an instant summary of this post from your preferred AI assistant.



