appscript.dev
Automation Intermediate Forms Sheets Gmail

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 (Name and Email).
  • A Signups sheet with a header row and these columns: email, name, status (registered or waitlist), and createdAt. 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

  1. onFormSubmit opens the Signups sheet and reads every row, dropping the header with slice(1).
  2. It counts the rows whose status is already registered — this is the live seat count at the moment of submission.
  3. If that count is below CAPACITY the new person is registered; otherwise they go on the waitlist.
  4. It reads the Name and Email answers from the event’s namedValues, which keys each form answer by its question title.
  5. It appends a row with the email, name, decided status, and a timestamp.
  6. 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 registered rows, sees 29 < 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 registered rows, sees 30 < 30 is 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:

emailnamestatuscreatedAt
[email protected]Danaregistered2025-08-06 10:14
[email protected]Theowaitlist2025-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:

  1. In the Apps Script editor open Triggers (the clock icon).
  2. Add a trigger for onFormSubmit, event source From spreadsheet, event type On form submit.
  3. Save and approve the authorisation prompt, including Gmail send access.
  4. 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 LockService before 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 Name and Email. Rename a question in the form and e.namedValues will return undefined and 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 status cell to something other than registered or waitlist, the capacity maths will quietly drift.

Related