One of the most powerful patterns in Apps Script is syncing a Google Sheet to Google Calendar — creating calendar events from spreadsheet rows, storing event IDs back in the sheet, and updating or deleting events when the data changes.
The Core Pattern: Store Event IDs in the Sheet
The key to a reliable sync is saving the calendar event ID back to the sheet row after creating the event. This lets you find and update or delete the exact event later.
Add a column Event ID (column I) to your Sales Tracker for this purpose.
Full Sync: Create or Update Events from Sheet
functionsyncDealsToCalenar(){var sheet =SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sales Tracker");var data = sheet.getRange(2,1, sheet.getLastRow()-1,9).getValues();// Columns: [Customer Name, Email, Product, Amount, Region, Sales Rep, Status, Date, Event ID]var calendar =CalendarApp.getDefaultCalendar(); data.forEach(function(row, i){var customerName = row[0];var product = row[2];var salesRep = row[5];var status = row[6];var dealDate = row[7]?newDate(row[7]):newDate();var existingEventId = row[8];if(status !=="In Progress")return;// Schedule follow-up 2 days after the deal datevar start =newDate(dealDate.getTime()+2*24*60*60*1000); start.setHours(10,0,0,0);var end =newDate(start.getTime()+30*60*1000);var title ="Follow-up – "+ customerName +" ("+ product +")";var description ="Sales rep: "+ salesRep +"\nStatus: "+ status;if(existingEventId){// Update the existing eventvar event = calendar.getEventById(existingEventId);if(event){ event.setTitle(title); event.setTime(start, end); event.setDescription(description);Logger.log("Updated event for: "+ customerName);return;}}// Create a new event and store its IDvar newEvent = calendar.createEvent(title, start, end,{description: description }); sheet.getRange(i +2,9).setValue(newEvent.getId());Logger.log("Created event for: "+ customerName +" | ID: "+ newEvent.getId());});}
Deleting Events When a Deal is Removed
functionremoveCalendarEventsForCancelledDeals(){var sheet =SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sales Tracker");var data = sheet.getRange(2,1, sheet.getLastRow()-1,9).getValues();var calendar =CalendarApp.getDefaultCalendar(); data.forEach(function(row, i){var status = row[6];var eventId = row[8];// If status changed to Closed or deal removed, clean up the calendar eventif(status ==="Closed"&& eventId){var event = calendar.getEventById(eventId);if(event){ event.deleteEvent(); sheet.getRange(i +2,9).clearContent();// Clear the stored IDLogger.log("Deleted calendar event for closed deal: "+ row[0]);}}});}
Triggering Sync on Edit
Combine the sync with an onEdit trigger so the calendar stays live as the sheet changes:
functiononEditInstallable(e){var sheet = e.source.getActiveSheet();if(sheet.getName()!=="Sales Tracker")return;var editedCol = e.range.getColumn();// Re-sync if Status (col 7) or Date (col 8) changedif(editedCol ===7|| editedCol ===8){syncDealsToCalenar();}}
Reading the Sheet to Build a Weekly Agenda Email
This combines calendar reading with Gmail to send a Monday morning agenda showing all this week's sales-related events: