Send tiered deadline countdown reminders
Email Northwind teammates at 7, 3, and 1 days out from a Sheet of upcoming deadlines.
Published Sep 30, 2025
A single “one week until deadline” email rarely lands well. Sent too early and people file it; sent too late and there is no time left to act. The fix at Northwind is tiered nudges — a heads-up a week out, a check-in three days out, a real “tomorrow” alert the day before — each to the right owner, with no duplicates.
This script reads the team’s Deadlines sheet and sends those staged
reminders. It tracks which stage each row last received so a deadline only
generates one email per tier — no morning floods, no escalating noise.
What you’ll need
- A
Deadlinessheet with these headers in row 1:task,owner,due,lastReminded. Paste the spreadsheet ID intoDEADLINES_SHEET_ID. ownershould be a single email address — the person responsible.duemust be a real date (format the column as Date) — strings are ignored by theinstanceof Dateguard.- Leave
lastRemindedblank for new rows. The script fills it in.
The script
// The spreadsheet that holds the Deadlines list.
const DEADLINES_SHEET_ID = '1abcDeadlinesId';
// Reminder stages in days. Earliest first so the find() picks the largest
// stage the row still qualifies for.
const STAGES = [7, 3, 1];
// Milliseconds in a day, used to convert (due - today) into whole days.
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
/**
* Reads the Deadlines sheet and sends a reminder email to the owner of
* any row whose due date has just crossed a 7/3/1-day threshold. Tracks
* the last stage sent so each tier fires exactly once per row.
*/
function tieredCountdown() {
const sheet = SpreadsheetApp.openById(DEADLINES_SHEET_ID).getSheets()[0];
const values = sheet.getDataRange().getValues();
if (values.length < 2) {
Logger.log('No deadline rows — nothing to do.');
return;
}
// 1. Split off the header and build a name -> column-index map.
const [header, ...rows] = values;
const col = Object.fromEntries(header.map((k, i) => [k, i]));
const today = new Date();
let sent = 0;
rows.forEach((r, i) => {
// 2. Skip rows without a real due date.
if (!(r[col.due] instanceof Date)) return;
// 3. Days remaining, rounded up so an item due at 6pm today still
// counts as "1 day out" if we run this morning.
const days = Math.ceil((r[col.due] - today) / ONE_DAY_MS);
// 4. Find the largest stage the row qualifies for that we have not
// already sent. STAGES is [7, 3, 1] — we walk that order so a
// fresh week-out row picks 7, a three-day row picks 3, etc.
const stage = STAGES.find((s) => days <= s && r[col.lastReminded] !== s);
if (!stage) return;
// 5. Send the reminder and stamp the lastReminded column so the next
// run can move on to the next tier without re-sending this one.
GmailApp.sendEmail(
r[col.owner],
`${days}d to deadline: ${r[col.task]}`,
`Reminder: "${r[col.task]}" is due in ${days} day${days !== 1 ? 's' : ''}.`
);
values[i + 1][col.lastReminded] = stage;
sent++;
});
// 6. Persist any updated lastReminded values in a single write.
sheet.getDataRange().setValues(values);
Logger.log('Sent ' + sent + ' reminder(s).');
}
How it works
tieredCountdownreads the wholeDeadlinessheet and bails if there are no data rows.- It builds a
colmap from the header so the rest of the script readsr[col.due]instead ofr[2]. - For each row it computes days remaining as
(due - today) / 86,400,000, rounded up so a deadline at the end of today still counts as “1 day”. - It uses
Array.findoverSTAGES([7, 3, 1]) to pick the highest tier the row currently qualifies for that has not already been sent. The storedlastRemindedvalue gates against repeats. - It sends the email and writes the new stage back into the in-memory grid.
- After the loop, a single
setValuescall persists every updatedlastRemindedcell.
Example run
Suppose today is Monday and the sheet looks like this:
| task | owner | due | lastReminded |
|---|---|---|---|
| Edit reel for Fabrikam | [email protected] | 2025-10-07 (7d) | |
| Brochure copy to client | [email protected] | 2025-10-03 (3d) | 7 |
| Site launch checklist | [email protected] | 2025-10-01 (1d) | 3 |
| Brief next quarter | [email protected] | 2025-11-15 (49d) |
After a run:
- Row 1 crosses 7 days → email to Alex,
lastRemindedbecomes 7. - Row 2 crosses 3 days (already had 7) → email to Priya,
lastRemindedbecomes 3. - Row 3 crosses 1 day (already had 3) → email to Sam,
lastRemindedbecomes 1. - Row 4 is 49 days out, so the
find()returns nothing and no email goes out.
Tomorrow’s run sends nothing for rows 1–3 unless they cross another tier.
Trigger it
Run this once a day, early enough to land before the workday starts:
- In the Apps Script editor, open Triggers (the clock icon).
- Add a trigger for
tieredCountdown, time-driven, day timer, 6am–7am. - Save and approve the spreadsheet and Gmail authorisation prompts.
Watch out for
- If you change
STAGES, the existinglastRemindedvalues may no longer line up. The script will simply pick the next matching stage on the next run, but you can clear the column to restart cleanly. - A row that was already past its first tier on the day you set it up
will fire all earlier tiers at once. Pre-fill
lastRemindedfor those rows to the largest tier that has already passed if you want to skip them. - The script sends one email per row. A row with five owners in the
ownercell (comma-separated) works because Gmail accepts that, but bouncing addresses still cost a quota slot — keep the column clean. - A skipped run (quota outage, trigger pause) does not lose data — rows still progress on the next run. They just arrive a day late.
Related
Send a feedback survey after each event
Email attendees a survey link automatically after Northwind workshops or trainings.
Updated Oct 24, 2025
Build a team-capacity view from calendars
Show how booked the Northwind team is this week — meeting hours per person.
Updated Oct 20, 2025
Flag meetings that could have been emails
Detect short, agendaless, oversized meetings — the smell of bad calendar hygiene.
Updated Oct 12, 2025
Archive past events to a log sheet
Keep a searchable Northwind meeting history — every event logged with title, attendees, duration.
Updated Sep 26, 2025
Email a clean daily agenda each morning
Send Awadesh a list of today's Northwind events at 7am — no app needed.
Updated Sep 22, 2025