Over the last two weeks I built a complete lead-handling setup out of four small pieces, each one a post of its own. This is the map of the whole thing: what each piece does, how they fit, and what the community caught along the way.
Everything runs on one Google Apps Script attached to one Google Sheet. No Zapier, no CRM, no monthly fee.
The four pieces
1. Collect the leads. A free Apps Script webhook receives form posts and appends them to the sheet. A shared token stops drive-by scripts, and the post is honest about what that alone does not stop. Collect leads in a Google Sheet with a free Apps Script webhook (no Zapier)
2. Answer instantly. Every new row gets a branded auto-reply from your own Gmail within minutes. Auto-reply to new leads from a Google Sheet with Apps Script
3. Follow up once. If the lead has not replied after a delay you set (default 48 hours), one polite follow-up goes out. If they wrote back in the meantime, the script notices and stays quiet. One polite follow-up when a lead goes quiet (Apps Script)
4. Keep the bots out. A honeypot field plus a time trap filter form spam before it touches the sheet, with signed timestamps for the bots that read your page source. Keep bots out of your Google Sheet lead form (honeypot + time trap)
Put together: a lead lands in the sheet, gets an answer in minutes, gets one nudge two days later if they went quiet, and most junk never enters the sheet at all.
What the comments fixed
Two readers made this materially better, and the tutorials carry their fixes with credit:
- Hayrullah Kar caught formula injection:
appendRowtreats any value starting with=,+,-or@as a formula, so a "name" can become a live=HYPERLINK(...)in your sheet. Prefix those with an apostrophe. He also caughtgetActiveSheet()silently following tab order - pin the target withgetSheetByName('Leads'). - Trung Thanh Tran caught the forgeable time trap: anything stamped in the browser can be faked in the browser. Serve the timestamp from your script with an HMAC signature and an expiry bound. And if you dedupe, wrap the scan and the append in
LockService.getScriptLock()- only that pair, so concurrent submissions wait milliseconds, not for the whole request.
This is why I post the code instead of only selling it: four eyes on a small script find what two miss.
The packaged version: LeadReply Kit
If you want the pipeline without assembling it, the LeadReply Kit ($29, one time) packages it: the script, two editable email templates, a sample sheet layout, and a 10-minute setup guide.
One honest note on where it stands. The kit is mock-tested sample code - tested against a stubbed Sheet and Gmail, not your live account - so read it before pointing it at real leads. After the comment rounds I gave it a hardening pass: every send is now wrapped so one rejected address marks the row error with the reason and the queue keeps moving, instead of a single bad row silently stalling every lead behind it. The guide explains how to retry those rows, and how to tell a temporary quota stop from a dead address.
What I would add next
- A third email for the truly quiet ones - though I would stop at three.
- A ping to you (Slack or email) when a lead replies, so the warm ones do not sit.
- Double opt-in on the auto-reply, if your leads come from anywhere public.
If you build from any of this, tell me what broke. That is how the next version gets better.



