How to Track Warranty Expiration Dates in Google Sheets, with Reminders (2026)
Warranties are easy to buy and easy to forget. The laptop, the office printer, the dishwasher, the company vans: each one comes with coverage that quietly runs out after a year or three, and the time you find out is usually the week after it ended. If you already keep purchases or assets in a spreadsheet, a few extra columns turn it into a warranty tracker. This guide sets up a Warranties tab that works out each end date from the purchase date, handles extended warranties, shows what's expiring in the next 90 days, and adds a free Apps Script that emails each owner one digest 60, 30 and 7 days before coverage ends.
It's the same idea as tracking contract renewals, with two differences: the end date usually has to be calculated (purchase date plus a number of months), and an item can have two dates, the manufacturer's warranty and an extended plan that runs past it.
Step 1: a Warranties tab
Create a tab named Warranties with these headers in row 1. The script finds columns by name, but the formulas below assume this order (A to M):
| Col | Header | What goes in it |
|---|---|---|
| A | Item | e.g. “Dell Latitude 5440 (front desk)” |
| B | Serial | Serial number or asset tag; manufacturers ask for it when you file a claim |
| C | Vendor | Who you'd call: the manufacturer, the store, or the warranty provider |
| D | Purchase date | The date coverage starts (check the terms: some start at delivery or installation) |
| E | Warranty months | Length of the standard warranty, e.g. 12, 24 or 36 |
| F | Warranty ends | A formula (Step 2), formatted as a date |
| G | Extended until | End date of an extended warranty or protection plan, if you bought one; otherwise blank |
| H | Coverage ends | A formula (Step 2): the later of F and G |
| I | Days left | A formula (Step 2) |
| J | Owner email | Who should get the reminder; blank sends it to you |
| K | Status | Leave blank while you own it; Retired, Sold, Disposed, Replaced or Claimed stops reminders |
| L | Proof of purchase | A link to the receipt or invoice in Drive (see the tips) |
| M | Sent log | Leave empty; the script records what it sent |
Format D, F, G and H as dates (Format › Number › Date). A date typed as text, like “Jan 11”, won't calculate.
Step 2: end date, coverage and days left
In F2, fill down. EDATE adds whole months and handles short months (a 12-month warranty bought on February 29 ends February 28):
=IF(OR(D2="",E2=""),"",EDATE(D2,E2))
That gives the anniversary date. If your terms say coverage lasts “through” the day before (a one-year warranty from January 11 ending January 10), use EDATE(D2,E2)-1 instead. When the paperwork prints an exact end date, just type it over the formula.
In H2, the date coverage actually runs out, whichever is later:
=IF(AND(F2="",G2=""),"",MAX(F2,G2))
In I2, the days remaining (format it as a plain number; negative means it has expired):
=IF(H2="","",H2-TODAY())
TODAY() recalculates every day, so Days left counts down on its own.
Step 3: what's expiring in the next 90 days
On a new tab, this lists every item still in service whose coverage ends within 90 days, soonest first:
=SORT(FILTER({Warranties!A2:A,Warranties!C2:C,Warranties!H2:H,Warranties!I2:I},
Warranties!H2:H<>"",Warranties!I2:I>=0,Warranties!I2:I<=90,Warranties!K2:K=""),4,TRUE)
A FILTER that finds nothing shows #N/A; wrap it in IFERROR(…,"Nothing expiring") if you like. Format the third column as a date. For a quick count on a dashboard: =COUNTIFS(Warranties!I2:I,">=0",Warranties!I2:I,"<=30",Warranties!K2:K,"").
Step 4: conditional formatting
Select I2:I, open Format › Conditional formatting, pick Custom formula is, and add these rules:
| Custom formula | Color | Meaning |
|---|---|---|
=AND(ISNUMBER(I2),I2>=0,I2<=30) | Red | Ends within 30 days |
=AND(ISNUMBER(I2),I2>30,I2<=90) | Yellow | Ends within 90 days |
=AND(ISNUMBER(I2),I2<0) | Gray | Expired |
Step 5: email reminders before coverage ends
Sheets can't send an email when a date arrives on its own; its notification rules react to edits, not the calendar. A short Apps Script on a daily trigger does it. Open Extensions › Apps Script, paste this, set DEFAULT_TO (or leave it blank to email yourself), save, run sendWarrantyReminders once to approve permissions, then add a trigger: Triggers › Add trigger › sendWarrantyReminders › Time-driven › Day timer.
/**
* Daily warranty expiration reminders from a "Warranties" tab.
* Headers used (any order): Item, Warranty ends, Sent log.
* Optional: Serial, Vendor, Extended until, Owner email, Status.
* Emails each owner one digest 60, 30 and 7 days before coverage ends
* (the later of Warranty ends and Extended until). Blank owner = you.
*/
const SHEET_NAME = 'Warranties';
const DEFAULT_TO = ''; // e.g. 'it@yourcompany.com' (blank = you)
const REMIND_DAYS = [60, 30, 7]; // days before coverage ends
const STOP_STATUS = /^(retired|sold|disposed|replaced|claimed)$/i;
const MAX_EMAILS = 20; // safety cap per run
function sendWarrantyReminders(now) {
const sh = SpreadsheetApp.getActive().getSheetByName(SHEET_NAME);
const rows = sh.getDataRange().getValues();
const head = rows[0].map((h) => String(h).trim());
const col = (name) => head.indexOf(name);
const c = {
item: col('Item'), serial: col('Serial'), vendor: col('Vendor'),
ends: col('Warranty ends'), ext: col('Extended until'),
owner: col('Owner email'), status: col('Status'), log: col('Sent log'),
};
if (c.item === -1 || c.ends === -1 || c.log === -1) {
throw new Error('Need Item, Warranty ends and Sent log columns.');
}
const tz = Session.getScriptTimeZone();
// A time-driven trigger passes an event object, so only use a real Date.
const today = now instanceof Date ? new Date(now) : new Date();
today.setHours(0, 0, 0, 0);
const get = (row, i) => (i === -1 ? '' : row[i]);
const isDate = (v) => v instanceof Date && !isNaN(v);
const ymd = (d) => Utilities.formatDate(d, tz, 'yyyy-MM-dd');
const nice = (d) => Utilities.formatDate(d, tz, 'EEE, MMM d, yyyy');
const daysTo = (d) => {
const x = new Date(d); x.setHours(0, 0, 0, 0);
return Math.round((x - today) / 86400000);
};
const when = (n) => (n === 0 ? 'today' : n === 1 ? 'tomorrow' : `in ${n} days`);
const hitOf = (left, list) =>
left < 0 ? undefined : list.filter((n) => left <= n).sort((a, b) => a - b)[0];
const me = Session.getEffectiveUser().getEmail();
const byOwner = {}, logs = [];
for (let r = 1; r < rows.length; r++) {
const row = rows[r];
if (!row[c.item] || STOP_STATUS.test(String(get(row, c.status)).trim())) continue;
const base = get(row, c.ends), ext = get(row, c.ext);
// Coverage ends on the later of the two dates.
const end = isDate(ext) && (!isDate(base) || ext > base) ? ext : base;
if (!isDate(end)) continue;
const left = daysTo(end);
const hit = hitOf(left, REMIND_DAYS);
if (hit === undefined) continue;
const sent = String(row[c.log] || '').split(';').filter(String);
const key = `W${ymd(end)}@${hit}`;
if (sent.includes(key)) continue;
const label = [row[c.item], get(row, c.serial) && `S/N ${get(row, c.serial)}`,
get(row, c.vendor)].filter(String).join(', ');
const kind = end === ext ? 'extended warranty' : 'warranty';
const to = String(get(row, c.owner)).trim() || DEFAULT_TO || me;
(byOwner[to] = byOwner[to] || []).push({
left, text: `${label}: ${kind} ends ${when(left)} (${nice(end)})`,
r: r + 1, log: sent.concat(key).slice(-9).join(';'),
});
}
let emails = 0;
for (const to of Object.keys(byOwner)) {
if (emails >= MAX_EMAILS || MailApp.getRemainingDailyQuota() < 1) break;
const items = byOwner[to].sort((a, b) => a.left - b.left);
MailApp.sendEmail(to, `Warranties ending soon (${items.length})`,
'Check these before coverage runs out (file claims, book a last ' +
'inspection, or decide on an extension):\n\n- ' +
items.map((x) => x.text).join('\n- ') + '\n\nSent from the Warranties sheet.');
// Log only after the email for this owner went out.
items.forEach((x) => sh.getRange(x.r, c.log + 1).setValue(x.log));
emails++;
}
}
How it behaves:
- One email per owner, only when something's due. Items are grouped by Owner email, so the IT lead gets one digest for the laptops and the office manager one for the appliances, soonest first. Rows with a blank owner go to
DEFAULT_TOor you. - 60, 30 and 7 days before coverage ends. It uses the later of Warranty ends and Extended until, and says which one is ending. It reads the date columns directly, so it doesn't depend on the Coverage ends or Days left formulas.
- No catch-up pile. If the trigger starts late or misses a few days, it sends the one reminder that fits (say, the 30-day one at 19 days left), not all of the earlier ones.
- Each reminder goes out once. Sent log entries include the end date they were for (like
W2027-01-11@30), so if you renew or extend coverage and change the date, the new date gets its own reminders. Entries are written only after that owner's email has gone out. - Stops when you're done. Set Status to Retired, Sold, Disposed, Replaced or Claimed and the row is skipped. Expired items are ignored.
We mock-tested this logic before publishing: the first run sent one digest per owner with the right lines in date order, a rerun the same day and the next day sent nothing, an extended warranty was picked over the standard one (and an extension dated earlier than the standard end was ignored), a missed 7-day reminder went out once on the last day, and nothing was logged when the email quota was used up.
Tips
- Keep the proof with the row. Most claims need the receipt and the serial number. Save receipts to a Drive folder and paste each file's link into Proof of purchase, or use
=HYPERLINK(url,"receipt")for a short label. Keep the folder shared only with the people who need it; receipts can show card digits and addresses. - Read the terms once. Warranties differ on when coverage starts (purchase, delivery or installation), what's covered, and whether you have to register the product. Note anything unusual in a Notes column. This guide only does date arithmetic and isn't legal advice.
- Use the reminder. The weeks before a warranty ends are the time to report that rattle, run a battery or screen check, book a last service visit, or decide whether an extended plan is worth it.
- Match time zones. Set the script's time zone (Project Settings in Apps Script) to the same zone as the spreadsheet (File › Settings), or a date can land a day off.
- Ownership and quotas. The trigger runs as whoever created it, and the emails come from that account. Sends count against your Apps Script email quota; one digest per owner per day is far below it, and
MAX_EMAILScaps each run anyway. - Servicing, not just warranties? For recurring service intervals on equipment and vehicles, see maintenance schedule reminders. For software subscriptions and service agreements, see contract renewal alerts.
See SheetReminders
Related Google Sheets guides
- How to generate a PDF or Google Doc for each row in Google Sheets
- Best Autocrat alternatives for mail merge to PDF and Docs
- Make invoices or certificates from Google Sheets rows
- Create sales quotes or estimates from Google Sheets
- Generate receipts from Google Sheets rows
- Create purchase orders from Google Sheets
- Make packing slips from Google Sheets orders
- Generate a PDF certificate from a Google Form response
- Issue numbered training completion certificates with expiry dates
- Make a PDF progress report for each student
- Generate offer letters from a Google Sheets hiring tracker
- Email each row’s PDF as an attachment
- Automatic email reminders from a date column
- Contract renewal and expiration alerts from Google Sheets
- Track certificate of insurance (COI) expirations in Google Sheets
- Track employee license and certification expirations
- Track equipment and vehicle maintenance with reminders
- Track warranty expiration dates with reminders
- Birthday and work anniversary reminders from Google Sheets
- Rent due and lease renewal reminders from Google Sheets
- Send invoice payment reminders from Google Sheets
- Highlight overdue tasks and send email reminders
- Follow-up reminders for leads, clients and candidates
- Membership renewal reminders for clubs and associations
- SheetWeave vs Autocrat
- SheetWeave vs Document Studio
- SheetWeave vs RowDocs
Instructions refer to Google Sheets and Apps Script as they worked on November 13, 2026. Not affiliated with Google or any other vendor named here. Google Sheets and Google Drive are trademarks of Google LLC. Features change, so verify in your own account. Questions or corrections: hello@loomwise.app.