If you track quotes in a spreadsheet, you already know the problem. A quote goes out on Monday. By Thursday, you meant to follow up. By the following Monday, you are not sure if you did. The prospect moved on.
This workflow template reads your quotes sheet every morning, checks how many days have passed since each quote was sent, and automatically sends a follow-up email from your Gmail account to any prospect who has not responded within your follow-up window. After sending, it writes "Follow-Up Sent" back into the sheet so the row is marked and not contacted again.
No CRM required. No new software. Just Google Sheets, Gmail, and n8n.
A scheduled n8n workflow that:
By the end of this tutorial, you have a google sheets automation that handles follow-ups without anyone needing to remember to do them.
A Google Sheet set up as a quotes tracker. Your sheet needs at minimum these columns:
Additional columns (quote value, product, salesperson) are fine: the workflow only reads and writes the five above.
n8n instance. n8n Cloud or self-hosted, version 1.0 or later.
Google Sheets credentials in n8n. In Credentials, add a Google Sheets OAuth2 credential authorized against the Google account that owns the sheet.
Gmail credentials in n8n. Add a Gmail OAuth2 credential authorized against the account you want follow-ups to send from. This should be a real mailbox your team monitors: replies from prospects will land there.
The complete node sequence:
Schedule Trigger (daily, 8am)
→ Google Sheets (Read all rows from Quotes sheet)
→ Code node (filter rows due for follow-up: 3 or 7 days old, status blank)
→ IF node (check if any rows need follow-up)
→ True: Loop Over Items
→ Gmail Send (follow-up email to each prospect)
→ Google Sheets Update (write "Follow-Up Sent" to status column)
→ False: (end, nothing to do today)
The Loop Over Items node handles multiple prospects in a single run: if three quotes are due today, all three get an email and all three rows get updated in a single workflow execution.
Before building any nodes manually, use n8n's built-in AI to generate the workflow structure. This gives you a connected starting point in seconds; you then configure credentials and refine the logic in the steps that follow.
Open n8n and create a new workflow. Click the Generate workflow button in the top-right corner of the canvas (the sparkle/AI icon), or look for the AI description bar that appears on an empty canvas.
Use this prompt:
Build a workflow that runs every day at 8am. Read all rows from a Google Sheet called Quotes Tracker. Find rows where the Date Sent column is exactly 3 or 7 days ago and the Status column is blank. For each matching row, send a follow-up email from Gmail to the address in the Prospect Email column. After sending each email, update the Status column in that row to "Follow-Up Sent".
The AI will generate a set of connected nodes. It will not be fully configured: credentials are missing, column names are placeholders, and the Code node logic will need to be replaced with the specific JavaScript in the steps below. That is expected.
What the AI typically gets right:
What you will configure in the steps below:
Once the AI has generated the workflow, proceed to Step 2 to configure each node.
Create a new workflow and add a Schedule Trigger node.
Set it to run daily at 8am (or whatever time your team starts the day). Running it in the morning means follow-ups land in prospect inboxes at the start of their day.
Add a Google Sheets node after the trigger. Configure it:
Run the node manually to confirm it returns your quotes data. Check the output panel to see the exact column names n8n is using as JSON keys: you will need these in the next step.
Add a Code node after the Google Sheets node. This node does three things: calculates how many days have passed since each quote was sent, keeps only the rows that fall on your follow-up days, and skips any row that already has a status value.
const items = $input.all();
const today = new Date();
today.setHours(0, 0, 0, 0);
const followUpDays = [3, 7]; // Days after sending when follow-up should go out
const dueForFollowUp = items.filter(item => {
const status = item.json['Status'] || '';
if (status.trim() !== '') return false; // Already followed up or closed
const dateSent = new Date(item.json['Date Sent']);
if (isNaN(dateSent)) return false; // Skip rows with no date
const daysDiff = Math.floor((today - dateSent) / (1000 * 60 * 60 * 24));
return followUpDays.includes(daysDiff);
});
return dueForFollowUp.map(item => ({ json: item.json }));
Adjust 'Status' and 'Date Sent' to match your exact column header names from the Google Sheets output. Change followUpDays to whatever intervals make sense for your sales cycle: [3, 7, 14] for a longer cycle, [2, 5] for a faster one.
Run the node with test data to confirm it returns only the rows you expect. If you want to test without waiting for real dates to pass, temporarily change a "Date Sent" value in your sheet to three days ago and run the workflow manually.
Add an IF node after the Code node. This prevents the workflow from throwing an error when no follow-ups are due today.
Configure the condition:
Connect the True branch to the Loop Over Items node. Leave the False branch unconnected: when nothing is due, the workflow ends silently.
Add a Loop Over Items node on the True branch. This node feeds one row at a time to the Gmail Send and Google Sheets Update nodes, so each prospect gets their own email and their own row update.
No configuration is needed on this node: connect it to receive the filtered rows from the IF node, then connect its output to the Gmail Send node.
Add a Gmail node inside the loop. Set the operation to Send Email.
Configure it:
Hi {{ $json['Prospect Name'] }},
I wanted to follow up on the quote we sent over for {{ $json['Quote Number or Description'] }}.
If you have any questions or would like to discuss details, I am happy to jump on a call. Just reply to this email or reach us at [phone number].
Looking forward to hearing from you.
[Your name]
[Company name]
Adjust the body to match your tone. The prospect name, quote description, and email are all pulled from the sheet row dynamically.
After the Gmail Send node, add a Google Sheets node with the operation set to Update Row.
Configure it:
This writes "Follow-Up Sent" into the Status column for the row that was just emailed. On the next daily run, the Code node in Step 3 will skip this row because the status is no longer blank.
Toggle the workflow to Active. Test by temporarily adding a row with a "Date Sent" value three days ago and a blank Status, then triggering the workflow manually. Confirm the follow-up email arrives in the test prospect's inbox and the Status column in the sheet updates to "Follow-Up Sent."
Date format inconsistencies. The Code node's date math only works if "Date Sent" values are in a consistent format. YYYY-MM-DD (e.g. 2026-05-23) is the most reliable because JavaScript's Date parser handles it correctly across timezones. If your team is entering dates in MM/DD/YYYY or other formats, either standardize the entry format or add a normalization step at the top of the Code node. Inconsistent date formats are the most common reason this workflow fails silently.
Replied quotes that do not get their status updated. This workflow marks rows as "Follow-Up Sent" after the email goes out, but it does not know whether the prospect replied. If a prospect responds, your team needs to manually update the Status column (e.g. to "Replied," "Closed," or "Won") so the row is excluded from future follow-up runs. Build the habit of updating the Status column when a quote is resolved.
Multiple follow-ups on the same day. If a prospect has both a Day 3 and Day 7 follow-up due on the same run (which would only happen if the follow-up days array overlaps with a row that was somehow missed earlier), they will receive two emails. The safest guard is to keep the Status update immediate: as long as "Follow-Up Sent" is written to the row after the first email, the second follow-up will never fire because the row is already marked.
Add a "Closed" status to permanently remove rows from follow-up. The Code node already skips any row with a non-blank Status. Train your team to mark won, lost, or declined quotes as "Closed" and those rows will never be emailed again; no need to delete them from the sheet.
Personalize the follow-up with the quote value. Add a "Quote Value" column to your sheet and reference it in the email body: "the quote we sent for ${{ $json['Quote Value'] }}." Specificity makes follow-ups feel less automated, even when they are.
Escalate unresponded quotes after Day 14. Add a second filter pass in the Code node for rows that are 14 days old and still show "Follow-Up Sent" as their status. Route those to a separate Gmail action that notifies your sales manager internally: a human should decide whether to continue pursuing or close the quote.
Quote follow-up is one of the highest-ROI automation targets for small manufacturers because the cost of not following up is a real, measurable number: deals that went to a competitor because your team forgot. Unlike more complex automation builds, this workflow requires no ERP connection, no webhook setup, and no API credentials beyond what you already have in Google Workspace.
The build also teaches a pattern that applies directly to other processes your team manages in Google Sheets: supplier contract renewals, overdue invoices, certification expiry dates. Any sheet with a date column and a status column is a candidate for this same workflow structure.
The Flow Kaizen guide covers how to sequence your first five automation builds, starting with quick wins like this one and building toward more connected workflows as your team's confidence grows.