Blog / Product Brand Operations
Creator Outreach Tracker with Follow-Up Drafts in Google Sheets
By Keyona Meeks, Founder, ReRev Labs · Oct 31, 2026 · 9 min read
The short answer: An influencer outreach spreadsheet goes stale because someone has to update the status by hand. Fix that by letting the sheet read the email itself. Keep one row per creator with their email and a status. A small Google Apps Script finds the Gmail thread for each sent row, marks the row "Replied" when the creator answers, and writes a follow-up draft in the same thread when they have gone quiet. You read each draft and press send yourself. I mock-tested the script; I did not run it in a live Google account.
By the end of this post, you'll be able to:
- Set up a Creators sheet with the columns the script needs.
- Paste in a script that tracks replies and drafts follow-ups.
- Test it on a copy with fake rows before it touches a real inbox.
- Know where it stops working.
You send a note to forty creators. Eleven answer. Of the other twenty-nine, some never saw it and some are waiting for you to nudge them. A week later you cannot say which is which, because the sheet says "Sent" for everyone and the inbox is full.
I do not claim a spreadsheet beats a creator platform. If you run hundreds of creators with payments and content approvals, use software built for that. This post is for a small team that does outreach by email and wants a sheet that keeps itself honest.
Why does an outreach spreadsheet go out of date?
Because the status column is a copy of something that lives somewhere else. The truth is in the email thread: did they reply, and when. A person has to go look, then come back and type "Replied." That step gets skipped on a busy week.
The fix is to read the fact from the source instead of asking a person to copy it. I wrote about that idea in AI agents vs. automation: whether someone replied is not a judgment call, it is something the thread already says. The script checks the thread. No AI model decides anything.
When I searched "influencer outreach tracking spreadsheet" on October 30, 2026, page one was mostly spreadsheet templates and database templates, plus a few guides from influencer platforms. Titles only. I did not review them. A template gives you the columns. What it does not give you is a sheet that updates when the email does.
What does the sheet need?
Make a tab called Creators with these headers in row 1. The names must match exactly:
| Column | What goes in it |
|---|---|
| Name | The creator's name. The script uses the first word in the draft. |
| The address you wrote to. | |
| Status | You type To send, then Sent after you send. The script sets the rest. |
| Thread ID | Leave blank. The script fills it. |
| Follow-ups | Leave blank. The script counts them. |
| Last activity | Leave blank. The script fills it. |
| Note | The script writes a message here when something looks wrong. You can add your own. |
You can add other columns, such as platform, handle, niche or a link to their page. The script only looks for the seven above. If you also track what a creator is allowed to do with your product or content, that is a separate sheet with its own rules.
How do you send the first message?
Send it from Gmail like any other email, then set the row's Status to Sent. That is the only manual step. I kept it manual on purpose, because a first message to a stranger should be one you read.
If you send in bulk, look at the CAN-SPAM guide before you start. The FTC's CAN-SPAM compliance guide for business is the US starting point, checked October 30, 2026. The rules depend on where you and the creator live and on what the message says. Check with an attorney if you are unsure.
What does the script do?
For every row with Status Sent or Follow-up drafted, each morning:
- Finds the thread. If the Thread ID is blank, it searches your sent mail for that email address and saves the ID of the newest match.
- Checks the last message. If the newest real message is not from you, it sets Status to
Replied. - Drafts a follow-up. If the last message is yours and is at least 5 days old, it writes a draft reply in the same thread and sets Status to
Follow-up drafted. - Notices when you sent the draft. If a newer message from you appears, it sets Status back to
Sentso the wait starts again. - Stops after two. After two follow-ups and another 5 quiet days, it sets Status to
No reply.
Google's Gmail reference describes the pieces it relies on. getThreadById "Gets a thread by ID." createDraftReply "Creates a draft message replying to the sender of the last message in this thread using the reply-to address." Source: Class GmailThread and Class GmailApp, checked October 30, 2026.
How do you set it up?
- Open the sheet. Choose Extensions, then Apps Script. If this is new to you, read what Google Apps Script is first.
- Delete the placeholder code and paste the script below.
- Click Save.
- In the function list, choose
setupCreatorTriggerand click Run. Google asks you to allow the script to read your sheet and read and change your Gmail. Read the list. Approve it only if you agree.
// Creator outreach tracker: find the Gmail thread for each sent row, check for a reply,
// and draft a follow-up when a creator has gone quiet. Paste into Extensions > Apps Script.
const CONFIG = {
SHEET_NAME: 'Creators',
WAIT_DAYS: 5, // draft a follow-up after this many days of silence
MAX_FOLLOW_UPS: 2, // stop drafting after this many
FOLLOW_UP_BODY: 'Hi {name},\n\nJust checking whether you had a chance to see my note below. ' +
'Happy to answer questions, and no problem at all if it is not a fit.\n\nThanks',
MAX_ROWS: 100 // rows handled per run
};
const COLS = ['Name', 'Email', 'Status', 'Thread ID', 'Follow-ups', 'Last activity', 'Note'];
function checkCreators() {
const sheet = SpreadsheetApp.getActive().getSheetByName(CONFIG.SHEET_NAME);
const values = sheet.getDataRange().getValues();
const header = values[0];
const col = {};
COLS.forEach(function (c) {
const i = header.indexOf(c);
if (i === -1) throw new Error('Missing column: ' + c);
col[c] = i;
});
const me = Session.getEffectiveUser().getEmail().toLowerCase();
const now = new Date();
let handled = 0;
for (let r = 1; r < values.length && handled < CONFIG.MAX_ROWS; r++) {
const row = values[r];
const status = String(row[col['Status']]).trim();
if (status !== 'Sent' && status !== 'Follow-up drafted') continue;
handled++;
const email = String(row[col['Email']]).trim();
let threadId = String(row[col['Thread ID']]).trim();
const rowNum = r + 1;
if (!threadId) {
const found = GmailApp.search('in:sent to:' + email, 0, 1);
if (!found.length) {
sheet.getRange(rowNum, col['Note'] + 1).setValue('No sent thread found for this email');
continue;
}
threadId = found[0].getId();
sheet.getRange(rowNum, col['Thread ID'] + 1).setValue(threadId);
}
const thread = GmailApp.getThreadById(threadId);
if (!thread) {
sheet.getRange(rowNum, col['Note'] + 1).setValue('Thread ID not found');
continue;
}
// Ignore unsent drafts so our own follow-up draft does not count as a message.
const messages = thread.getMessages().filter(function (m) { return !m.isDraft(); });
const last = messages[messages.length - 1];
const fromMe = last.getFrom().toLowerCase().indexOf(me) !== -1;
if (!fromMe) {
sheet.getRange(rowNum, col['Status'] + 1).setValue('Replied');
sheet.getRange(rowNum, col['Last activity'] + 1).setValue(last.getDate());
continue;
}
if (status === 'Follow-up drafted') {
// If the draft was sent, the newest message is newer than our note. Start waiting again.
if (last.getDate() > new Date(row[col['Last activity']])) {
sheet.getRange(rowNum, col['Status'] + 1).setValue('Sent');
sheet.getRange(rowNum, col['Last activity'] + 1).setValue(last.getDate());
}
continue;
}
const days = (now - last.getDate()) / 86400000;
const done = Number(row[col['Follow-ups']]) || 0;
if (days < CONFIG.WAIT_DAYS) continue;
if (done >= CONFIG.MAX_FOLLOW_UPS) {
sheet.getRange(rowNum, col['Status'] + 1).setValue('No reply');
continue;
}
const first = String(row[col['Name']]).trim().split(' ')[0] || 'there';
thread.createDraftReply(CONFIG.FOLLOW_UP_BODY.replace('{name}', first));
sheet.getRange(rowNum, col['Status'] + 1).setValue('Follow-up drafted');
sheet.getRange(rowNum, col['Follow-ups'] + 1).setValue(done + 1);
sheet.getRange(rowNum, col['Last activity'] + 1).setValue(now);
}
}
function setupCreatorTrigger() {
ScriptApp.getProjectTriggers().forEach(function (t) {
if (t.getHandlerFunction() === 'checkCreators') ScriptApp.deleteTrigger(t);
});
ScriptApp.newTrigger('checkCreators').timeBased().everyDays(1).atHour(8).create();
}
Run setupCreatorTrigger once. It schedules checkCreators each morning and removes only its own earlier trigger. Google runs a time trigger at some point inside the hour you pick, not at the exact minute.
How do you change the wait time or the wording?
Edit the CONFIG block at the top:
WAIT_DAYSis how many quiet days before a draft.MAX_FOLLOW_UPSis how many drafts per creator.FOLLOW_UP_BODYis the draft text.{name}becomes the creator's first name.MAX_ROWSlimits how many rows one run handles.
Write the follow-up in your own voice. A generic nudge reads as generic.
What should you check before you trust it?
I tested the script with made-up rows and threads in a mock harness (13 of 13 checks passed) and a syntax check. I did not run it against a live Gmail account or a live Sheet. Test it on a copy with a few rows you control:
- Make a copy of your sheet. Add three rows with email addresses you own. Send each a short message from your Gmail and set Status to
Senton all three. - Reply to one from the other address.
- Set
WAIT_DAYSto 0 and runcheckCreatorsby hand. - Check that the replied row says
Replied, and the other two each have a draft and sayFollow-up drafted. - Send one draft, run it again, and check that the row goes back to
Sent.
Steps 4 and 5 matter most. Google's line says the draft replies "to the sender of the last message in this thread", and for a thread whose last message is your own, I could not confirm who the draft is addressed to. Open each draft and check the To line before you send. I also could not confirm two other things: whether an unsent draft shows up in getMessages() (the script skips messages that report isDraft(), so it should not matter, but test it), and how well search matches a thread when you have written to the same person before. If you wrote to someone twice, the script picks the newest thread. Fix the Thread ID by hand if it picked the wrong one.
What else should you know?
- Auto-replies. An out-of-office answer counts as a reply, so the row says
Repliedeven though the creator has not answered. Look at those by hand. - Replies from another address. If a creator answers from a different address, the thread may still match, because it is the same thread. If they write a new email, the script does not see it. Update the row yourself.
- Managers and agents. If an agent replies for the creator, the row says
Replied. That is correct, but you may want a note. - Only rows you mark. It ignores any status except
SentandFollow-up drafted. - Google limits. Scripts have daily limits on runtime and on email. Drafts are not sent, but check Google's quotas page for the current numbers.
Where does an LLM fit in this?
Not in the tracking. Whether a creator replied, and how many days it has been, are facts the thread already holds, and plain rules read them more reliably than a model. An LLM can help in one place: drafting a follow-up that refers to what the creator actually posts, given their page and your first note. A person should read each draft, because a follow-up that gets a detail about a creator wrong costs more than a late one. My stance: AI writes the code, plain automation runs it, and an LLM shows up only where the input is messy and a person checks the result. The same pattern, for your own Gmail, is in the follow-up engine.
Where this has limits
- Not run in a live account. Mock-tested only. Test on a copy.
- It reads Gmail. The script asks to read and change your Gmail. Use a mailbox you are comfortable giving that to, and read the permission screen.
- One mailbox. It sees threads in the account that installed the trigger. If several people send outreach, each needs their own copy.
- It drafts, it does not send. That is deliberate.
- No payments, content approvals or rights. This is an outreach tracker, not a creator management platform.
- Email rules vary. See the CAN-SPAM note above and check with an attorney if you are unsure.
- Template pages change. My look at the search results is from October 30, 2026.
If outreach is the step that gets dropped on your team, see our AI-powered operations work or tell us about it on /letstalk. Bring your current sheet with fake rows and the step that gets skipped. Do not send passwords or login details.
Frequently asked questions
What columns should an influencer outreach spreadsheet have?
At minimum: name, email, status, the date you last contacted them and a note. This script adds a thread ID and a follow-up count so it can read the email for you.
Can Google Sheets track whether a creator replied?
Not by itself. A Google Apps Script can read the Gmail thread and write the result back to the row, which is what the script above does.
Will it send follow-ups automatically?
No. It writes a draft in the same thread and you send it. A follow-up to a stranger is worth reading first.
Does it work with Google Workspace accounts?
It should. Workspace and personal accounts have different daily limits, listed on Google's quotas page. Test it on a copy first.
What if a creator replies from a different email address?
If they reply in the same thread, the script sees it. If they start a new email, it does not. Update the row by hand.
Do I need to know how to code?
No. You paste the script, run one setup function and approve the permissions. An AI tool can help you change the wording. Test it on a copy.
Read next: Track creator content usage rights once creators start sending content, and operations automation for product brands for the order side of the same business.