I asked Gemini how to do what the title of this question says, using an apps script. (I am discouraged to use plugins as they usually come with a limited free trial and a cost so no such solutions are accepted unless a trusted free plugin is suggested here). It gave me an Apps-script solution (to be deployed in a google form) that it claims, does the following:
It accepts the respondent’s email ID and lets the respondent submit once with an empty OTP field (NOT set as required) and the submission then triggers generation and sending of OTP to the email id filled in the field. The respondent is then required to edit response (the email id will be prefilled anyway) and fill the OTP and submit again and the script verifies keeps the respondent trying until it is keyed in correctly.
However the script and the triggers together were not able to do the above even after I tried repeated modifications with Gemini’s assistance and followed its instructions to the tee.
I might have made minor errors though and maybe also my fundamental understanding of triggers might be lacking. I could use an extra pair of eyes to check and find the errors and also point fundamental flaws and fix this issue.
Below are some essential details. Kindly ask for more information as needed and I will see if I can furnish.
Here is the Link to Fill the Form
Below is the script deployed:
function onSubmit(e) {
Logger.log("onSubmit triggered");
var itemResponses = e.response.getItemResponses();
var emailItem = itemResponses.find(item => item.getItem().getTitle() == "Your Email
Address"); // Replace with your email question title
if (!emailItem) {
Logger.log("ERROR: Email question NOT found!");
itemResponses.forEach(itemResponse => Logger.log("Item Title: " +
itemResponse.getItem().getTitle()));
return;
}
var email = emailItem.getResponse();
Logger.log("Email: " + email);
var emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;
if (!emailRegex.test(email)) {
Logger.log("ERROR: Invalid email address: " + email);
e.response.withItemResponses().setConfirmationMessage("Invalid email address.
Please enter a valid email.");
return;
}
// Get the edit URL
var editUrl = e.response.getEditResponseUrl();
Logger.log("Edit URL: " + editUrl);
var cache = CacheService.getScriptCache();
cache.put("pending_email", email, 300); // Store email for 5 minutes
cache.put("edit_url", editUrl, 300); // Store edit URL
// Generate and send OTP
var otp = generateOTP();
cache.put(email + "_otp", otp, 300); // Store OTP for 5 minutes
sendEmail(email, otp, editUrl); // Send email with OTP and edit URL
// Instead of setEditResponse, use a confirmation message to prompt for OTP entry
e.response.withItemResponses().setConfirmationMessage("Thank you for submitting your
email. Please check your inbox (and spam folder) for the OTP. Click the link in the
email to complete your submission.");
return;
}
function onFormSubmit(e) {
Logger.log("onFormSubmit triggered");
var itemResponses = e.response.getItemResponses();
var emailItem = itemResponses.find(item => item.getItem().getTitle() == "Your Email
Address"); // Replace with your email question title
var email = emailItem.getResponse();
var otpQuestion = itemResponses.find(item => item.getItem().getTitle() == "OTP"); //
Replace with your OTP question title
var submittedOtp = otpQuestion ? otpQuestion.getResponse() : null;
var cache = CacheService.getScriptCache();
var cachedOtp = cache.get(email + "_otp");
if (submittedOtp && cachedOtp && submittedOtp == cachedOtp) {
cache.put(email + "_verified", true, 3600);
e.response.withItemResponses().setConfirmationMessage("Your email has been
verified. Thank you for your submission!");
} else if (submittedOtp) {
e.response.withItemResponses().setConfirmationMessage("Incorrect OTP. Please try
again.");
} else {
e.response.withItemResponses().setConfirmationMessage("Please enter the OTP.");
}
}
function generateOTP() {
var otp = Math.floor(100000 + Math.random() * 900000); // 6-digit OTP
return otp.toString();
}
function sendEmail(email, otp, editUrl) { // Add editUrl parameter
var subject = "Your OTP for Google Form Verification";
var body = "Your OTP is: " + otp + "nnPlease click the following link to complete
your submission: " + editUrl; // Include editUrl
try {
MailApp.sendEmail(email, subject, body);
Logger.log("OTP email sent successfully to: " + email);
} catch (error) {
Logger.log("ERROR sending OTP email: " + error);
}
}
Below is a screenshot of the two triggers set.

…Finally, below are the error logs upon the execution of the triggers:
Cloud logs
Feb 24, 2025, 3:22:24 PM Info onSubmit triggered
Feb 24, 2025, 3:22:24 PM Info Email: [email protected]
Feb 24, 2025, 3:22:25 PM Info Edit URL: https://docs.google.com/forms/d/e/1FAIpQLSdtWPCzDm92Tah34gnj4UZHiZgQ22L6nQPxC5LrnwDCoUphsA/viewform?edit2=2_ABaOnud9q2OpMIXEeE6nvxwkIxPCr6JbP-5y9OQJoLoNKEN2va_vWQLR-nQZ8QauOy8iPHw
Feb 24, 2025, 3:22:25 PM Info OTP email sent successfully to: [email protected]
Feb 24, 2025, 3:22:25 PM Error TypeError: e.response.withItemResponses is not a function
at onSubmit(Code:39:14)
Cloud logs
Feb 24, 2025, 3:22:22 PM Info onFormSubmit triggered
Feb 24, 2025, 3:22:23 PM Error TypeError: e.response.withItemResponses is not a function
at onFormSubmit(Code:62:16)