appscript.dev
Automation Intermediate Forms Gmail

Build a quiz with instant personalized feedback

Email scored Northwind quiz results with explanations the moment a quiz is submitted.

Published Jul 9, 2025

Northwind’s onboarding quiz runs on a Google Form, which is fine for collecting answers but tells the person who submitted it almost nothing. Google’s built-in quiz mode can show a score, but it cannot explain why an answer was wrong, and chasing that feedback by hand defeats the point of an automated quiz.

This automation closes the loop the moment a quiz is submitted. A form-submit trigger scores the response against an answer key, builds a per-question review with a one-line explanation for each, and emails the result straight to the person who took the quiz. They get a score and the reasoning behind it within seconds, with no marking by anyone.

What you’ll need

  • A Google Form quiz whose question titles exactly match the keys in the ANSWERS object below — the script looks answers up by question title.
  • A short-answer or email question titled Your email so the script knows where to send the result.
  • The Form connected to its script (open the Form, choose Script editor from the three-dot menu) so the form-submit trigger can be installed.

The script

ANSWERS is the answer key — one entry per question, each with the correct answer and a one-line explanation. onFormSubmit runs on every submission.

// The answer key. Each key MUST match a Form question title exactly.
// "correct" is the expected answer; "explain" is the feedback line.
const ANSWERS = {
  'Q1: What does .clear() do on a Sheet?': {
    correct: 'Clears values',
    explain: 'Removes data, keeps formatting.',
  },
  'Q2: What does CacheService persist?': {
    correct: 'Strings for 6 hours',
    explain: 'Items are evicted at the TTL or under pressure.',
  },
};

// The Form question that collects the respondent's email address.
const EMAIL_QUESTION = 'Your email';

/**
 * Runs on every form submission. Scores the response against ANSWERS,
 * builds a per-question review, and emails it to the respondent.
 *
 * @param {Object} e The form-submit event, with e.namedValues.
 */
function onFormSubmit(e) {
  let score = 0;
  const review = [];

  // 1. Check each question in the answer key against what was submitted.
  for (const [question, expected] of Object.entries(ANSWERS)) {
    const given = e.namedValues[question]?.[0];
    const right = given === expected.correct;
    if (right) score++;

    // Tick or cross, the question, then the explanation underneath.
    review.push(`${right ? '✓' : '✗'} ${question}\n  ${expected.explain}`);
  }

  // 2. Find where to send the result.
  const email = e.namedValues[EMAIL_QUESTION]?.[0];
  if (!email) {
    Logger.log('No email address on this submission — cannot send feedback.');
    return;
  }

  // 3. Email the score and the per-question review.
  const total = Object.keys(ANSWERS).length;
  GmailApp.sendEmail(
    email,
    `Quiz: ${score} / ${total}`,
    review.join('\n\n')
  );
}

How it works

  1. onFormSubmit runs automatically each time the quiz is submitted. Apps Script passes the answers in e.namedValues, keyed by question title.
  2. The script loops over every entry in ANSWERS. For each question it reads the submitted answer with e.namedValues[question]?.[0] — the ?. keeps it safe if a question was left blank.
  3. It compares the submitted answer to expected.correct. A match adds one to score.
  4. For every question it pushes a review line — a tick or cross, the question, and the explanation indented underneath — so the feedback is the same whether the answer was right or wrong.
  5. It reads the respondent’s email from the Your email question. If there is no address, it logs a message and stops rather than throwing.
  6. It emails the result: the subject carries the score (3 / 5), and the body is the full review joined with blank lines between questions.

Example run

Someone submits the quiz, answering Q1 correctly and Q2 incorrectly, with the email [email protected]. They receive:

Subject: Quiz: 1 / 2

✓ Q1: What does .clear() do on a Sheet? Removes data, keeps formatting.

✗ Q2: What does CacheService persist? Items are evicted at the TTL or under pressure.

They see their score and, for the question they missed, the explanation that tells them what the right answer involved — all within a few seconds of clicking submit.

Trigger it

The script must run on every submission, so install a form-submit trigger:

  1. In the Apps Script editor, open Triggers (the clock icon).
  2. Click Add trigger.
  3. Choose function onFormSubmit, event source From form, event type On form submit.
  4. Save and approve the authorisation prompt.

Submit a test response to confirm the email arrives and the score is right.

Watch out for

  • Question titles must match exactly. The keys in ANSWERS are looked up against e.namedValues verbatim. A trailing space or a renamed question breaks the match silently — that question simply scores zero.
  • Answer matching is exact and case-sensitive. Clears values will not match clears values. For multiple-choice questions this is fine; for short-answer questions, normalise both sides before comparing.
  • One email per submission counts against your Gmail quota. Consumer accounts send roughly 100 mails a day, Workspace accounts around 1,500. A busy quiz day can approach the consumer limit.
  • Use the trigger event, not getResponses(). Reading responses on a timer risks emailing the same person twice. The form-submit trigger fires once per submission, which is exactly what you want.
  • The trigger runs as you. Every feedback email is sent from your account and shows your address as the sender. Set a friendly reply-to or alias if that matters.

Related