Create events from appointment-form submissions
Book slots from a Google Form into the Northwind appointments calendar automatically.
Published Aug 13, 2025
Northwind takes client briefings by appointment. For a while the studio used a shared Google Form for booking, then someone copied each submission into the calendar by hand. Two weeks in, a slot was double-booked because the manual step lagged behind the form by half a day. The same copy-and-paste job is what Apps Script triggers are built for.
This script runs every time the booking form is submitted. It reads the name, email, and chosen slot, creates a 30-minute event on the studio calendar, and invites the client as a guest — all before the submitter has closed the form tab. The form is the source of truth; the calendar mirrors it automatically.
What you’ll need
- A Google Form with three required fields named exactly
name,email, andslot. Theslotfield should be a Date-and-time question. - Edit access to the calendar that should receive the appointments. The script
uses your default calendar — change
APPOINTMENTS_CALENDAR_IDif you keep appointments on a dedicated calendar. - The form linked to a Google Sheet (responses live in a sheet), so the installable trigger has somewhere to fire from.
The script
// The calendar that should receive appointments. Leave as null to use
// the script owner's default calendar; set to a calendar ID to write
// to a shared "Northwind appointments" calendar instead.
const APPOINTMENTS_CALENDAR_ID = null;
// How long each appointment lasts, in minutes. The form only captures
// the start time, so the end time is computed from this constant.
const APPOINTMENT_MINUTES = 30;
// Form field names — must match the question titles in your Google Form
// exactly, case sensitive.
const FIELD_NAME = 'name';
const FIELD_EMAIL = 'email';
const FIELD_SLOT = 'slot';
/**
* Installable form-submit handler. Reads the submission, validates the
* key fields, and creates a guest-invited event on the appointments
* calendar.
*
* @param {GoogleAppsScript.Events.SheetsOnFormSubmit} e Form submit event.
*/
function onFormSubmit(e) {
// 1. `namedValues` keys responses by question title. Each value is an
// array because Forms permits multi-answer questions; we take the
// first entry of each.
const v = e.namedValues || {};
const name = (v[FIELD_NAME] || [''])[0].trim();
const email = (v[FIELD_EMAIL] || [''])[0].trim();
const slotRaw = (v[FIELD_SLOT] || [''])[0].trim();
// 2. Guard the inputs. A blank slot or a malformed date used to silently
// produce a "1970" event — better to log and bail.
if (!name || !email || !slotRaw) {
Logger.log('Skipped submission with missing fields: ' + JSON.stringify(v));
return;
}
const slot = new Date(slotRaw);
if (isNaN(slot.getTime())) {
Logger.log('Skipped submission with unparseable slot: ' + slotRaw);
return;
}
// 3. Compute the end time from the constant duration so a future
// redesign (45-minute calls?) is a one-line change.
const end = new Date(slot.getTime() + APPOINTMENT_MINUTES * 60_000);
// 4. Pick the right calendar. The default is fine for a solo studio;
// a calendar ID is better when several team members share the load.
const cal = APPOINTMENTS_CALENDAR_ID
? CalendarApp.getCalendarById(APPOINTMENTS_CALENDAR_ID)
: CalendarApp.getDefaultCalendar();
// 5. Create the event with the client on the guest list. Google sends
// them the invite automatically — no separate email step required.
cal.createEvent('Appointment: ' + name, slot, end, {
guests: email,
description: 'Booked via Northwind form on '
+ Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'd MMM yyyy HH:mm'),
sendInvites: true,
});
}
How it works
- The trigger fires with an event object whose
namedValuesmap keys responses by question title. The script pullsname,email, andslotout of it and trims each. - If any required field is missing, or the slot string cannot be parsed into
a real
Date, the script logs the offending submission and returns. That keeps malformed bookings from creating 1970-era ghost events. - The end time is computed by adding
APPOINTMENT_MINUTESto the start, so the duration lives in one place at the top of the file. - The calendar is chosen by the
APPOINTMENTS_CALENDAR_IDconstant —nullmeans “use the script owner’s default calendar”, a string means “look up that calendar by ID and write there instead”. createEventtakes the client’s email underguests, and Google Calendar sends the invitation automatically becausesendInvitesis true.
Example run
A client submits the form with these answers:
| Field | Value |
|---|---|
| name | Priya Patel |
| [email protected] | |
| slot | 2026-06-04 14:00 |
A few seconds later the calendar contains:
| Title | Start | End | Guests |
|---|---|---|---|
| Appointment: Priya Patel | 4 Jun 2026 14:00 | 4 Jun 2026 14:30 | [email protected] |
Priya receives the invitation in her inbox; the studio sees the appointment on the calendar without a manual copy step.
Trigger it
Form submissions cannot fire simple triggers when the script owner is offline, so the wiring needs to be an installable trigger:
- Open the form’s linked response spreadsheet, then Extensions → Apps Script.
- Paste the script above into the editor.
- Open Triggers, click Add trigger, and pick
onFormSubmit, event source From spreadsheet, event type On form submit. - Approve the authorisation prompt the first time — the script needs permission to read the form responses and write to Calendar.
Watch out for
- The field names in the form (
name,email,slot) must matchFIELD_NAME,FIELD_EMAIL, andFIELD_SLOTexactly, including case. Rename the question in the form and the script silently sees an empty value on the next submission. - The slot string comes from a Date-and-time question and is parsed in the script’s time zone. If your form takes bookings from clients across time zones, capture the zone as a separate field rather than guessing.
- The script does not check for overlap with existing events. Two submissions
for the same slot will create two appointments. Add an availability check
with
cal.getEvents(slot, end)if double-booking is a real risk. - An installable trigger fails silently when the script owner’s authorisation expires (for instance after a password change). Open the project’s Executions tab once a month and confirm form submits are still landing.
Related
Schedule personal habits and routines
Block recurring habits on Awadesh's calendar — gym, walks, deep-work mornings.
Updated Nov 5, 2025
Sync birthdays and anniversaries to Calendar
Populate recurring personal dates from a Sheet — the Northwind team rituals calendar.
Updated Nov 1, 2025
Generate recurring events with custom exceptions
Handle complex recurrence rules in code — every Tuesday except UK bank holidays.
Updated Oct 28, 2025
Auto-reschedule low-priority conflicts
Move flexible Northwind events around fixed ones — focus blocks bend, client calls don't.
Updated Oct 16, 2025
Build a contract-renewal calendar
Track Northwind's recurring-revenue renewal dates as calendar events for proactive sales.
Updated Oct 8, 2025