setup guide¶
Follow this guide to deploy your own instance of CDL Scheduler.
prerequisites¶
You will need:
A Google account with access to Google Calendar, Gmail, and Google Sheets
A GitHub account with GitHub Pages enabled
Node.js (v16+) with npm installed
git installed locally
clasp (Command Line Apps Script Projects) — install with:
npm install -g @google/clasp
Enable the Google Apps Script API at script.google.com/home/usersettings
step 1: clone the repository¶
Fork the CDL Scheduler repository on GitHub, then clone your fork:
git clone https://github.com/YOUR_USERNAME/scheduler.git
cd scheduler
step 2: set up the backend (Google Apps Script)¶
log in to clasp¶
clasp login
This opens a browser window to authorize clasp with your Google account.
create a new Apps Script project¶
cd backend
clasp create --type webapp --title "CDL Scheduler Backend"
This creates a new Apps Script project and generates a .clasp.json file in the backend/ directory.
push the backend code¶
clasp push --force
This uploads all .gs files and appsscript.json to your Apps Script project.
enable the Calendar Advanced Service¶
Open the Apps Script editor:
clasp openIn the editor, click Services (the
+icon in the left sidebar)Find Google Calendar API and click Add
Keep the default identifier
Calendarand click Add
set Script Properties¶
In the Apps Script editor:
Click the gear icon (Project Settings) in the left sidebar
Scroll down to Script Properties
Click Edit script properties and add the following:
Property |
Description |
Example |
|---|---|---|
|
Google Calendar ID to check for availability |
|
|
Your email address (receives booking notifications) |
|
|
Your display name in emails |
|
|
ID of a Google Sheet for storing bookings (create a blank sheet and copy the ID from the URL) |
|
|
Your GitHub Pages URL |
|
|
Title of calendar events that define your availability |
|
|
JSON array of calendar IDs to check for conflicts |
|
|
Minimum hours before a slot can be booked |
|
|
Maximum days in advance a slot can be booked |
|
|
Days before booking tokens expire |
|
|
Secret key for the cleanup endpoint (generate a random string) |
|
Tip
To generate a secure CLEANUP_KEY, run:
python3 -c "import secrets; print(secrets.token_hex(32))"
deploy the web app¶
In the Apps Script editor, click Deploy > New deployment
Click the gear icon next to “Select type” and choose Web app
Set:
Description:
v1(or any version label)Execute as:
MeWho has access:
Anyone
Click Deploy
Copy the Web app URL — you will need this for the frontend configuration
Important
After every clasp push --force, you must create a new versioned deployment for changes to take effect:
Click Deploy > Manage deployments
Click the pencil icon on your deployment
Under Version, select New version
Click Deploy
step 3: set up the frontend (GitHub Pages)¶
update frontend configuration¶
Edit config/settings.yaml with your Apps Script URL and preferences:
availability_pattern: "Office hours"
min_notice_hours: 12
max_advance_days: 90
default_timezone: "America/New_York"
theme_color: "#CCE2D8"
apps_script_url: "YOUR_APPS_SCRIPT_WEB_APP_URL_HERE"
Replace YOUR_APPS_SCRIPT_WEB_APP_URL_HERE with the URL you copied during deployment.
customize meeting types¶
Edit config/meeting-types.yaml to define your meeting types:
meeting_types:
- id: office-hours
name: "Office hours"
duration: 15
description: "Meet with me about a course."
- id: project-meeting
name: "Project meeting"
duration: 30
description: "Discuss a project update."
instructions: "Please share an agenda before our meeting."
Each meeting type has:
id— unique identifier (used internally)name— display name shown to visitorsduration— meeting length in minutes (15, 30, 45, or 60)description— shown on the meeting type selection pageinstructions— (optional) additional instructions shown to visitors
customize locations¶
Edit config/locations.yaml to define available meeting locations:
locations:
- id: virtual
label: "Virtual (Zoom)"
value: "https://zoom.us/my/yourroom"
- id: in-person
label: "In-person"
value: "Room 123, Building Name"
enable GitHub Pages¶
Go to your repository on GitHub
Navigate to Settings > Pages
Under Source, select Deploy from a branch
Choose main branch and / (root) directory
Click Save
Your scheduler will be available at https://yourusername.github.io/scheduler after a few minutes.
step 4: set up GitHub Actions¶
The cleanup workflow automatically removes expired bookings. It requires two repository secrets.
Go to your repository on GitHub
Navigate to Settings > Secrets and variables > Actions
Click New repository secret and add:
Secret |
Value |
|---|---|
|
Your Apps Script web app URL (same URL from the deployment step) |
|
The same |
The cleanup runs automatically every Sunday at midnight UTC. You can also trigger it manually from the Actions tab.
step 5: create availability¶
CDL Scheduler finds your available time slots by looking for calendar events that match your AVAILABILITY_PATTERN.
Open Google Calendar
Create a new event with the title matching your pattern (e.g., “Office hours”)
Set it as a recurring event on the days and times you want to be available
Make sure the event is on the calendar matching your
CALENDAR_ID
Tip
The availability pattern is a case-sensitive substring match. If your pattern is “Office hours”, events titled “Office hours” or “Office hours (Spring 2026)” will both match.
step 6: verify your setup¶
Visit your scheduler site at
https://yourusername.github.io/schedulerConfirm that your meeting types are displayed
Select a meeting type and verify that available slots appear on the calendar
Make a test booking:
Pick an available slot
Fill in the form with test details
Submit the booking
Verify that:
A calendar event was created on your Google Calendar
You received a confirmation email
The test visitor email received a confirmation
The reschedule and cancel links in the email work correctly
Tip
If no availability slots appear, check:
Your
AVAILABILITY_PATTERNmatches the exact title of your calendar eventsThe calendar events are on the calendar matching
CALENDAR_IDThe slots are within the
MIN_NOTICE_HOURSandMAX_ADVANCE_DAYSwindowThe
CONFLICT_CALENDAR_IDSdoesn’t include calendars with events blocking your availability windows