HomeAbout MeBook a Call

Dynamic Google Form Validation with Vertex AI and Apps Script

By Vo Tu Duc
May 04, 2026
Dynamic Google Form Validation with Vertex AI and Apps Script

Google Forms is the go-to for quick data collection, but its biggest weakness can put your data integrity at risk as your processes become more complex.

image 0

The Challenge of Data Integrity in Google Forms

Google Forms is an undeniably powerful tool. It’s the go-to solution for whipping up surveys, registration forms, and simple data collection tools in minutes. It’s accessible, free, and integrates seamlessly into the [Automatically create new folders in Google Drive, generate templates in new folders, fill out text automatically in new files, and save info in [Automated Web Scraping with [Multilingual Text-to-Speech Tool with SocialSheet Streamline Your Social Media Posting 123](https://votuduc.com/Multilingual-Text-to-Speech-Tool-with-Google-Workspace-p809282)](https://votuduc.com/Automated-Web-Scraping-with-Google-Sheets-p292968)](https://workspace.google.com/marketplace/app/auto_create_folder_and_files/430076014869) ecosystem. But as your processes become more sophisticated, you start to bump into the ceiling of its capabilities, particularly when it comes to data quality. The raw, unfiltered data that flows into your connected Google Sheet can quickly become a liability, requiring hours of manual cleanup, follow-up emails, and corrections. This is the core challenge: ensuring the data you collect is not just present, but also meaningful, contextual, and immediately usable.

Why Standard Validation Falls Short

Google Forms does offer a set of built-in validation rules, and for simple cases, they work perfectly. You can ensure a field contains a valid email address, a number within a specific range, or a certain number of characters. This is rule-based validation, and it’s fundamentally rigid.

image 1

Where does it break down? It falls short the moment you need to validate quality or intent, not just format. Consider these scenarios:

  • Subjective Content: You ask for “Project Feedback.” How do you validate that the user’s response is constructive and not just “It was fine”? Standard rules can’t differentiate between a detailed paragraph and a two-word dismissal.

  • Complex Criteria: You have a form for submitting project proposals. You need the “Project Description” to include a problem statement, a proposed solution, and key metrics. A simple “text contains” rule is too brittle and easily bypassed.

  • [How to build a Custom Sentiment Analysis System for Operations Feedback Using Google Forms OSD App Clinical Trial Management and Building Self Correcting Agentic Workflows with Vertex AI](https://votuduc.com/How-to-build-a-Custom-Sentiment-Analysis-System-for-Operations-Feedback-Using-Google-Forms-AppSheet-and-Vertex-AI-p428528): You’re collecting customer reviews. You want to automatically flag negative reviews for immediate follow-up. Google Forms has no native concept of sentiment.

  • Contextual Relevance: You ask users to describe their technical issue. How do you ensure the description is actually about a technical problem and not a billing question? You can’t.

Standard validation operates on a black-and-white, yes-or-no basis. It can check the what, but it has no ability to understand the how or the why behind the user’s input. This limitation is the primary driver for seeking a more intelligent approach.

Introducing an Intelligent Solution with Vertex AI and Apps Script

This is where we graduate from static rules to dynamic intelligence. By combining the [Automated Job Creation in Real Time Jobber and Google Sheets Integration from Gmail](https://votuduc.com/Automated-Job-Creation-in-Jobber-from-Gmail-p115606) power of [AI Powered Cover Letter Automated Quote Generation and Delivery System for Jobber Engine](https://votuduc.com/AI-Powered-Cover-Letter-Automated Work Order Processing for UPS-Engine-p111092) with the cognitive capabilities of Google’s Vertex AI, we can build a validation layer that understands context, nuance, and quality.

Here’s how the two components create a powerful synergy:

  1. **Genesis Engine AI Powered Content to Video Production Pipeline: The Conductor. Apps Script acts as the essential bridge. It lives within your AC2F Streamline Your Google Drive Workflow environment and can be triggered by events, such as a new Google Form submission. When a user submits a form, our Apps Script code will intercept the data before it’s finalized, giving us a crucial window to perform our custom validation. It’s the mechanism that lets us connect our form to an external brain.

  2. Vertex AI: The Brain. This is where the magic happens. Vertex AI provides access to Google’s state-of-the-art Generative AI models, like Gemini. Instead of checking if a text field contains a specific word, we can send the user’s entire response to the AI and ask sophisticated questions in plain English:

  • “Is the sentiment of this feedback positive, negative, or neutral?”

  • “Does this project description clearly outline a problem and a solution? Provide a simple ‘yes’ or ‘no’ answer.”

  • “Based on this user’s request, classify it into one of the following categories: Technical Support, Sales Inquiry, or Account Management.”

This combination transforms our form from a simple data receptacle into an intelligent gatekeeper. We are no longer limited by predefined rules; our validation logic is now as flexible and powerful as the language model we use.

What You Will Build in This Tutorial

To make this concept concrete, we’re going to build a practical, real-world solution. By the end of this tutorial, you will have created an intelligent “Project Idea Submission” form that actively vets the quality of submissions.

Here’s the step-by-step breakdown of our final product:

  1. A Google Form: We’ll set up a simple form with fields like “Your Name,” “Project Title,” and a crucial paragraph field for the “Project Description.”

  2. An Apps Script Trigger: We will write a script that automatically runs every time a new response is submitted to our form.

  3. Vertex AI Integration: The script will take the text from the “Project Description” field and send it to the Vertex AI Gemini API.

  4. AI-Powered Validation Logic: We will craft a specific prompt for the AI, asking it to evaluate whether the description meets a set of quality criteria (e.g., “Does it mention a clear goal? Does it identify the target audience?”).

  5. Conditional Automated Feedback: Based on the AI’s “pass” or “fail” assessment, our script will perform an action:

  • On Success: The data is written to a Google Sheet, perhaps with an extra column automatically populated with “AI-Approved.”

  • **On Failure: The script will automatically send a personalized email to the submitter, explaining why their submission was rejected and providing the AI-generated feedback on how to improve and resubmit their idea.

Prerequisites and Environment Setup

Alright, before we dive into the code and start orchestrating our AI-powered validation, we need to lay the groundwork. A solid setup is the foundation of any successful project, and this one is no different. We’ll walk through configuring your cloud environment, creating the user-facing form, and linking everything together with Apps Script. Let’s get our digital hands dirty.

Configuring Your Google Cloud Project and Enabling the Vertex AI API

Our Apps Script won’t be able to communicate with Vertex AI on its own; it needs a properly configured Google Cloud Project to act as the bridge. This project handles authentication, permissions, and, crucially, billing for the API calls we’ll be making.

  1. Select or Create a Google Cloud Project:
  • Navigate to the Google Cloud Console.

  • If you have an existing project you’d like to use, select it from the project dropdown at the top of the page.

If not, click the dropdown and select* “NEW PROJECT”**. Give it a descriptive name like form-validation-ai and click “CREATE”.

  1. Enable Billing:
  • Vertex AI is a powerful, enterprise-grade service, and it requires a billing account to be enabled. This is a non-negotiable step.

From your project’s dashboard, navigate to the* “Billing”** section in the left-hand menu.

  • If your project isn’t already linked to a billing account, you’ll be prompted to link an existing one or create a new one. Follow the on-screen instructions. Don’t worry, the usage for this project will be well within the free tier or cost mere cents.
  1. Enable the Vertex AI API:
  • With billing sorted, we can now activate the magic. We need to explicitly tell Google Cloud that this project is authorized to use the Vertex AI service.

In the search bar at the top of the console, type* “Vertex AI API”** and select it from the results.

You’ll land on the API’s dashboard. If it’s not already enabled, click the blue* “ENABLE”** button. This process might take a minute or two.

Once you see the API dashboard with traffic and latency metrics (they’ll all be zero for now), you’re all set on the Google Cloud side.

Creating the Google Form and its Destination Spreadsheet

Next, we need the user-facing component: the Google Form itself. This is where we’ll collect the input that our AI will later validate. We also need a place to store these responses, which is where Google Sheets comes in.

  1. Create a New Google Form:
  • Go to forms.google.com and create a new blank form.

  • Give your form a title, such as “Project Idea Submission”.

For our example, let’s add a single question. Set the question type to* “Paragraph”** and enter the question text: Please describe your project idea in 1-2 sentences. This gives us a nice chunk of free-form text for our AI to analyze.

  1. Link to a Google Sheet:
  • This is a critical step. The Apps Script we write will be triggered by form submissions, and it will interact with the data stored in the linked spreadsheet.

At the top of the Form editor, click on the* “Responses”** tab.

Click the green* “Link to Sheets”** icon.

A dialog will appear. Select* “Create a new spreadsheet”**. You can keep the default name or give it a custom one. Click “CREATE”.

A new browser tab will open with your brand-new Google Sheet. You’ll see columns for Timestamp and the question you created. Every time a user submits the form, a new row will be added here automatically. This sheet is now the source of truth for our form’s data.

Setting Up the Apps Script Project via the Form Editor

Finally, let’s create the Apps Script project that will house our logic. By creating it directly from the Form, it becomes “bound” to the form, making it easier to set up triggers and interact with form events.

  1. Open the Script Editor:
  • Go back to your Google Form editor.

  • Click the three-dot menu (More) in the top-right corner.

Select* “Script editor”** from the dropdown menu.

Step 1: Capturing Submissions with Apps Script Triggers

The entire process kicks off the moment a user hits “Submit” on your Google Form. To intercept this action and get our hands on the data, we’ll use [Architecting Multi Tenant AI Workflows in Building Modular Agentic Apps Script with Gemini Function Calling](https://votuduc.com/architecting-multi-tenant-ai-workflows-in-google-apps-script-p-20260321290501). Specifically, we’ll leverage an installable trigger that runs a function automatically every time a new submission comes in. This is the foundational step that connects our form to the powerful validation logic we’re about to build.

Understanding the onFormSubmit Event Object

When a trigger fires in response to a form submission, it doesn’t just run our function in a vacuum. It passes along a crucial piece of information: the event object. This object is a treasure trove of contextual data about the event that just occurred. For a form submission, it contains everything we need to know about the form itself and the specific answers the user provided.

The event object, conventionally named e in the function parameter, has several key properties. The most important one for our purpose is response. This property is an instance of the FormResponse class, which holds all the answers from a single submission.

Here’s a simplified look at the structure of the event object e:


{

"authMode": "LIMITED",

"response": { / *A FormResponse object* / },

"source": { / *A Form object* / },

"triggerUid": "123456789"

}

  • response: This is our primary target. It contains a method called getItemResponses() which returns an array of all the individual question-and-answer pairs for that submission.

  • source: This refers to the Google Form itself, allowing you to programmatically get the form’s title, ID, or other properties if needed.

  • authMode: This tells you about the authorization level under which the script is running.

By accessing e.response, we can systematically extract every piece of data submitted by the user.

Writing the Initial Trigger Function to Access Response Data

Let’s translate this theory into practice. We’ll write our first Apps Script function to capture the submission and log the data. This allows us to verify that our trigger is working correctly and see the exact structure of the data we’re receiving.

  1. Open your Google Form.

  2. Click the three-dot menu (More) in the top-right corner and select Script editor. This will open the Apps Script IDE.

  3. Replace any boilerplate code with the following function:


/**

* This function is triggered automatically whenever the Google Form is submitted.

* It iterates through the user's responses and logs them.

*

* @param {Object} e The event object passed by the onFormSubmit trigger.

*/

function onFormSubmitHandler(e) {

// Get the FormResponse object from the event object

const formResponse = e.response;

// Get an array of all the ItemResponse objects

const itemResponses = formResponse.getItemResponses();

// Log the email of the respondent (if collected)

const respondentEmail = formResponse.getRespondentEmail();

Logger.log(`Processing submission from: ${respondentEmail}`);

// Loop through each ItemResponse to get the question and answer

for (let i = 0; i < itemResponses.length; i++) {

const itemResponse = itemResponses[i];

const question = itemResponse.getItem().getTitle();

const answer = itemResponse.getResponse();

// Log the question-answer pair for debugging

Logger.log(`Question: "${question}" | Answer: "${answer}"`);

}

}

Setting up the Trigger:

This function won’t run on its own. You need to tell Google Forms to execute it upon every submission.

  1. In the Apps Script editor, click the Triggers icon (a clock) on the left-hand sidebar.

  2. Click the + Add Trigger button in the bottom-right.

  3. Configure the trigger with the following settings:

  • Choose which function to run: onFormSubmitHandler

  • Choose which deployment should run: Head

  • Select event source: From form

  • Select event type: On form submit

  1. Click Save. You’ll be prompted to authorize the script’s permissions. Review and allow them.

Now, go back to your form, submit a test response, and then check the logs in the Apps Script editor (by clicking the Executions icon) to see your questions and answers printed out.

Structuring the User Input for AI Processing

Raw data is great for logging, but AI models, particularly Large Language Models (LLMs) like those in Vertex AI, perform best when they receive clear, well-structured input. Simply sending a jumble of answers isn’t enough. We need to format the user’s submission into a coherent prompt or data structure that the AI can easily parse and understand.

A highly effective method is to convert the question-and-answer pairs into a simple JSON object or a formatted string. This preserves the context of each answer by keeping it tied to its original question.

Let’s modify our function to build this structured object. This object will be the payload we eventually send to our Vertex AI endpoint.


/**

* This function is triggered automatically whenever the Google Form is submitted.

* It captures the user's responses and structures them into a JSON object

* suitable for processing by an AI model.

*

* @param {Object} e The event object passed by the onFormSubmit trigger.

* @returns {Object} A structured object containing the form responses.

*/

function onFormSubmitHandler(e) {

const formResponse = e.response;

const itemResponses = formResponse.getItemResponses();

// Initialize an empty object to hold our structured data

const structuredData = {};

// Loop through each response and populate the object

itemResponses.forEach(itemResponse => {

const question = itemResponse.getItem().getTitle();

const answer = itemResponse.getResponse();

// Use the question as the key and the answer as the value

// A simple transformation can make keys more programmatic (e.g., lowercase, no spaces)

const key = question.replace(/\s+/g, '_').toLowerCase();

structuredData[key] = answer;

});

// Log the final structured object to see the result

Logger.log("Structured data for AI processing:");

Logger.log(JSON.stringify(structuredData, null, 2));

// In the next steps, we will send this 'structuredData' object to Vertex AI

// For now, we'll return it for potential chaining or testing

return structuredData;

}

After submitting another test response, your logs will now show a clean, predictable JSON object. For a form with questions “Your Name” and “Project Description”, the output would look something like this:


{

"your_name": "Ada Lovelace",

"project_description": "I want to build an analytical engine to perform complex calculations and automate computational tasks."

}

This structured format is perfect. It’s machine-readable, maintains the crucial link between each question and its answer, and is ready to be sent as a payload to our AI validation service.

Step 2: Integrating Vertex AI for Intelligent Analysis

With our Apps Script function triggered and the user’s input in hand, it’s time for the main event: sending that data to a powerful AI model for analysis. This is where we bridge the gap between a simple form and an intelligent validation system. We’ll use Google’s Vertex AI platform, specifically the Gemini Pro model, to evaluate the substance of the user’s response. This process involves four key stages: authenticating our script, crafting a precise request for the AI, executing the API call, and interpreting the model’s response.

Authenticating with Google Cloud from Apps Script

Before our script can talk to Vertex AI, it needs permission. We can’t just send an anonymous request to a powerful cloud service. Thankfully, Apps Script has a streamlined, built-in mechanism for handling this using OAuth2. It allows the script to act on behalf of the user who is running it (or who installed the trigger).

The magic happens with ScriptApp.getOAuthToken(). This method generates a short-lived access token that we can include in our API request, proving to Google Cloud that our script is authorized.

To enable this, you must explicitly declare the necessary permission scope in your script’s manifest file.

  1. In the Apps Script editor, go to Project Settings (the gear icon ⚙️).

  2. Check the box for “Show ‘appsscript.json’ manifest file in editor”.

  3. Return to the Editor (the <> icon) and open the newly visible appsscript.json file.

  4. Add the cloud-platform scope to the oauthScopes array. This scope grants the necessary permissions to call Vertex AI APIs.

Your appsscript.json file should look something like this:


{

"timeZone": "America/New_York",

"dependencies": {},

"exceptionLogging": "STACKDRIVER",

"runtimeVersion": "V8",

"oauthScopes": [

"https://www.googleapis.com/auth/forms",

"https://www.googleapis.com/auth/script.external_request",

"https://www.googleapis.com/auth/cloud-platform"

]

}

The first time you run a function that uses this scope, Google will prompt you (or the user) with a consent screen to authorize these permissions. This is a one-time setup step for each user.

Crafting a Powerful Prompt for Gemini to Evaluate Input Quality

The quality of your AI’s analysis is directly proportional to the quality of your prompt. We aren’t just asking a simple question; we are instructing the model on its role, the context of the task, and—most importantly—the exact format for its response. A well-structured prompt ensures we get back predictable, machine-readable data.

Let’s assume our form has a long-answer question asking for “Project Feedback.” We want to validate that the feedback is substantive and not just a one-word answer like “good” or “ok”.

Here’s how we can construct a powerful prompt to guide Gemini:


/**

* Creates a structured prompt for the Gemini model to evaluate text quality.

* @param {string} feedbackText The user's input from the Google Form.

* @return {string} A formatted prompt string.

*/

function createValidationPrompt(feedbackText) {

// We instruct the model on its role, the criteria, and the desired output format.

const prompt = `

You are an AI assistant responsible for validating the quality of user-submitted project feedback.

**Context:**

High-quality feedback is constructive, specific, and provides enough detail to be actionable. It should be more than a few words.

Low-quality feedback is vague, generic, non-constructive, or too short to be useful (e.g., "it was good", "no comment", "ok").

**Task:**

Analyze the following user feedback and classify its quality.

**User Feedback:**

"${feedbackText}"

**Output Format:**

Respond ONLY with a valid JSON object with two keys:

1. "quality": A string that is either "high" or "low".

2. "reason": A brief, one-sentence explanation for your classification.

Example Response:

{"quality": "low", "reason": "The feedback is too vague and does not provide specific details."}

`;

return prompt;

}

This prompt is effective because:

  • It defines a persona: “You are an AI assistant…”

  • It provides clear context: It explains what constitutes “high” vs. “low” quality.

  • It isolates the user input: The feedback is clearly demarcated.

  • It enforces a strict output format: Requesting a JSON object is crucial for making the response easy to parse in our code later.

Executing the API Call to Vertex AI

Now we combine our authentication token and our crafted prompt to make a POST request to the Vertex AI API endpoint. We’ll use Apps Script’s built-in UrlFetchApp service for this.

The endpoint for the Gemini Pro model follows this structure:

https://us-central1-aiplatform.googleapis.com/v1/projects/YOUR_PROJECT_ID/locations/us-central1/publishers/google/models/gemini-pro:predict

Remember to replace YOUR_PROJECT_ID with your actual Google Cloud Project ID.

Here is the function that brings it all together:


/**

* Calls the Vertex AI Gemini API to get an analysis of the provided text.

* @param {string} prompt The fully constructed prompt for the model.

* @return {object|null} The parsed JSON response from the API, or null on error.

*/

function callVertexAI(prompt) {

const projectId = 'your-gcp-project-id'; // <-- IMPORTANT: Replace with your Project ID

const location = 'us-central1';

const modelId = 'gemini-pro';

const endpoint = `https://${location}-aiplatform.googleapis.com/v1/projects/${projectId}/locations/${location}/publishers/google/models/${modelId}:predict`;

const token = ScriptApp.getOAuthToken();

const payload = {

"instances": [

{ "prompt": prompt }

],

"parameters": {

"temperature": 0.2,

"maxOutputTokens": 256,

"topK": 40,

"topP": 0.95

}

};

const options = {

'method': 'post',

'contentType': 'application/json',

'headers': {

'Authorization': 'Bearer ' + token

},

'payload': JSON.stringify(payload),

'muteHttpExceptions': true // Important for custom error handling

};

try {

const response = UrlFetchApp.fetch(endpoint, options);

const responseCode = response.getResponseCode();

const responseBody = response.getContentText();

if (responseCode === 200) {

return JSON.parse(responseBody);

} else {

Logger.log(`Error calling Vertex AI API. Status: ${responseCode}, Body: ${responseBody}`);

return null;

}

} catch (e) {

Logger.log(`Exception during API call: ${e.message}`);

return null;

}

}

Parsing the JSON Response from the AI Model

If the API call was successful, the callVertexAI function returns a parsed JSON object. However, the actual content generated by Gemini is nested inside this object. Furthermore, because we asked the model to give us a JSON string, we essentially have a JSON object containing a string that is also JSON.

Our final step is to safely parse this nested structure to get our final quality and reason values.

A typical successful response body from the API looks like this:


{

"predictions": [

{

"content": "```json\n&#x7B;\"quality\": \"low\", \"reason\": \"The feedback is too short and lacks any actionable detail.\"&#x7D;\n```",

"safetyAttributes": { ... },

"citationMetadata": { ... }

}

]

}

Notice the content field contains our desired JSON, but it’s wrapped in a string, sometimes with Markdown code fences (```json). We need to extract this and parse it again.

Here’s a robust function to handle the parsing:


/**

* Parses the raw response from the Vertex AI API to extract the model's structured output.

* @param &#x7B;object&#x7D; apiResponse The parsed JSON object from the callVertexAI function.

* @return &#x7B;object|null&#x7D; An object with 'quality' and 'reason' keys, or null if parsing fails.

*/

function parseGeminiResponse(apiResponse) &#x7B;

if (!apiResponse || !apiResponse.predictions || !apiResponse.predictions[0]) &#x7B;

Logger.log('Invalid API response structure.');

return null;

&#x7D;

try &#x7B;

let contentString = apiResponse.predictions[0].content;

// Clean up potential markdown code fences from the model's output

contentString = contentString.replace(/```json\n/g, '').replace(/\n```/g, '');

// The model's output is a string that needs to be parsed into a JSON object

const validationResult = JSON.parse(contentString);

if (validationResult.quality && validationResult.reason) &#x7B;

return validationResult; // Success! e.g., &#x7B; quality: 'low', reason: '...' &#x7D;

&#x7D; else &#x7B;

Logger.log('Parsed JSON is missing required keys "quality" or "reason".');

return null;

&#x7D;

&#x7D; catch (e) &#x7B;

Logger.log(`Failed to parse nested JSON from model response: ${e.message}`);

Logger.log(`Original content: ${apiResponse.predictions[0].content}`);

return null;

&#x7D;

&#x7D;

With these functions in place, we now have a complete workflow. We can take user input, send it to a world-class AI for nuanced analysis, and get back a clean, structured object that tells us whether the input is high-quality and why. The next step is to use this result to take action back in our Google Form.

Step 3: Automating Real-Time User Feedback

What’s the point of an automated validation system if it keeps the results to itself? The final, crucial step in closing the loop is to communicate the outcome of the AI’s analysis back to the user or a designated reviewer. This immediate feedback transforms a simple data collection tool into an interactive, intelligent system. Fortunately, Google Apps Script makes this incredibly straightforward with its native mail services.

Using the MailApp Service to Send Notifications

Automated Client Onboarding with Google Forms and Google Drive. provides a suite of built-in services accessible directly within Apps Script, and MailApp is one of the most powerful. It allows your script to send emails on your behalf, without needing to configure SMTP servers or handle complex authentication protocols.

At its core, the service is elegantly simple. The primary method you’ll use is MailApp.sendEmail().


// A basic example of sending an email

MailApp.sendEmail("[email protected]", "Submission Status", "Your submission has been processed.");

The first time you run a script that uses MailApp, Google will prompt you for permission. You must grant the script authorization to “Send email as you.” This is a one-time setup step for each script project.

The sendEmail method is highly versatile. While the simple version takes a recipient, subject, and plain-text body, you can also pass a single JavaScript object for more advanced options, including sending richly formatted HTML emails, adding CC/BCC recipients, and including attachments.


// An advanced example using an options object

MailApp.sendEmail(&#x7B;

to: "[email protected]",

cc: "[email protected]",

subject: "Submission Status Update",

htmlBody: "<h1>Submission Processed</h1><p>Your submission has been reviewed. See below for details.</p>"

&#x7D;);

For our use case, this service is the perfect tool to instantly deliver the AI’s verdict.

Building Conditional Logic for Approval or Rejection Emails

Our system now has a clear decision point. The Vertex AI model has analyzed the form data and provided a structured response—let’s assume it returns a JSON object with a status (e.g., “Approved” or “Rejected”) and a reason. Our script’s job is to act as the dispatcher, routing this decision into the correct notification.

This is a classic use case for an if...else statement. We’ll check the status from the AI’s response and prepare a different email template for each outcome.

Let’s see how this looks in practice. We’ll build on our previous function, taking the AI’s parsed response as an input.


function sendNotification(formResponse, aiAnalysis) &#x7B;

const recipientEmail = formResponse.namedValues['Email Address'][0];

let emailSubject = "";

let emailBody = "";

if (aiAnalysis.status === 'Approved') &#x7B;

// Logic for the approval email

emailSubject = "✅ Your Submission has been Approved!";

emailBody = "Congratulations! Your recent submission has been reviewed and approved by our system.";

&#x7D; else &#x7B;

// Logic for the rejection email

emailSubject = "❌ Action Required: Your Submission Needs Revision";

emailBody = "Thank you for your submission. After a review, our system has determined that it requires revision.";

&#x7D;

// The sendEmail call will happen after we construct the message

MailApp.sendEmail(recipientEmail, emailSubject, emailBody);

&#x7D;

This structure cleanly separates the two possible paths. The if block handles the “happy path” (approval), while the else block manages the rejection scenario. Now, we can make these emails far more useful by injecting specific details from the form and the AI’s feedback.

Populating Emails with Dynamic Content from the Form and AI Analysis

Static emails are good, but personalized, dynamic emails are great. They provide context, clarity, and actionable feedback. We can easily achieve this by using JavaScript’s template literals (the backtick ` character) to construct our email strings, embedding variables directly into them.

We have two primary sources of dynamic data:

  1. The Form Response (formResponse): This object contains all the data the user submitted, like their name, the title of their submission, or any other relevant fields.

  2. **The AI Analysis (aiAnalysis): This object contains the verdict from Vertex AI, crucially including the reason for a rejection.

Let’s upgrade our sendNotification function to create rich, contextual emails.


/**

* Sends a dynamic notification email based on AI analysis.

* @param &#x7B;Object&#x7D; formResponse - The form submission event object.

* @param &#x7B;Object&#x7D; aiAnalysis - The parsed JSON response from the Vertex AI API.

*/

function sendNotification(formResponse, aiAnalysis) &#x7B;

// Extract dynamic data from the form submission

const recipientEmail = formResponse.namedValues['Email Address'][0];

const userName = formResponse.namedValues['Your Name'][0];

const submissionTitle = formResponse.namedValues['Submission Title'][0];

let emailSubject = "";

let htmlEmailBody = ""; // We'll use HTML for better formatting

if (aiAnalysis.status === 'Approved') &#x7B;

emailSubject = `✅ Your Submission '${submissionTitle}' has been Approved!`;

htmlEmailBody = `

<p>Hi ${userName},</p>

<p>Great news! Your submission titled "<b>${submissionTitle}</b>" has passed our automated validation and has been approved.</p>

<p>No further action is required from you at this time.</p>

<p>Thank you,<br />The Automated System</p>

`;

&#x7D; else &#x7B;

// Extract the specific reason for rejection from the AI response

const rejectionReason = aiAnalysis.reason;

emailSubject = `❌ Action Required: Your Submission '${submissionTitle}' Needs Revision`;

htmlEmailBody = `

<p>Hi ${userName},</p>

<p>Thank you for your submission titled "<b>${submissionTitle}</b>".</p>

<p>Our automated validation system has flagged it for revision. Please review the feedback below and resubmit the form.</p>

<hr>

<p><b>Reason for Rejection:</b></p>

<p style="color: #D32F2F; font-family: monospace; background-color: #FBE9E7; padding: 10px; border-radius: 4px;">

${rejectionReason}

</p>

<hr>

<p>We appreciate you taking the time to make the necessary corrections.</p>

<p>Thank you,<br />The Automated System</p>

`;

&#x7D;

// Send the dynamically generated email using the advanced options

MailApp.sendEmail(&#x7B;

to: recipientEmail,

subject: emailSubject,

htmlBody: htmlEmailBody

&#x7D;);

&#x7D;

With this implementation, the user receives an email that is not only immediate but also deeply personal and helpful. An approval email confirms the exact item that was approved. More importantly, a rejection email provides the specific, AI-generated reason, empowering the user to correct the submission without requiring manual intervention from an administrator. This completes the automated feedback loop, making the entire process efficient and user-friendly.

The Complete Code: A Full Walkthrough

With the high-level architecture established, let’s dive into the core of our solution: the Google Apps Script code. This script acts as the central nervous system, orchestrating the data flow from the form submission to Vertex AI and back. We’ll break it down into logical, reusable functions.

The Main Controller Function (onFormSubmit)

This is the heart of our operation. The onFormSubmit function is a special type of function in Apps Script known as a trigger. We’ll configure it to run automatically every time a user submits our Google Form. Its primary role is to act as a controller—gathering data, calling helper functions, and directing traffic based on the results.

Here’s the step-by-step logic:

  1. Capture the Event: The function is triggered with an event object (e) that contains all the information about the form submission, including the user’s answers.

  2. Extract and Prepare Data: We’ll pull the specific responses we need for validation from the event object. For this example, let’s assume our form has a field named “Product Description” that we want to validate.

  3. Construct the Prompt: This is a critical step. We will craft a precise prompt for the Vertex AI model, combining our validation rules with the user’s submitted text. This prompt engineers the AI to act as our validation engine.

  4. Invoke Vertex AI: The function calls our dedicated helper function (callVertexAI), passing the constructed prompt to it.

  5. Process the Result: Upon receiving the AI’s response, it parses the result. We’ll design our prompt to ask for a JSON response (e.g., {"isValid": boolean, "reason": "string"}) for easy and reliable parsing.

  6. Take Action: Based on the validation result, it calls another helper (sendNotificationEmail) to alert an administrator or the user of the outcome.


/**

* Main function triggered on every form submission.

* Acts as the controller for the validation workflow.

*

* @param &#x7B;Object&#x7D; e The event object passed by the form submission trigger.

*/

function onFormSubmit(e) &#x7B;

try &#x7B;

// 1. Get the form response object

const formResponse = e.response;

const itemResponses = formResponse.getItemResponses();

// 2. Extract specific answers by question title.

// IMPORTANT: Replace "Product Description" and "Submitter Email" with the exact titles of your form questions.

let productDescription = '';

let userEmail = formResponse.getRespondentEmail(); // Gets email if form collects it automatically

for (let i = 0; i < itemResponses.length; i++) &#x7B;

const question = itemResponses[i].getItem().getTitle();

const answer = itemResponses[i].getResponse();

if (question === 'Product Description') &#x7B;

productDescription = answer;

&#x7D;

// You can add more 'if' blocks here to capture other fields

&#x7D;

if (!productDescription) &#x7B;

throw new Error("The 'Product Description' field was not found or is empty.");

&#x7D;

// 3. Construct a detailed prompt for the AI model

const prompt = `

You are a content validation API. Analyze the following product description.

The description must meet these criteria:

1. It must be at least 20 words long.

2. It must not contain any profanity or offensive language.

3. It must mention a specific feature of the product.

4. It must not contain any placeholder text like "Lorem Ipsum" or "TBD".

Analyze the following text:

"${productDescription}"

Return your response ONLY as a valid JSON object with two keys:

- "isValid": a boolean (true if all criteria are met, false otherwise).

- "reason": a brief string explaining why it failed, or "All criteria met." if it passed.

`;

// 4. Call the Vertex AI helper function

const aiResponse = callVertexAI(prompt);

Logger.log(`Raw AI Response: ${aiResponse}`);

// 5. Parse the JSON response from the AI

const validationResult = JSON.parse(aiResponse);

const isValid = validationResult.isValid;

const reason = validationResult.reason;

Logger.log(`Validation Result: ${isValid}, Reason: ${reason}`);

// 6. Take action based on the result

sendNotificationEmail(userEmail, isValid, reason, productDescription);

&#x7D; catch (error) &#x7B;

// Centralized error handling (more on this later)

Logger.log(`ERROR in onFormSubmit: ${error.toString()}`);

sendNotificationEmail('[email protected]', false, `An unexpected error occurred: ${error.message}`, `Error processing submission.`);

&#x7D;

&#x7D;

The Vertex AI API Call Helper Function

To keep our main function clean and adhere to the single-responsibility principle, we’ll abstract the entire API interaction into its own function, callVertexAI. This function handles the technical details of authenticating and communicating with the Vertex AI endpoint.

Its responsibilities include:

  1. Configuration: Fetching necessary details like the GCP Project ID and the specific model endpoint from PropertiesService, which is a secure way to store script properties.

  2. Authentication: Getting a valid OAuth2 token to authorize the API request.

  3. Payload Construction: Building the JSON payload in the exact format required by the Vertex AI API. This includes the prompt and model parameters like temperature (creativity) and maxOutputTokens.

  4. API Request: Using Apps Script’s built-in UrlFetchApp service to send the POST request to the API endpoint.

  5. Response Handling: Receiving the response, extracting the generated content, and returning it to the calling function.


/**

* Calls the Vertex AI API to get a prediction.

*

* @param &#x7B;string&#x7D; prompt The prompt to send to the model.

* @returns &#x7B;string&#x7D; The content of the AI's response.

*/

function callVertexAI(prompt) &#x7B;

// Store these in Script Properties for security and manageability

// Go to Project Settings > Script Properties

const scriptProperties = PropertiesService.getScriptProperties();

const projectId = scriptProperties.getProperty('GCP_PROJECT_ID');

const modelId = 'gemini-1.0-pro'; // Or your specific model

const location = 'us-central1'; // Or your model's location

const apiEndpoint = `https://${location}-aiplatform.googleapis.com/v1/projects/${projectId}/locations/${location}/publishers/google/models/${modelId}:predict`;

// Get the active user's OAuth2 token to authenticate the request

const accessToken = ScriptApp.getOAuthToken();

// Construct the request payload in the format Vertex AI expects

const payload = &#x7B;

"instances": [&#x7B;

"content": prompt

&#x7D;],

"parameters": &#x7B;

"temperature": 0.1, // Low temperature for deterministic, factual responses

"maxOutputTokens": 256,

"topK": 1,

"topP": 0

&#x7D;

&#x7D;;

// Set up the options for the UrlFetchApp call

const options = &#x7B;

'method': 'post',

'contentType': 'application/json',

'headers': &#x7B;

'Authorization': `Bearer ${accessToken}`

&#x7D;,

'payload': JSON.stringify(payload),

'muteHttpExceptions': true // Important for custom error handling

&#x7D;;

// Make the API call

const response = UrlFetchApp.fetch(apiEndpoint, options);

const responseCode = response.getResponseCode();

const responseBody = response.getContentText();

if (responseCode === 200) &#x7B;

const jsonResponse = JSON.parse(responseBody);

// Navigate the response structure to get the actual content

return jsonResponse.predictions[0].content;

&#x7D; else &#x7B;

// Throw a detailed error if the API call fails

throw new Error(`Vertex AI API request failed with code ${responseCode}: ${responseBody}`);

&#x7D;

&#x7D;

The Email Notification Helper Function

Once the validation is complete, we need a way to communicate the result. This helper function, sendNotificationEmail, is a simple but effective utility for this purpose. It dynamically constructs and sends an email, making the system’s feedback loop complete.


/**

* Sends an email notification about the validation result.

*

* @param &#x7B;string&#x7D; recipient The email address to send the notification to.

* @param &#x7B;boolean&#x7D; isValid The result of the validation.

* @param &#x7B;string&#x7D; reason The reason provided by the AI.

* @param &#x7B;string&#x7D; submittedContent The original content that was validated.

*/

function sendNotificationEmail(recipient, isValid, reason, submittedContent) &#x7B;

const subject = isValid ? '✅ Form Submission Approved' : '❌ Action Required: Form Submission Rejected';

const status = isValid ? 'APPROVED' : 'REJECTED';

const body = `

A new form submission has been processed with the following result:

Status: ${status}

Reason: ${reason}

------------------------------------

Submitted Content:

------------------------------------

${submittedContent}

------------------------------------

This is an automated notification.

`;

// Use MailApp service to send the email

MailApp.sendEmail(recipient, subject, body);

Logger.log(`Notification email sent to ${recipient}`);

&#x7D;

Handling Errors and Edge Cases

A production-ready script must be resilient. What happens if the Vertex AI API is down? What if the model returns an unexpected format? What if the form changes and our script can’t find a field? This is where robust error handling comes in.

Our primary strategy is the try...catch block within the main onFormSubmit function.

  • try block: This wraps our entire “happy path” logic. If any error occurs inside this block (e.g., a failed API call from callVertexAI or a JSON parsing error), execution immediately jumps to the catch block.

  • catch (error) block: This is our safety net.

  • It logs the detailed error message using Logger.log() for debugging purposes. You can view these logs in the Apps Script editor under “Executions”.

  • It calls the sendNotificationEmail function to send an alert to a designated administrator. This ensures that a system failure doesn’t go unnoticed. The submission can then be reviewed manually.

  • muteHttpExceptions: In our callVertexAI function, setting 'muteHttpExceptions': true is crucial. By default, UrlFetchApp will throw an error and halt execution on non-200 HTTP responses. Setting this to true allows our code to receive the error response, check the status code, and throw our own, more descriptive error, which is then caught by the onFormSubmit function’s catch block. This gives us full control over the error handling flow.

Conclusion: Beyond Validation

We’ve journeyed from a simple Google Form to a sophisticated, AI-powered gatekeeper for data quality. But the true power of this architecture isn’t just about rejecting bad data—it’s about fundamentally changing how you interact with information at its point of entry. What we’ve built is more than a validator; it’s a blueprint for intelligent automation within the Automated Discount Code Management System ecosystem.

Recap: The Power of AI-Driven Data Quality

Let’s take a step back. Traditional form validation is rigid. It checks for patterns (@ in an email) or presence (a required field). It’s effective but lacks understanding. By integrating Vertex AI via Apps Script, we’ve transcended these limitations.

  • **From Syntactic to Semantic: We’ve moved beyond checking if the data looks right to understanding if the data is right in its given context. This is the leap from syntax to semantics, and it’s a game-changer for data integrity.

  • The “Intelligence Layer”: Apps Script acted as the crucial orchestrator, the “glue” connecting the user-friendly interface of Google Forms with the immense analytical power of Vertex AI. This creates an invisible but powerful intelligence layer for your everyday tools.

  • Proactive, Not Reactive: This solution tackles the “garbage in, garbage out” problem at the source. Instead of cleaning messy data later, we ensure high-quality, contextually-aware data is captured from the very first submission, saving countless hours of downstream data wrangling.

Expanding the Solution for Other Use Cases

The form validation example is just the beginning. The pattern you’ve learned—Interface (Forms) -> Orchestrator (Apps Script) -> Intelligence (Vertex AI)—is a remarkably flexible and reusable framework. Consider these possibilities:

  • Automated Content Categorization: Imagine a form for submitting internal blog ideas. Vertex AI could analyze the submitted abstract and automatically assign categories like “Engineering,” “Marketing,” or “Product Update,” populating a spreadsheet column without any manual intervention.

  • Sentiment Analysis & Routing: A customer feedback form could use Vertex AI to perform sentiment analysis on open-ended comments. Positive feedback could be logged for testimonials, while negative feedback could automatically trigger a high-priority ticket in a project management tool.

  • Entity Extraction for Structured Data: A project intake form could have a single field for “Project Description.” The AI could parse this text to extract key entities like Project Name, Client, Due Date, and Key Deliverables, and then neatly populate them into separate columns in your Google Sheet.

  • Real-Time Translation & Summarization: For global teams, a form could accept input in any language. Apps Script could send the text to Vertex AI to be translated into a common language (e.g., English) and even generate a one-sentence summary before it’s stored, making the data immediately accessible to everyone.

This architecture effectively turns your simple Google Form into an intelligent front-end for any number of complex workflows.

Call to Action: Streamline Your Workflow with ContentDrive app

Building custom solutions with Apps Script is incredibly powerful, but it requires development time and ongoing maintenance. If the idea of infusing your Automated Email Journey with Google Sheets and Google Analytics with AI excites you, but you want to accelerate the process, you’re ready for the next step.

The principles we’ve discussed—automating categorization, extracting information, and creating intelligent workflows—are at the core of what we’ve built with the ContentDrive app. It’s a no-code solution that connects directly to your Google Drive, using AI to automatically process, tag, and organize your files and the information within them.

If you’re ready to move beyond custom scripts and implement powerful, production-ready AI automation across your entire Automated Google Slides Generation with Text Replacement, take a look at what you can achieve.

**➡️ Explore the ContentDrive app and supercharge your Google Drive today!**Whether you choose to build your own custom solution or leverage a ready-made platform, the key takeaway is this: the barrier to entry for powerful AI integration is lower than ever. The tools are at your fingertips, waiting to turn manual processes into intelligent, automated workflows. Now is the time to start transforming your data from a passive asset into an active, intelligent partner in your daily work.


Tags

Google FormsVertex AIApps ScriptData ValidationAutomationGoogle WorkspaceAI

Share


Previous Article
From Talk to Task Automating Google Meet Action Items with Gemini
Vo Tu Duc

Vo Tu Duc

A Google Developer Expert, Google Cloud Innovator

Stop Doing Manual Work. Scale with AI.

Hi, I'm Vo Tu Duc (Danny), a recognised Google Developer Expert (GDE). I architect custom AI agents and Google Workspace solutions that help businesses eliminate chaos and save thousands of hours.

Want to turn these blog concepts into production-ready reality for your team?
Book a Discovery Call

Table Of Contents

Portfolios

AI Agentic Workflows
Cloud Engineering
AppSheet Solutions
Change Management
Strategy Playbooks
Product Showcase
Uncategorized
Workspace Automation

Related Posts

Automate Site Defect Punch Lists with Gemini and Google Chat
May 22, 2026
© 2026, All Rights Reserved.
Powered By

Quick Links

Book a CallAbout MeVolunteer Legacy

Social Media