| Frontend | HTML form on your site (already exists) |
| Backend | Google Apps Script (free, serverless) |
| Storage | Google Sheet — every submission becomes a new row |
| Notification | Email to your inbox when someone submits |
| Cost | ₹0 — Google's free tier handles thousands of submissions/month |
| Setup time | ~15 minutes |
If you've ever needed a contact form, a "Become a Partner" form, an early-access waitlist, or any form on a static site, you've probably hit the same wall: the form has nowhere to go. You either pay for Formspree / Netlify Forms / Basin (~$10/month after a tiny free tier), or roll your own backend.
Google Apps Script gives you a third option that's completely free, serverless, and doesn't require an account anywhere except Google: write a small function that receives your form's POST request, appends a row to a Google Sheet, and emails you a notification. It works for thousands of submissions a month before you ever hit a quota.
This guide walks through the exact setup we use on shrinkto.com/become-a-partner, end to end. Copy the code, paste, deploy, and you're done.
Step 1: Create the Google Sheet
This sheet will store every form submission as a new row.
-
Open
sheets.newin your browser That URL creates a fresh Google Sheet instantly. (You can also go to drive.google.com and create one manually.) -
Rename the sheet
Click "Untitled spreadsheet" at the top and give it a meaningful name like
ShrinkTo Partner Inquiries. This name is just for your own organisation. -
Add column headers in row 1
Type each column header into row 1, one per cell. For the partner form, use these headers in this order:
You can adjust headers later, but adding them now makes the sheet readable from day one.Timestamp · Name · Email · Company · Website · Partnership Type · Message · Source · User Agent - Bold the header row Select row 1, click the Bold button (or press Ctrl+B / Cmd+B). Optional but worth doing — the bold row visually separates headers from data.
Step 2: Open the Apps Script editor
Apps Script lives inside Google Sheets — you don't install anything separately.
- From your sheet, click the Extensions menu At the top of the sheet, click Extensions, then click Apps Script. A new tab opens with the script editor.
-
Rename the project
In the new tab, click "Untitled project" at the top and rename it to something like
ShrinkTo Partner Form Handler. -
Delete the default code
You'll see a default
function myFunction() {}— select all the code in the editor and delete it.
Step 3: Paste the doPost handler
Copy the entire code block below into the empty Apps Script editor. Replace YOUR_EMAIL@example.com with the email where you want notifications to land.
// ============================================
// ShrinkTo Partner Form Handler
// Receives form submissions, appends to sheet, sends email.
// ============================================
const NOTIFICATION_EMAIL = 'YOUR_EMAIL@example.com'; // <-- change this
const SITE_NAME = 'ShrinkTo';
function doPost(e) {
try {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const data = e.parameter; // form-urlencoded fields
const timestamp = new Date();
// Append a row matching your column headers
sheet.appendRow([
timestamp,
data.name || '',
data.email || '',
data.company || '',
data.website || '',
data.partnershipType || '',
data.message || '',
data.source || '',
data.userAgent || ''
]);
// Send email notification to you
const subject = 'New ' + SITE_NAME + ' partner inquiry: ' + (data.name || 'Unknown');
const htmlBody = '<h2>New partner inquiry</h2>' +
'<p><strong>Name:</strong> ' + escapeHtml(data.name) + '</p>' +
'<p><strong>Email:</strong> <a href="mailto:' + escapeHtml(data.email) + '">' + escapeHtml(data.email) + '</a></p>' +
'<p><strong>Company:</strong> ' + escapeHtml(data.company || '-') + '</p>' +
'<p><strong>Website:</strong> ' + escapeHtml(data.website || '-') + '</p>' +
'<p><strong>Type:</strong> ' + escapeHtml(data.partnershipType || '-') + '</p>' +
'<p><strong>Message:</strong></p><p>' + escapeHtml(data.message || '-').replace(/\n/g, '<br>') + '</p>' +
'<hr><small>Source: ' + escapeHtml(data.source || '-') + '</small>';
MailApp.sendEmail({
to: NOTIFICATION_EMAIL,
replyTo: data.email || NOTIFICATION_EMAIL,
subject: subject,
htmlBody: htmlBody
});
return ContentService
.createTextOutput(JSON.stringify({ success: true }))
.setMimeType(ContentService.MimeType.JSON);
} catch (err) {
Logger.log(err);
return ContentService
.createTextOutput(JSON.stringify({ success: false, error: err.toString() }))
.setMimeType(ContentService.MimeType.JSON);
}
}
function doGet(e) {
return ContentService
.createTextOutput(JSON.stringify({ status: SITE_NAME + ' partner endpoint active' }))
.setMimeType(ContentService.MimeType.JSON);
}
function escapeHtml(s) {
if (!s) return '';
return String(s)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
Save the script: Ctrl+S / Cmd+S, or click the floppy-disk icon. The first save will prompt you to confirm the project name — just click OK.
Step 4: Deploy as a Web App
This is the step that gives you a public URL your form can POST to.
- Click "Deploy" → "New deployment" Top-right of the Apps Script editor, click the blue Deploy button, then New deployment.
- Click the gear icon next to "Select type" From the dropdown that appears, choose Web app.
-
Configure the deployment
- Description: "ShrinkTo partner form v1" (any label works)
- Execute as: Me (your email) — this runs the script with your Google account permissions, which is what lets it write to your sheet and send email from your address
- Who has access: Anyone — required so your website can call this URL without authentication
- Click Deploy The first time, Google will pop up an authorization window. Click Authorize access, choose your account, and click Allow. You'll see a "Google hasn't verified this app" warning — click "Advanced" then "Go to (your project name) (unsafe)". This warning appears because the app is yours and unverified by Google's review process; it's safe because you wrote the code.
-
Copy the Web App URL
After deployment, you'll see a URL that looks like
https://script.google.com/macros/s/AKfy.../exec. Copy this entire URL — you'll paste it into your website next.
Step 5: Connect your website form
Open the page that has your form (in this case, become-a-partner.html). Find the line that looks like this:
const APPS_SCRIPT_URL = 'PASTE_YOUR_GOOGLE_APPS_SCRIPT_DEPLOYMENT_URL_HERE';
Replace the placeholder with the URL you copied from Apps Script:
const APPS_SCRIPT_URL = 'https://script.google.com/macros/s/AKfy.../exec';
Save the file and re-deploy your site. That's it on the website side.
Step 6: Test end-to-end
This is the most satisfying part — proving the whole thing works.
-
Open your live partner page
Visit
shrinkto.com/become-a-partnerin a real browser (not localhost — the form needs to call the deployed Apps Script URL). - Fill out the form with test data Use your real email so you can verify the notification arrives. The other fields can be anything.
- Click submit You should see a "Sending…" state for 1–3 seconds, then a green success message.
- Check your Google Sheet Open the sheet — you should see a new row at the bottom with the test data and a current timestamp.
- Check your inbox You should have an email from your own Gmail address (Apps Script sends as the deploying user) with the partner inquiry details. The "Reply To" header is set to the form submitter's email so you can reply directly.
Common issues and how to fix them
"The form backend is not configured yet" message
You forgot to replace the APPS_SCRIPT_URL placeholder in your HTML. Open the page source, search for PASTE_, and replace with your actual deployment URL.
"Script function not found: doPost"
You deployed before saving, or you saved a different file. Go back to the Apps Script editor, make sure the code is in Code.gs (not a new file), save, then re-deploy as a new version (not a new deployment — re-use the same one).
Submissions go to sheet but no email arrives
Check the spam folder first. If still nothing, open Apps Script, click Executions in the left sidebar, find the latest run, and check the logs for errors. The most common cause is hitting Gmail's daily quota (100 emails for free Gmail accounts, 1500 for Workspace) — usually only an issue at scale.
"Authorization required" error in browser console
You deployed with "Who has access: Only myself" instead of "Anyone". Open your deployment, click the pencil/edit icon, change the access setting, and re-deploy as a new version.
Submissions stop working after editing the script
Editing the code requires creating a new deployment version for the changes to go live. In the Deploy menu, click Manage deployments, click the pencil icon, change "Version" to "New version", and click Deploy. The URL stays the same.
Customising for your form
The script above is generic. Two common customisations:
Different form fields
Match the order of sheet.appendRow([...]) in the script to your sheet's column headers. If you add a field called budget in your form, add a corresponding header in the sheet, then add data.budget || '' to the appendRow array in the matching position.
Send a confirmation email to the submitter too
Add a second MailApp.sendEmail call after the first one:
MailApp.sendEmail({
to: data.email,
subject: 'Thanks for reaching out to ' + SITE_NAME,
htmlBody: '<p>Hi ' + escapeHtml(data.name) + ',</p>' +
'<p>Thanks for your interest in partnering with us. ' +
'We\'ll get back to you within 2–3 business days.</p>' +
'<p>— ' + SITE_NAME + ' team</p>'
});
Free tier limits to know about
Google Apps Script's free quotas are generous for typical website forms:
- Email: 100/day for free Gmail accounts, 1,500/day for Google Workspace
- Script runtime: 6 minutes per execution (way more than a form needs)
- Daily script triggers: 90 minutes total per day (each form submission = ~1 second)
- Sheet writes: Effectively unlimited for normal use
For most small sites and indie projects, you'll never come close to these limits. If you do, you've grown to the point where a real backend (Supabase, Firebase, etc.) makes sense anyway.
Security considerations
The deployed Apps Script URL is public — anyone with the URL can POST to it. Three things to be aware of:
- Spam. Bots will eventually find the endpoint. Add a hidden honeypot field (a form input that's invisible via CSS) and reject submissions where it's filled in. Or add a Cloudflare Turnstile token.
- Rate limiting. Apps Script doesn't have built-in rate limits. If spam becomes an issue, add a check at the start of
doPostusingPropertiesServiceto track submissions per IP per hour. - PII storage. Anything you collect goes into your Google Sheet. Don't ask for sensitive data (passwords, financial info) in a form like this — use a proper authenticated backend for that.
You just built a serverless form backend
This same pattern works for any form — newsletter signups, beta waitlists, feedback forms, RSVP pages. Reuse the script with different sheets and different field mappings.
handshake See it live: ShrinkTo's partner page →