Create a calendar booking from a form
Schedule Northwind appointments from form data — slot, attendee, confirmation.
Published Jul 13, 2025
Northwind’s portrait sessions are short and back-to-back, which means the booking step has to be friction-free. Calendly works, but it is another subscription and another login; a Google Form already lives next to the spreadsheets the studio runs on. The trouble is the last mile — turning a form answer into an event on the photographer’s calendar without anyone copying and pasting times across tools.
This script closes that gap. When someone submits the booking form, it reads the preferred slot, creates a 30-minute event on the default calendar, and invites the attendee by email. Confirmation, calendar hold and reminder all happen in one go, with no manual scheduling.
What you’ll need
- A Google Form with three short-answer questions named exactly
Name,Email, andPreferred slot. The slot field should ask for an ISO date-time like2026-05-27T14:30(a “date and time” question works best). - A Google account whose default calendar should hold the bookings.
- The form linked to its Apps Script project so the trigger fires on submit.
The script
// How long each appointment lasts. 30 minutes covers a portrait session
// at Northwind; change once here rather than scattering the number.
const SLOT_MINUTES = 30;
// Title prefix on the calendar event. Keeps studio events easy to scan
// from the week view.
const EVENT_TITLE_PREFIX = 'Appointment';
// Description shown inside the calendar event itself. The attendee sees
// this in their invite email and on the event detail page.
const EVENT_DESCRIPTION = 'Booked via the Northwind appointment form.';
/**
* Runs on every form submission. Reads the requested slot and creates a
* calendar event with the attendee invited, so the booking is confirmed
* without anyone touching the calendar by hand.
*
* @param {GoogleAppsScript.Events.FormsOnFormSubmit} e Form submit event.
*/
function onFormSubmit(e) {
// 1. Pull the three answers we need. namedValues always returns arrays.
const name = (e.namedValues.Name && e.namedValues.Name[0]) || '';
const email = (e.namedValues.Email && e.namedValues.Email[0]) || '';
const slotIso = (e.namedValues['Preferred slot'] && e.namedValues['Preferred slot'][0]) || '';
// 2. Refuse to schedule with missing data. Skipping is cheaper than
// creating a junk event and chasing it down later.
if (!name || !email || !slotIso) {
Logger.log('Skipping booking — missing field: ' + JSON.stringify(e.namedValues));
return;
}
// 3. Parse the slot. Forms hand back strings, and an unparseable date
// yields Invalid Date — guard against that explicitly.
const start = new Date(slotIso);
if (isNaN(start.getTime())) {
Logger.log('Skipping booking — unparseable slot: ' + slotIso);
return;
}
const end = new Date(start.getTime() + SLOT_MINUTES * 60_000);
// 4. Create the event on the default calendar and invite the attendee.
// `guests` triggers a Google Calendar invite email automatically.
CalendarApp.getDefaultCalendar().createEvent(
`${EVENT_TITLE_PREFIX}: ${name}`,
start,
end,
{
guests: email,
sendInvites: true,
description: EVENT_DESCRIPTION,
}
);
Logger.log(`Booked ${name} (${email}) at ${start.toISOString()}`);
}
How it works
onFormSubmitreceives the event payload from the form trigger, which includese.namedValueskeyed by question title.- It reads the three answers we care about, defaulting to empty strings so a missing field does not throw.
- It bails out early if any required field is missing — better a quiet skip than a malformed calendar event in the photographer’s week.
- It parses the slot string into a
Date. ISO date-times from the Forms “date and time” field parse cleanly; anything else falls into theisNaNguard. - It computes the end time by adding
SLOT_MINUTESworth of milliseconds. - It calls
CalendarApp.getDefaultCalendar().createEventwith the attendee as a guest. ThesendInvites: trueoption is what triggers the Google-generated confirmation email, so no separateGmailAppcall is needed.
Example run
A potential client submits the form with:
| Field | Value |
|---|---|
| Name | Maya Okafor |
| [email protected] | |
| Preferred slot | 2026-05-29T11:00 |
Immediately after submission, the photographer’s default Google Calendar gains
an event titled Appointment: Maya Okafor from 11:00 to 11:30 on 29 May 2026,
described as “Booked via the Northwind appointment form.” Maya receives a
Google Calendar invite at [email protected] with the same details and an
Accept/Maybe/Decline prompt.
The Apps Script log records Booked Maya Okafor ([email protected]) at 2026-05-29T11:00:00.000Z.
Trigger it
The trigger is wired to the form, not the clock:
- From the bound Apps Script project, open Triggers.
- Add a trigger for
onFormSubmit, source From form, type On form submit. - Approve the Calendar and Forms scopes when prompted on the first run.
Test by submitting the form yourself with your own email — the invite arrives in your inbox within a few seconds.
Watch out for
- The script does not check for clashes. Two people booking the same slot will both get events at the same time. To prevent that, query the calendar for events in the requested window before creating the new one and bail out if any are returned.
- Time zones bite. A naive ISO string like
2026-05-29T11:00is interpreted in the script’s time zone — set the project’s time zone to the studio’s zone under Project settings to avoid surprises. - The “date and time” form field is the most reliable input. Free-text dates
(
"next Tuesday at 11") will not parse. Either constrain the question type or accept the occasionalisNaNskip. - Calendar invites send automatically via
sendInvites: true. If the studio prefers a branded confirmation, drop that tofalseand send a styled email — see Send branded confirmation emails on submission.
Related
Trigger an onboarding sequence on form submit
Kick off tasks when a new Northwind hire submits their starter form.
Updated Oct 17, 2025
Build a content-submission queue
Collect Northwind guest posts or ideas for review through a Form.
Updated Oct 9, 2025
Score sentiment in open-text feedback
Rate Northwind feedback comments without manual review — using the in-Sheet sentiment function.
Updated Oct 5, 2025
Build a peer-nomination and voting system
Collect and tally Northwind nominations for awards or initiatives — one ballot, anonymous.
Updated Oct 1, 2025
Roll a form over each cycle
Archive old responses and reset for the next Northwind cycle — quarterly OKR check-ins.
Updated Sep 27, 2025