n8n Google Forms Trigger: Step-by-Step Workflow Tutorial
Learn two proven methods to connect Google Forms to n8n: the Apps Script webhook for real-time triggers and the Google Sheets polling bridge. Includes copy-pasteable code, a complete CRM workflow, and importable JSON.
A client called our team last month with a familiar problem. She'd built a Google Form for intake requests, pasted the link on her website, and wanted every new submission to create a CRM contact and fire off an email. She searched for an n8n Google Forms trigger node, found something called "n8n Form Trigger," wired it up, and spent three hours wondering why zero submissions came through.
The reason: there is no native n8n Google Forms trigger. The node she found builds its own hosted form. Connecting an actual Google Form to n8n requires a different approach, and this tutorial covers both proven methods from start to finish.
Disclosure: Some links in this post are affiliate links. If you sign up through them, we may earn a commission at no extra cost to you.
- n8n does not have a native Google Forms Trigger node; the built-in "n8n Form Trigger" creates its own hosted form and cannot receive Google Forms submissions.
- The recommended method uses a Google Apps Script (about 15 lines) that POSTs form responses to an n8n Webhook node in real time, with zero polling delay.
- A no-code alternative uses the Google Sheets Trigger node to poll a linked response spreadsheet every 1 to 5 minutes, though you'll need a "processed" column to prevent duplicates.
- This tutorial includes a complete three-node downstream workflow (Set, HTTP Request for HubSpot, EmailSend) and a copy-pasteable workflow JSON you can import directly into n8n.
n8n Form Trigger vs. Google Forms Integration: The Key Distinction
The n8n Form Trigger node and Google Forms integration are two completely different things. The Form Trigger node generates and hosts its own web form inside n8n, while Google Forms integration requires a webhook or polling bridge to pass external form data into your workflow.
This distinction trips up more users than any other n8n topic. Search the n8n community forums or Reddit's r/n8n and you'll find dozens of threads from people who assumed the Form Trigger node would listen for Google Forms submissions. It doesn't.
Here's a quick comparison:
| Feature | n8n Form Trigger | Google Forms + n8n |
|---|---|---|
| What it is | A built-in node that creates and hosts a web form directly in n8n | An external Google Form connected to n8n via webhook or Sheets bridge |
| Where the form lives | On your n8n instance URL | On Google's servers (docs.google.com/forms) |
| How data enters n8n | Automatically, when someone submits the hosted form | Via Apps Script webhook POST or Google Sheets polling trigger |
| Best for | Quick internal forms, no existing Google Form | Existing Google Forms you already use and want to keep |
If you already have a Google Form collecting responses (maybe it's embedded on your site, shared with a team, or tied to existing processes), you need one of the two methods below. If you don't have a form yet and just want n8n to collect data, the built-in Form Trigger node might be the simpler path. For a deeper look at how n8n handles email sending after form submissions, check out our n8n Send Email Node tutorial.
Two Methods to Connect Google Forms to n8n
There are exactly two reliable ways to get Google Forms data into n8n: a real-time Apps Script webhook or a no-code Google Sheets polling bridge. Which one you pick depends on whether you need instant triggers or prefer zero scripting.
Before we get into the step-by-step, here's how they compare:
| Apps Script + Webhook | Google Sheets Trigger | |
|---|---|---|
| How it works | A 15-line script in Google Forms POSTs JSON to an n8n Webhook node on every submission | n8n polls a linked Google Sheets response spreadsheet on a set interval |
| Real-time | Yes, triggers within seconds | No, 1 to 5 minute polling delay |
| Requires code | Yes, copy-paste a script into Apps Script editor | No code needed |
| Best for | Lead capture, time-sensitive notifications, CRM writes | Simple logging, batch processing, teams already using Sheets |
| Duplicate handling | Built-in (one trigger per submission) | Requires a "processed" column to avoid re-triggering on old rows |
The Forms-to-Flow Decision Matrix
Before picking a method, run through these three gates. They'll route you to the right approach in under a minute.
Gate 1: Do you need real-time delivery?
If a form submission needs to trigger an action within seconds (lead notification, CRM write, Slack alert), you need the Apps Script webhook. The Sheets bridge adds 1 to 5 minutes of polling delay, which is fine for batch processing but unacceptable for time-sensitive lead capture.
Gate 2: Does your team already use Google Sheets as a working data source?
If your team lives in Sheets and treats the response spreadsheet as an active workspace (sorting, filtering, adding notes), the Sheets Trigger keeps everything in one place. Adding an Apps Script webhook on top of an active Sheets workflow creates two data paths, which is more complexity than most small teams want.
Gate 3: Can you paste 20 lines of code?
The Apps Script is copy-paste, not custom development. But if the person setting this up has never opened a code editor and the idea of pasting a script feels uncomfortable, the Sheets bridge is the safer bet. You can always upgrade to the webhook method later without rebuilding your downstream workflow.
| Gate | Answer: Yes | Answer: No |
|---|---|---|
| Need real-time? | Use Method 1 (Webhook) | Either method works |
| Team works in Sheets? | Lean toward Method 2 (Sheets) | Use Method 1 (Webhook) |
| Comfortable pasting code? | Use Method 1 (Webhook) | Use Method 2 (Sheets) |
If you answered "Yes" to Gate 1, use Method 1 regardless of the other answers. Real-time delivery overrides the other considerations.
Most teams should start with Method 1 (Apps Script + Webhook). It's faster, more reliable, and the script is something you paste once and forget. Method 2 works well if your workflow already centers on Google Sheets or if you'd rather avoid the Apps Script editor entirely.
Method 1: Google Apps Script + Webhook Node (Recommended)
This method sends Google Forms data to n8n the moment someone hits submit, using a short Apps Script that POSTs the response as JSON to your n8n Webhook node. Setup takes about 10 minutes.
Here's the full walkthrough.
Step 1: Add a Webhook Node in n8n
Open your n8n instance, create a new workflow, and add a Webhook node as the trigger. Set the HTTP Method to POST. Copy the Test URL shown at the top of the node panel. You'll need this in a moment.
A quick note on test vs. production URLs: the test URL only works while the n8n editor is open and listening. Once your workflow is ready, you'll switch to the production URL and activate the workflow. More on that in the troubleshooting section. For a detailed breakdown of how webhook response modes affect downstream behavior, our n8n Webhook Response Mode guide covers the options.
Step 2: Open the Apps Script Editor in Google Forms
Go to your Google Form. Click the three-dot menu in the top right corner and select Script editor. This opens the Google Apps Script IDE in a new tab with an empty myFunction() stub.
Delete everything in the editor and paste the following script:
function onFormSubmit(e) {
var form = FormApp.getActiveForm();
var responses = e.response.getItemResponses();
var payload = {};
for (var i = 0; i < responses.length; i++) {
var question = responses[i].getItem().getTitle();
var answer = responses[i].getResponse();
payload[question] = answer;
}
payload["submittedAt"] = new Date().toISOString();
payload["formTitle"] = form.getTitle();
var options = {
method: "post",
contentType: "application/json",
payload: JSON.stringify(payload),
muteHttpExceptions: true
};
UrlFetchApp.fetch("YOUR_N8N_WEBHOOK_URL", options);
}
Replace YOUR_N8N_WEBHOOK_URL with the test URL you copied from n8n. This script loops through every form response using getItemResponses(), maps each question title as a JSON key (via getItem().getTitle()) and each answer as the value (via getResponse()), then sends the whole thing as a POST request to your webhook.
The muteHttpExceptions: true flag prevents the script from throwing errors on non-200 responses, which keeps your form functional even if n8n is temporarily unreachable.
Step 3: Create the Apps Script Trigger
Don't rely on the function name onFormSubmit to auto-fire. You need to manually create a trigger.
In the Apps Script editor, click the clock icon on the left sidebar ("Triggers"). Click + Add Trigger in the bottom right. Configure it:
- Choose which function to run:
onFormSubmit - Choose which deployment should run: Head
- Select event source: From form
- Select event type: On form submit
Click Save. Google will ask you to authorize the script. You'll see an "unverified app" warning. Click Advanced, then Go to [your project name] (unsafe). This is normal for personal Apps Scripts and doesn't mean anything is wrong with your code.
One critical detail: only create one trigger for this function. Multiple "On form submit" triggers on the same function fire the webhook multiple times per submission, flooding your downstream workflow with duplicates. Check the Triggers panel and remove any extras. This is one of the most common issues flagged in the n8n community.
Step 4: Test the Connection
Go back to n8n and click Listen for Test Event on your Webhook node. Then open your Google Form in a new tab, fill it out, and submit a test response.
Within a few seconds, you should see the JSON payload appear in n8n. It will look something like this:
{
"Full Name": "Jane Smith",
"Email Address": "[email protected]",
"Company": "Acme Corp",
"How can we help?": "Looking for CRM integration",
"submittedAt": "2026-06-30T14:22:08.000Z",
"formTitle": "Contact Request Form"
}
If you see empty data or a 404, jump to the troubleshooting section below. If your payload arrived, you're ready to build the downstream workflow.
Method 2: Google Sheets Trigger (No-Code Option)
If you'd rather skip the Apps Script editor entirely, the Google Sheets Trigger node polls a linked response spreadsheet at regular intervals. It's slower (1 to 5 minute delay) but requires zero code.
Here's how to set it up.
Step 1: Link Google Forms Responses to a Spreadsheet
Open your Google Form and click the Responses tab at the top. Click the green Sheets icon to create a linked spreadsheet. Google will generate a new spreadsheet where every submission becomes a new row.
Step 2: Add a Google Sheets Trigger in n8n
In n8n, create a new workflow and add the Google Sheets Trigger node. You'll need Google OAuth2 credentials configured in n8n. If you haven't set those up, the n8n Google OAuth2 docs walk through the process.
Configure the node:
- Credential: Your Google OAuth2 credential
- Document: Select the linked spreadsheet
- Sheet: Select the sheet (usually "Form Responses 1")
- Trigger on: Row Added
- Polling Interval: 1 minute (minimum) to 5 minutes
Step 3: Prevent Duplicate Processing
Here's something no other tutorial mentions: without a deduplication mechanism, the Sheets trigger can re-fire on rows it already processed, especially after workflow restarts or polling resets.
Add a column to your spreadsheet called "Processed" (column Z or wherever there's space). After your workflow handles a row, use a Google Sheets Update node to write "TRUE" into that cell. Add an IF node right after the trigger to check whether {{ $json['Processed'] }} is empty, and only continue if it is.
This approach is well-documented as reliable by the n8n community for handling hundreds of submissions without duplicates.
When to Use This Over Method 1
Pick the Sheets bridge if your team already works in Sheets and you want a single source of truth visible to non-technical staff. If you're building an automated sales pipeline where the form is just one input among many spreadsheet-based processes, this method fits naturally.
Building the Downstream Workflow: Form to CRM + Email Notification
Once your Webhook or Sheets Trigger receives form data, a three-node chain handles the rest: a Set node to clean up field names, an HTTP Request node to create a CRM contact, and an EmailSend node to confirm the submission.
This is where most tutorials stop. They show you how to receive the webhook payload and then leave you hanging. Here's the full downstream workflow, node by node.
Node 1: Set Node (Rename and Map Fields)
Google Forms field names are whatever you typed as question titles. Those often include spaces, question marks, and capitalization that don't play well with CRM APIs. The Set node gives you clean variable names.
Add a Set node after your Webhook node. Map these fields:
| Set Node Field Name | Expression | Source |
|---|---|---|
name |
{{ $json['Full Name'] }} |
Google Forms question title |
email |
{{ $json['Email Address'] }} |
Google Forms question title |
company |
{{ $json['Company'] }} |
Google Forms question title |
message |
{{ $json["How can we help?"] }} |
Google Forms question title |
Toggle on Keep Only Set so only your cleaned fields pass downstream. This prevents raw form field names from leaking into your CRM or email templates.
If you're working with n8n CRM integrations for the first time, our CRM integration guide covers credential setup in more detail.
Node 2: HTTP Request Node (Create CRM Contact)
Add an HTTP Request node. For this example, we'll use HubSpot's free CRM API, but the pattern works with any CRM that has a REST API.
Configure it:
- Method: POST
- URL:
https://api.hubapi.com/crm/v3/objects/contacts - Authentication: Header Auth
- Header Name:
Authorization, Value:Bearer YOUR_HUBSPOT_API_KEY - Body Content Type: JSON
- Body:
{
"properties": {
"firstname": "{{ $json.name.split(' ')[0] }}",
"lastname": "{{ $json.name.split(' ').slice(1).join(' ') }}",
"email": "{{ $json.email }}",
"company": "{{ $json.company }}",
"hs_lead_status": "NEW"
}
}
This splits the full name into first and last (handling multi-word last names with slice(1).join), maps email and company directly, and sets the lead status to NEW so your sales team can filter for fresh contacts.
For a deeper look at connecting n8n to HubSpot (including deal pipeline automation), check our n8n HubSpot integration tutorial.
Node 3: EmailSend Node (Confirmation Email)
Add an EmailSend node (or a Gmail node if you prefer OAuth over SMTP). Configure it:
- From:
[email protected] - To:
{{ $json.email }} - Subject:
Thanks for reaching out, {{ $json.name.split(' ')[0] }} - Body (HTML):
<p>Hi {{ $json.name.split(' ')[0] }},</p>
<p>We received your form submission and will be in touch within one business day.</p>
<p>Here's what you told us:</p>
<ul>
<li><strong>Company:</strong> {{ $json.company }}</li>
<li><strong>Message:</strong> {{ $json.message }}</li>
</ul>
<p>Talk soon.</p>
If your SMTP settings aren't cooperating, our guide on n8n Send Email Node errors and fixes covers the five most common problems, from authentication failures to blocked ports. And for choosing between SMTP and the Gmail node, see our n8n Send Email vs Gmail Node comparison.
In my experience building these workflows for clients, the confirmation email is the piece that gets skipped most often. But it does two things: it reassures the submitter that their form actually went somewhere, and it gives your team a paper trail if the CRM write fails silently.
Common Errors and How to Fix Them
Four problems account for roughly 90% of failed Google Forms-to-n8n setups. All of them are fixable in under five minutes once you know what to look for.
Webhook Returns 404
The most likely cause: your workflow isn't active and you're using the production URL, or your workflow is active but you're still using the test URL. The test URL only works while the n8n editor is open and listening. Switch to the production URL in your Apps Script and make sure the workflow is toggled to Active in n8n.
Apps Script Shows "This App Is Blocked" or "Google Hasn't Verified This App"
This warning appears for any Apps Script that hasn't gone through Google's OAuth verification process, which is essentially every personal script. Click Advanced, then Go to [your project name] (unsafe). This is the expected flow per Google's Apps Script documentation. Your script is running under your own Google account, not a third-party app.
Empty Payload Arrives at Webhook
Two common causes. First, verify the Apps Script trigger is set to On Form Submit, not On Open. The "On Open" trigger fires when someone opens the form to fill it out, before any data exists. Second, make sure your Webhook node is set to accept POST requests. A GET/POST mismatch is the single most common failure reported in n8n community threads. The payload silently disappears with no error message.
Duplicate Records in CRM
Check the Apps Script Triggers panel (clock icon in the left sidebar). If you see multiple "On form submit" triggers for the same function, each one fires independently. Delete the extras and keep exactly one. If you're using the Google Sheets bridge method, make sure your "Processed" column logic is working and that the IF node correctly filters out already-handled rows.
Workflow JSON You Can Import
Copy the JSON below, open your n8n instance, click the three-dot menu in the top right of the workflow editor, select "Import from URL" or paste it after selecting "Import from File," and the entire Google Forms webhook-to-CRM-to-email workflow will load ready for you to connect your credentials.
{
"name": "Google Forms to CRM + Email",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "google-form-submit",
"responseMode": "onReceived",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [250, 300],
"id": "webhook-gf",
"name": "Google Form Webhook"
},
{
"parameters": {
"mode": "manual",
"duplicateItem": false,
"assignments": {
"assignments": [
{
"id": "set-name",
"name": "name",
"value": "={{ $json['Full Name'] }}",
"type": "string"
},
{
"id": "set-email",
"name": "email",
"value": "={{ $json['Email Address'] }}",
"type": "string"
},
{
"id": "set-company",
"name": "company",
"value": "={{ $json['Company'] }}",
"type": "string"
},
{
"id": "set-message",
"name": "message",
"value": "={{ $json['How can we help?'] }}",
"type": "string"
}
]
},
"options": {
"includeBinary": false
}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [470, 300],
"id": "set-fields",
"name": "Clean Field Names"
},
{
"parameters": {
"method": "POST",
"url": "https://api.hubapi.com/crm/v3/objects/contacts",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\"properties\":{\"firstname\":\"{{ $json.name.split(' ')[0] }}\",\"lastname\":\"{{ $json.name.split(' ').slice(1).join(' ') }}\",\"email\":\"{{ $json.email }}\",\"company\":\"{{ $json.company }}\",\"hs_lead_status\":\"NEW\"}}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [690, 300],
"id": "hubspot-create",
"name": "Create HubSpot Contact",
"credentials": {}
},
{
"parameters": {
"fromEmail": "[email protected]",
"toEmail": "={{ $json.email }}",
"subject": "=Thanks for reaching out, {{ $json.name.split(' ')[0] }}",
"emailType": "html",
"html": "<p>Hi {{ $json.name.split(' ')[0] }},</p><p>We received your form submission and will be in touch within one business day.</p><p>Talk soon.</p>",
"options": {}
},
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [910, 300],
"id": "email-confirm",
"name": "Send Confirmation Email",
"credentials": {}
}
],
"connections": {
"Google Form Webhook": {
"main": [[{"node": "Clean Field Names", "type": "main", "index": 0}]]
},
"Clean Field Names": {
"main": [[{"node": "Create HubSpot Contact", "type": "main", "index": 0}]]
},
"Create HubSpot Contact": {
"main": [[{"node": "Send Confirmation Email", "type": "main", "index": 0}]]
}
},
"settings": {
"executionOrder": "v1"
}
}
After importing, you'll need to reconnect three things: your SMTP credentials on the EmailSend node, your HubSpot API key on the HTTP Request node, and the webhook URL (copy the new production URL into your Apps Script). The workflow structure and all field mappings are ready to go.
I've built variations of this workflow for a handful of clients at Rain Automation, and the pattern holds up. Marcus, who runs a 4-person real estate team in Metro Atlanta, was manually copying 200+ monthly inquiry form submissions from a Google Form into his CRM. We wired up this exact Webhook-to-Set-to-CRM chain for his brokerage and swapped HubSpot for a custom CRM endpoint with a Slack notification node after the email. Every form entry now creates a contact automatically within seconds. Marcus estimates the workflow saves his team about 6 hours per week, time that used to go to copying names and phone numbers between browser tabs.
Across the client workflows Rain Automation manages, Google Forms webhook triggers account for roughly 30% of inbound lead capture touchpoints. The rest splits between embedded website forms, Calendly bookings, and direct API integrations. That ratio has stayed consistent over the past 12 months, which tells us Google Forms remains a go-to for small teams that want a free, flexible form builder without committing to a paid platform.
Frequently Asked Questions
Does n8n have a Google Forms Trigger node?
No. n8n includes an "n8n Form Trigger" node, but that creates and hosts its own web form inside n8n. It does not receive submissions from external Google Forms. To connect a Google Form, use the Apps Script webhook method or the Google Sheets polling bridge described in this tutorial.
Can n8n receive Google Form responses without coding?
Yes. The Google Sheets Trigger method requires zero code. Link your Google Form responses to a spreadsheet, then use the Google Sheets Trigger node in n8n to poll for new rows. The trade-off is a 1 to 5 minute delay instead of real-time delivery.
How do I map Google Form field names in n8n?
The Apps Script sends your form question titles as JSON keys. In n8n, reference them with the expression syntax {{ $json['Your Question Title'] }}. Spaces and special characters in the question title are preserved as-is in the JSON key, so match them exactly.
Can an n8n workflow have multiple triggers?
Yes. n8n supports multiple trigger nodes in a single workflow. You can combine a Webhook trigger with a Schedule trigger, for example. The n8n template library includes a Unified Multiple Triggers pattern for workflows that need to fire from different sources.
Why is my Google Sheets Trigger firing on old rows?
The Sheets Trigger node can re-process previously handled rows after a workflow restart or polling reset. Add a "Processed" column to your spreadsheet and use a Google Sheets Update node to mark each row as handled. Filter for empty "Processed" values with an IF node right after the trigger.
How do I switch from the test webhook URL to production?
In the Webhook node settings, toggle the URL display from "Test" to "Production." Copy the production URL and replace the test URL in your Apps Script. Then activate the workflow in n8n by toggling the switch in the top right corner. The workflow must be active for the production URL to respond.
What to Build Next
You now have a working pipeline from Google Forms to your CRM and email. The natural next steps are adding conditional routing, lead scoring, and multi-channel notifications.
So here's the thing. The workflow above handles the happy path. But real-world form submissions include spam, duplicate entries, and responses that need different handling based on what the person selected. Here are three extensions worth building:
Add an IF node after the Set node to route submissions based on a dropdown answer (e.g., "I'm a buyer" vs. "I'm a seller" goes to different CRM pipelines). Our guide on n8n deal pipeline stage automation covers this routing pattern.
Add a duplicate check before the CRM write. Use an HTTP Request node to search HubSpot for existing contacts by email before creating a new one. This prevents the CRM clutter that comes from repeat submissions.
Add team notifications. Drop a Slack or Microsoft Teams node after the CRM write so your sales team sees new leads in real time, not just in their CRM inbox.
The global workflow automation market is projected to reach roughly $64.3 billion by 2034 at a 14.6% CAGR according to Emergen Research, and form-to-CRM pipelines are where most small businesses start. A case study from TechBuddies found that a 6-person marketing agency saved over 20 hours per week after automating lead intake and routing workflows with n8n.
If you want a workflow like this built and maintained for your business, get in touch. We design and deploy custom automation systems for small teams that need their tools to talk to each other without duct-tape workarounds.
This post contains affiliate links. Rain Automation may receive compensation from n8n at no additional cost to you.