Minimal gig booking site for GitHub Pages with Google Calendar for availability and Google Apps Script for event creation.
Gig offers usually arrive through SMS, WhatsApp, Messenger, Instagram, or email. The problem is not getting the first enquiry. The problem is the repeated back and forth after that:
- are you free on this date?
- where is it?
- what is the rate?
- what gear is provided?
- what time is load in?
This project gives you one link to send to bookers so they can check availability and submit the core details in one pass. That removes the slow message loop and pencils the date into your calendar immediately without exposing private event details on the public calendar.
- Gives bookers a single booking link instead of a fragmented message thread.
- Uses a public Google Calendar as a derived availability layer.
- Lets the booker submit the key details in one form.
- Creates a detailed private event and a privacy-safe public blocker automatically.
- Sends you an email notification for each accepted submission.
- Keeps the whole setup simple, lightweight, and free for normal personal use.
- The booker opens the page.
- They check the embedded Google Calendar.
- They submit:
- music type
- date
- start time
- end time
- location
- rate
- gear provided
- load in
- optional contact
- optional notes
- Google Apps Script creates a detailed event in the private source calendar.
- Google Apps Script creates or updates a generic timed blocker in the public availability calendar.
- You receive an email notification with the submitted details.
If the bandleader or organiser wants to own the final invite, they can still create and send a separate Google Calendar invite later. The private calendar remains the source of truth. The public calendar is only an availability layer.
This uses the simplest free architecture that still allows calendar writes while keeping the public calendar privacy-safe.
- GitHub Pages
- plain HTML, CSS, and JavaScript
- embedded public Google Calendar
- form submission directly to Google Apps Script
- Google Apps Script web app
- CalendarApp for private event creation and public blocker sync
- MailApp for notification emails
- basic anti-bot checks before event creation
- time-driven or manual sync from private calendar to public calendar
GitHub Pages is static hosting only. It can serve the website, but it cannot securely create Google Calendar events by itself. Google Apps Script is the minimal Google-native backend that can receive the form submission, write to a private source calendar, mirror privacy-safe blockers to a public calendar, and send an email.
flowchart LR
Booker[Booker]
Site[GitHub Pages Site]
PublicCal[Public Availability Calendar]
PrivateCal[Private Source Calendar]
Script[Google Apps Script]
Email[Gmail Notification]
Booker -->|view availability| Site
Site -->|embed| PublicCal
Booker -->|submit booking form| Site
Site -->|POST booking request| Script
Script -->|create detailed event| PrivateCal
Script -->|create generic blocker| PublicCal
PrivateCal -->|sync future busy slots| Script
Script -->|send notification| Email
- Website hosting: GitHub Pages
- Availability display: public Google Calendar embed
- Event creation: Google Apps Script web app
- Source of truth: private Google Calendar
- Public blocker sync: Google Apps Script
- Email notification: Google Apps Script via MailApp
- Running cost: effectively free within normal GitHub Pages and Google account quotas
index.htmlMain booking page markup.styles.cssMinimal CLI-style monochrome visual design and responsive layout.app.jsFrontend configuration, calendar wiring, form handling, and UI feedback.google-apps-script/Code.gsGoogle Apps Script backend that creates private events, syncs public blockers, sends the email, and applies anti-bot rules.
- Push this repository to GitHub.
- Open the repository settings.
- Go to
Pages. - Set the source to deploy from the main branch.
- Use the repository root as the publish directory.
- Save the settings and wait for the Pages build to finish.
Because the frontend is static, there is no build step required.
- Open Google Apps Script.
- Create a standalone project.
- Paste in
google-apps-script/Code.gs. - Save the script.
- Open
Project Settingsand add these Script Properties:PRIVATE_CALENDAR_IDPUBLIC_CALENDAR_IDEVENT_TITLE_PREFIXPUBLIC_EVENT_TITLENOTIFICATION_EMAILDEFAULT_LOOKAHEAD_DAYSMIN_SUBMIT_SECONDSCOOLDOWN_SECONDSSYNC_LOOKAHEAD_DAYSSYNC_PAST_DAYS
- Recommended values:
EVENT_TITLE_PREFIX=MusicPUBLIC_EVENT_TITLE=Booked / On HoldDEFAULT_LOOKAHEAD_DAYS=90MIN_SUBMIT_SECONDS=4COOLDOWN_SECONDS=600SYNC_LOOKAHEAD_DAYS=180SYNC_PAST_DAYS=2
- Deploy the project as a web app:
- Execute as:
Me - Who has access:
Anyone
- Execute as:
- Copy the deployed
/execURL. - Paste that URL into
app.jsasappsScriptWebAppUrl. - Add a time-driven trigger for
syncAvailabilityif you want the public calendar to stay current with external invites accepted into the private calendar. - Redeploy the Apps Script whenever the backend code changes.
Important note:
NOTIFICATION_EMAILshould not live in the repo- the private and public calendar IDs should not live in the repo backend code
- the anti-bot and availability settings are now also driven by Script Properties, so the backend can be tuned without committing personal configuration
- the public availability calendar may still be inferable from the frontend because this project intentionally uses a public Google Calendar embed on a static site
- public calendar events should stay generic and privacy-safe because that calendar is treated as an availability layer, not the full source of truth
The backend now reads all runtime configuration from Apps Script Script Properties.
PRIVATE_CALENDAR_IDThe private source calendar that holds the real detailed events.PUBLIC_CALENDAR_IDThe public availability calendar that holds generic blockers only.EVENT_TITLE_PREFIXPrefix used in private events and private email notifications, for exampleMusic.PUBLIC_EVENT_TITLEGeneric title used for public calendar blockers, for exampleBooked / On Hold.NOTIFICATION_EMAILAddress that receives booking notifications.DEFAULT_LOOKAHEAD_DAYSNumber of future days used when fetching availability.MIN_SUBMIT_SECONDSMinimum time a user must spend on the form before the submission is accepted.COOLDOWN_SECONDSCache-based duplicate cooldown window.SYNC_LOOKAHEAD_DAYSNumber of future days to mirror from the private calendar into the public calendar.SYNC_PAST_DAYSNumber of past days to keep included in sync cleanup.
Update app.js if you want to change:
- page title
- intro copy
- calendar embed URL
- public calendar link
- Apps Script endpoint
- music type dropdown values
- footer playlist and social links
The current backend includes a simple baseline protection layer:
- hidden honeypot field
- minimum time-on-form check using
startedAt - submission fingerprinting
- cooldown and duplicate blocking
This is intentionally lightweight. It is enough to reduce low-effort spam and repeated accidental submissions without adding visible friction for real users.
Important caveat:
- duplicate protection is currently strict
- identical submissions may be rejected after the first accepted request
That is good for reducing spam, but it can also block a legitimate retry if someone submits the exact same details again.
- The availability view is still the native Google Calendar embed.
- Browser date pickers are partially browser-controlled, so full styling is limited.
- Backend changes require a new Apps Script deployment version.
- Anti-bot protection is intentionally simple, not enterprise-grade.
- This is still a lightweight calendar workflow, not a full booking CRM or approval system.
This project is deliberately narrow. It does not try to replace your calendar, your messaging apps, or your booking workflow. It solves one specific problem well:
- a booker can see if a date looks free
- they can send the key details once
- the detailed event lands in the private calendar immediately
- the public calendar stays generic and privacy-safe
That is enough to reduce admin overhead and avoid double booking without paying for a booking platform or maintaining a custom backend.