Task guide
Send an email when a status changes
Email the person on a row whenever the status in that row changes. One installed trigger covers every tab in the spreadsheet, including tabs you copy later.
1 · Prepare
Put these pieces in place
- Create a Google Sheet with a task name in column A, a status in column B, and an email address in column C.
- Add a dropdown to column B with statuses such as In progress and Complete. Select the column and choose Insert, then Dropdown.
- Create a tab named Template if you duplicate tabs for new projects, or keep all tasks on one tab.
- Keep your headers in row 1, because the script skips that row.
Change these values
- Template
TemplateEdits on this tab are skipped so preparing a new template does not send an email.
- Status column
2Column B is column 2. The script sends an email only when a cell in this column changes.
- Email column
3Column C is column 3. The notification email is sent to the address in this cell.
- Task name column
1Column A is column 1. The script includes this item name in the email subject and body.
2 · Review before running
What the script can access and change
Permissions you approve
- See, edit, create, and delete all your Google Sheets spreadsheets. The code only reads the edited row and the link of this spreadsheet. It writes nothing.
- Read, compose, send, and permanently delete all your email from Gmail. Google asks for full Gmail access because the code uses GmailApp, but the code only sends one notification for each status change.
Effects of one run
- Sends an email notification when a single cell in column B, in row 2 or below, is changed to a new value.
- Reads the task name and email address from the edited row, and the new and previous status from the edit. It does not write anything to the spreadsheet.
- Every notification is sent from the account that installed the trigger, whoever made the edit.
- Does nothing when more than one cell is edited at once, when the edit is on the Template tab, or when the edit is in row 1.
3 · Copy
Paste this into the Apps Script editor
Create or open a script file, replace the example values marked above, then save the project.
const TEMPLATE = 'Template'; // Edits on this tab are skipped so preparing a new template does not send an email.
const STATUS_COLUMN = 2; // Column B is column 2. The script sends an email only when a cell in this column changes.
const EMAIL_COLUMN = 3; // Column C is column 3. The notification email is sent to the address in this cell.
const TASK_NAME_COLUMN = 1; // Column A is column 1. The script includes this item name in the email subject and body.
/** @param {Events.SheetsOnEdit} e */
function sendEmailOnStatusChange(e) {
if (!e || !e.range) return;
const range = e.range;
const sheet = range.getSheet();
if (sheet.getName() === TEMPLATE) return;
if (range.getNumRows() > 1 || range.getNumColumns() > 1) return;
if (range.getColumn() !== STATUS_COLUMN) return;
const editedRow = range.getRow();
if (editedRow < 2) return;
const newStatus = e.value;
const oldStatus = e.oldValue;
if (!newStatus || newStatus === oldStatus) return;
const recipient = String(sheet.getRange(editedRow, EMAIL_COLUMN).getValue()).trim();
if (!recipient || !recipient.includes('@')) return;
const taskName = sheet.getRange(editedRow, TASK_NAME_COLUMN).getValue() || ('Row ' + editedRow);
const sheetName = sheet.getName();
const spreadsheet = e.source;
const subject = 'Status updated to ' + newStatus + ' on ' + sheetName;
const body = 'The status for "' + taskName + '" was updated to ' + newStatus + '.\n\n' +
'Sheet: ' + sheetName + '\n' +
'Row: ' + editedRow + '\n' +
'Previous status: ' + (oldStatus || '(none)') + '\n\n' +
'Open spreadsheet: ' + spreadsheet.getUrl();
GmailApp.sendEmail(recipient, subject, body);
} 4 · Run
Follow these steps in order
Set up one test row
On your sheet tab, put headers in row 1. Put one task in row 2, choose a status in column B, and put your own email address in column C.
Paste the function and save
Choose Extensions, then Apps Script. Replace the starter code with this function and save.
Install the On edit trigger
Click Triggers, the clock icon in the left sidebar, then click Add Trigger. Choose sendEmailOnStatusChange as the function, From spreadsheet as the event source, and On edit as the event type. Click Save and approve access when Google asks.
Test by changing the status
Return to the spreadsheet and pick a different status in cell B2. Within a few seconds, an email with a subject such as Status updated to Complete on Sheet1 arrives in your inbox. When you duplicate the tab for a new project, edits on the new tab send emails too, with no new trigger to install.
5 · Confirm
How to tell it worked
Changing a status dropdown cell sends an email notification to the recipient on that row within a few seconds, with a link back to the spreadsheet.
If the result is different
Troubleshooting
- The Execution log shows "You do not have permission to call GmailApp.sendEmail".
- Keep the function name sendEmailOnStatusChange and install it as an On edit trigger from Triggers in the left sidebar. A function named onEdit runs as a simple trigger, and simple triggers cannot send email.
- No email arrives after I change a status.
- Open Triggers and check that an On edit trigger runs sendEmailOnStatusChange. Then check that the edit is in column B on row 2 or below, that column C in that row holds an email address, and that the tab is not named Template.
- Clicking Run in the editor sends nothing.
- Test the function by changing a status cell in the spreadsheet instead. The Run button calls it with no edit event, so the first line stops the function.
- Pasting several cells at once sends no email.
- Paste the values first, then change each status cell on its own. The script skips edits to more than one cell, so a large paste cannot send many emails at once.
Technical details
Names used in this script
Open these details when you want to adapt the example or look up an exact service or method name.
Show Apps Script symbols and service pages
Apps Script symbols
Service pages
Keep going
Related tasks, basics, and apps
- Task guide Add a timestamp when a row changes
When you change a status cell, the script writes the current date and time into the same row. You save the script and nothing else needs to be installed.
- Task guide Send an email for each row in a Google Sheet
Send one email to each person listed on a Customers tab. The script marks each row Sent as it goes, so a later run sends only to rows that are not marked yet.
- Task guide Respond to a Google Form submission
Get an email every time someone submits your Google Form, listing each question and its answer. The email goes only to you, the owner of the script.
- Object reference Spreadsheet
A Spreadsheet is the whole Google Sheets file. Use SpreadsheetApp.getActiveSpreadsheet() or openById() to get it, then call getSheetByName() or getActiveSheet().
- Object reference Sheet
A Sheet is one tab inside a Google spreadsheet. Get it with getSheetByName() or getActiveSheet(). Use getLastRow() to find the last row with content, or getRange() to select cells to read.
- Object reference Range
A Range represents one cell such as A2 or a rectangular block of rows and columns in a Sheet. Use getValue() for one cell and getValues() or setValues() for a 2D array of rows.
- Object reference Trigger builder
ScriptApp.newTrigger(functionName) returns a TriggerBuilder that configures a timeBased(), forSpreadsheet(), or forForm() trigger before create() saves it.
- Google app Google Sheets
SpreadsheetApp lets a Google Sheets script read a Sheet or Range, add a custom menu or formula, and act on each row, such as sending one email per row.
- Google app Gmail
GmailApp scripts can send messages, search threads, apply labels, and read attachments after you approve Gmail access for the project.
- Google app Triggers
A trigger is a saved schedule or event rule in ScriptApp that runs your function on time, on edit, or on form submit.