Google Sheets Email Automation for a Small Outbound Pilot
Run a small cold outbound pilot with Google Sheets email automation: columns, an Apps Script sender, reply tracking, and when to move to Smartlead.

Google Sheets email automation works for a small outbound pilot: a sheet of 50 to 100 prospects, an Apps Script that sends from one mailbox, and a column that records replies. It answers one question, whether your offer gets responses, before you pay for extra mailboxes and sequencing software. Below are the columns, the script, and the signs that it is time for Smartlead or Instantly.
Use the free lead form template
What the pilot is for
A recruiter testing a new niche, or an agency testing a new service line, usually doesn't need infrastructure yet. They need to know whether three different openers pull replies from one list of 80 people. A sheet gives you that cheaply, and every row is visible to you while it runs.
Treat it as an experiment with a stop date. Pick one offer, one audience and one sender, and run it for two weeks. If nobody replies, you saved yourself a domain purchase, mailbox setup and a month of warm-up. If people reply, you have real language to carry into a proper stack.
The sheet layout
Keep one tab called pilot. Every row is a prospect and every column is something the script reads or writes.
| Column | Who fills it | Purpose |
|---|---|---|
| You | Recipient address, verified before it goes in | |
| first_name, company | You | Merge fields for the message |
| subject, body | You or an AI draft | The final text, written per row so you can review it |
| variant | You | A, B or C, so you can compare openers later |
| status | Script | ready, sent, replied, bounced, stopped |
| thread_id | Script | Gmail thread ID for reply checks |
| sent_at | Script | Timestamp of the send |
| reply_class | You | interested, not now, wrong person, unsubscribe |
Write the subject and body into cells instead of merging inside the script. You can then read every message before it goes out, which matters when the list is small and eavh lead is worth a lot.
Step by step setup
- Use a separate sending address. Don't send a cold pilot from the mailbox you use for clients. A secondary domain with its own Google Workspace mailbox, set up with SPF, DKIM and DMARC, keeps a bad week away from your main reputation.
- Build the list and verify it. Run the addresses through any email verifier before they reach the sheet. Bounces hurt a single mailbxo faster than you expect.
- Fill the columns above. Set
statustoreadyonly on rows you have read. - Open Extensions, then Apps Script, and add the sender. The script below sends a few rows per run and records the thread.
- Add a time trigger. Run
sendReadyhourly during business hours in the prospect's time zone, and a second trigger forcheckRepliesevery few hours. - Add an opt-out line to every message. A plain sentence such as "Reply 'stop' and I won't email again" is enough for a pilot, and you must honor it by setting the row to
stopped.
function sendReady() {
const sh = SpreadsheetApp.getActive().getSheetByName('pilot');
const rows = sh.getDataRange().getValues();
const h = rows[0];
const c = name => h.indexOf(name);
let sent = 0;
for (let i = 1; i < rows.length && sent < 5; i++) {
if (rows[i][c('status')] !== 'ready') continue;
const msg = GmailApp
.createDraft(rows[i][c('email')], rows[i][c('subject')], rows[i][c('body')])
.send();
sh.getRange(i + 1, c('thread_id') + 1).setValue(msg.getThread().getId());
sh.getRange(i + 1, c('sent_at') + 1).setValue(new Date());
sh.getRange(i + 1, c('status') + 1).setValue('sent');
sent++;
}
}
function checkReplies() {
const sh = SpreadsheetApp.getActive().getSheetByName('pilot');
const rows = sh.getDataRange().getValues();
const h = rows[0];
const c = name => h.indexOf(name);
for (let i = 1; i < rows.length; i++) {
if (rows[i][c('status')] !== 'sent') continue;
const t = GmailApp.getThreadById(rows[i][c('thread_id')]);
const last = t.getMessages().pop();
if (t.getMessageCount() > 1) {
const bounce = /mailer-daemon|postmaster/i.test(last.getFrom());
sh.getRange(i + 1, c('status') + 1).setValue(bounce ? 'bounced' : 'replied');
}
}
}
The cap of 5 per run is deliberate. Apps Script has daily email quotas that differ between free Gmail and Workspace accounts, and Google changes them, so check the current quota page before you size a run. Stay well under it anyway, because deliverability will limit you before the quota does.
Capturing form answers in the same sheet
Cold email in a pilot usually ends with a link: a short form that asks the prospect what they need, or lets a candidate or client request a call. That form step is where interest turns into structured data, and in our AI Outbound Email System it sits right after the reply: a positive reply gets classified, the prospect gets the intake link, and the answers flow to the CRM. You can mirror that at pilot scale by sending the link in the follw-up and writing the answers back into the same sheet.
Put the row number in the link, for example ?row=42, and add a hidden field called row to the form. When the form is submitted, a webhook posts the answers to a small Apps Script web app that finds row 42 and sets status to form_submitted. The reply and the form answers then sit on one row.
Qwenvo is a free form builder our team builds, and it fits this step because webhooks, hidden fields and custom domains are all included at no cost. The web app side looks like this:
function doPost(e) {
const d = JSON.parse(e.postData.contents);
// Field names depend on your form; check a test submission first.
const row = Number(d.row);
const sh = SpreadsheetApp.getActive().getSheetByName('pilot');
sh.getRange(row, 1).offset(0, 12).setValue('form_submitted');
return ContentService.createTextOutput('ok');
}
Log a real test submission before you trust the field names, and read the webhooks help page for the exact payload. One honest limit: one-click Google Sheets connections are still being rolled out in Qwenvo, so today this goes through a webhook and a script. If you'd rather not write that, Zapier, Make or n8n can receive the same webhook. The form to Google Sheets guide covers the simpler case of appending every submission as a new row.
Where the pilot breaks
A sheet pilot has weak spots, and you should know them before you read the results.
- One mailbox carries everything. There is no rotation, so a bad batch affects your only sender.
- No warm-up. A new mailbox that starts sending right away lookks suspicious, so keep volume tiny in the first week.
- Reply detection is crude. Out of office messages and auto-replies count as replies until you read them, which is why
reply_classis manual. - Bounces arrive as mail. The script catches common bounce senders, but you still need to look at them.
- Nothing schedules per time zone. Triggers run on your clock, so split the list by region if timing matters.
None of these ruin a two-week test. They do make the numbers rough, so judge the pilot on whether real people answered, not on precise rates.
When to move to Smartlead or Instantly
Move when the pilot has done its job and the weak spots start costing you leads. Smartlead and Instantly are built for the parts above: connecting many mailboxes, rotating sends across them, warming accounts up, running multi-step sequences and handling replies in one inbox. Check each vendor's pricing page for current plans.
| Need | Sheet pilot | Smartlead or Instantly |
|---|---|---|
| Several sending mailboxes | One | Connect and rotate many |
| Warm-up | Manual and slow | Built in |
| Follow-up sequences | You write a second script | Native steps and delays |
| Reply handling | Manual column | Shared inbox and labels |
| Bounce handling | Read the mailer messages | Handled by the tool |
| Personalization at volume | Cells you edit by hand | Variables, often fed by enrichment |
Our rule of thumb is simple. If you got at least a few genuine positive replies, and you'd need a second mailbox or a follow-up sequence to go further, the pilot has paid for itself and the sheet is now the bottleneck. If you got nothing, change the offer before you buy tooling.
The full stack is more than a sending tool. On client projects, our AI Outbound Email System covers the domains, warm-up, Clay enrichment, AI-personalised sequences in Smartlead or Instantly, reply classification and CRM sync, and hands it all over so you own it. It exists for teams that have validated the offer and want meetings without hiring an SDR.
Questions teams ask
Can Google Sheets send cold email on its own?
Not by itself. Sheets needs Apps Script (or an add-on) to send through your Gmail or Workspace account. The script above does that, but you own the quotas, the pacing and the deliverability.
How many prospects should a pilot have?
Enough to compare two or three openers, so roughly 50 to 100 rows is a reasonable start. Smaller lists don't separate a good opener from luck, and larger ones push a sinngle new mailbox harder than it can handle.
Is it safe to send the pilot from my main domain?
We wouldn't. Use a separate domain and mailbox with SPF, DKIM and DMARC set up, so a spam complaint during the test doesn't reach your client email.
Do I need Qwenvo for this?
No. You can run the whole pilot with replies only. A form helps when you want structured answers from interested prospects, and Qwenvo is free for that. If you need payments through Stripe, note that Qwenvo uses PayPal only, so Tally or Jotform may suit you better.
Next step
Copy the column layout into a new sheet today, load 20 verified prospects, and set only five rows to ready. Run sendReady by hand once from the Apps Script editor and confirm the thread ID lands in the row before you add any trigger.
AI Outbound Email System
Third-party platform and model usage billed to you directly. No lock-in. Live in 14 days.