Build a registration system with waitlists
Manage Northwind workshop signups and overflow — first N get a slot, the rest get a waitlist.
Published Aug 6, 2025
Northwind runs workshops with a fixed number of seats. When a session is popular it sells out fast, and the awkward part is everyone after the cut-off: they have filled in the form, they think they have a place, and nobody has told them otherwise. A spreadsheet of raw form responses does not draw that line for you.
This script draws it automatically. Each form submission is checked against the room capacity — if there is space the person is registered, and if not they are placed on a waitlist. Either way they get an email straight away that tells them exactly where they stand, so there is never a silent gap between signing up and finding out.
What you’ll need
- A Google Form for workshop signups, with at least a Name and an
Email question. The exact question titles must match the keys used in
the script (
NameandEmail). - A
Signupssheet with a header row and these columns:email,name,status(registeredorwaitlist), andcreatedAt. This can be the form’s own response sheet or a separate one. - Gmail send access on the account running the script — the confirmation emails come from this address.
The script
// The spreadsheet that records every signup.
const SIGNUPS_SHEET_ID = '1abcSignupsId';
// How many seats the workshop has. Submissions past this go on the waitlist.
const CAPACITY = 30;
// The zero-based column index of the status field in the Signups sheet.
const STATUS_COL = 2;
/**
* Runs on every form submission. Decides whether the new signup gets a
* seat or a waitlist place, records it, and emails the person their status.
*
* @param {Object} e The form-submit event, with namedValues for each field.
*/
function onFormSubmit(e) {
const sheet = SpreadsheetApp.openById(SIGNUPS_SHEET_ID).getSheets()[0];
// 1. Count how many people already hold a confirmed seat.
const registered = sheet.getDataRange().getValues()
.slice(1)
.filter((row) => row[STATUS_COL] === 'registered')
.length;
// 2. Decide this submission's status against the capacity.
const status = registered < CAPACITY ? 'registered' : 'waitlist';
// 3. Pull the name and email out of the form event.
const email = e.namedValues.Email[0];
const name = e.namedValues.Name[0];
// 4. Record the signup with its decided status and a timestamp.
sheet.appendRow([email, name, status, new Date()]);
// 5. Tell the person where they stand, straight away.
const subject = status === 'registered'
? "You're in"
: 'Waitlist confirmed';
const body = status === 'registered'
? `Hi ${name}, you're registered for the Northwind workshop. ` +
'See you there.'
: `Hi ${name}, the Northwind workshop is at capacity, so you're on ` +
"the waitlist. We'll email you the moment a spot opens up.";
GmailApp.sendEmail(email, subject, body);
}
How it works
onFormSubmitopens theSignupssheet and reads every row, dropping the header withslice(1).- It counts the rows whose
statusis alreadyregistered— this is the live seat count at the moment of submission. - If that count is below
CAPACITYthe new person isregistered; otherwise they go on thewaitlist. - It reads the Name and Email answers from the event’s
namedValues, which keys each form answer by its question title. - It appends a row with the email, name, decided status, and a timestamp.
- It picks a subject and body to match the outcome and emails the person immediately, so a registration or a waitlist place is never a surprise.
Example run
A workshop with CAPACITY of 30. The 30th person submits the form:
- The script counts 29 existing
registeredrows, sees29 < 30, and registers them. They receive: “Hi Dana, you’re registered for the Northwind workshop. See you there.”
The 31st person submits two minutes later:
- The script now counts 30
registeredrows, sees30 < 30is false, and waitlists them. They receive: “Hi Theo, the Northwind workshop is at capacity, so you’re on the waitlist. We’ll email you the moment a spot opens up.”
The Signups sheet ends up with a clean record of both:
| name | status | createdAt | |
|---|---|---|---|
| [email protected] | Dana | registered | 2025-08-06 10:14 |
| [email protected] | Theo | waitlist | 2025-08-06 10:16 |
Trigger it
This script fires on form submissions, so it needs an installable trigger —
the simple onFormSubmit name alone is not enough when the script sends email:
- In the Apps Script editor open Triggers (the clock icon).
- Add a trigger for
onFormSubmit, event source From spreadsheet, event type On form submit. - Save and approve the authorisation prompt, including Gmail send access.
- Submit a test response and confirm a row is added and an email arrives.
Watch out for
- The capacity check is not concurrency-safe. Two people submitting in the
same instant can both read the same count and both be registered — for a
popular workshop, lock the sheet with
LockServicebefore counting. - Promoting from the waitlist is manual. When a registered person drops out, nothing automatically pulls up the next waitlisted name — you change the status and email them yourself.
- The form question titles must be exactly
NameandEmail. Rename a question in the form ande.namedValueswill returnundefinedand the script will throw. - Gmail has a daily send quota (around 100 for consumer accounts, 1,500 for Workspace). A burst of signups close to that limit will start failing.
- Editing rows by hand can break the count. If you change a
statuscell to something other thanregisteredorwaitlist, the capacity maths will quietly drift.
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