Basics
Google Apps Script triggers explained
Google Apps Script triggers run functions automatically when a file opens, a cell changes, a form is submitted, or a scheduled time arrives. Use a simple onEdit or onOpen for work in the bound file. Choose an installable trigger for a schedule, form submission, or work that needs authorization.
1 of 8
Choose a simple or installable trigger
A trigger is a rule that says "when this happens, run that function". The event can be a person opening a spreadsheet, a person editing a cell, a form being submitted, or a time of day arriving.
Apps Script has two kinds. Simple triggers work because of the name you give a function. Installable triggers work because you set them up once, either in the Triggers panel or in code.
The two kinds differ in how much your function is allowed to do, and whose account it acts as.
The reserved names doGet(e) and doPost(e) are web-app request handlers. Google sometimes groups them with triggers, but the simple-trigger limits below (no authorization, about thirty seconds) do not apply to them. They run when the deployed web app receives an HTTP request.
2 of 8
Simple triggers: named functions
A simple trigger needs no setup at all. Name a function onEdit and Apps Script calls it every time somebody changes a cell in the bound spreadsheet. You do not install or approve anything.
Each handler works only in a project bound to the matching kind of file. onInstall runs only for Editor add-ons.
- onOpen(e) runs when the file is opened by somebody who can edit it. This is where a custom menu is built.
- onEdit(e) runs after a cell value is changed by a person typing in the sheet.
- onSelectionChange(e) runs when the person selects a different cell or range.
- onInstall(e) runs when an Editor add-on is installed. It normally calls onOpen(e) so the add-on menu appears immediately.
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Automation')
.addItem('Run summary', 'showSummary')
.addToUi();
}
function showSummary() {
Logger.log('Summary requested from the custom menu.');
} 3 of 8
What a simple trigger cannot do
A simple trigger runs without anybody approving anything, so it cannot call a service that needs authorization. It cannot use GmailApp, DriveApp, UrlFetchApp, or CalendarApp. If it tries, the run fails with a message about permission.
A simple trigger can modify the file it is bound to, but it cannot access another file even when the person editing has access to it, and the identity of the person editing is not always available.
A simple trigger is stopped after about thirty seconds at the time of writing. Script executions and API requests do not cause edit triggers to run. A form submission writing into a response sheet also does not cause onEdit to run.
When you need to access another file, send mail, use Drive, or run longer, use an installable trigger instead.
4 of 8
The event object e
Trigger functions receive one argument, usually written as e. It holds facts about what happened, such as which cell changed, what was typed, which file it was, or which form response arrived.
For an edit, e.range is the Range that changed, e.value is the new value as text for a single cell, and e.source is the Spreadsheet it happened in. A form-submit trigger in a Form-bound project receives e.response, a FormResponse. An On form submit trigger in a response spreadsheet receives e.values, e.namedValues, and e.range; it does not have e.response.
Make the handler match the trigger source you installed. Add this function to a spreadsheet-bound project, edit a cell, then look at the Executions page to see what it recorded.
/** @param {Events.SheetsOnEdit} e */
function onEdit(e) {
const range = e.range;
const sheetName = range.getSheet().getName();
Logger.log('Edited ' + sheetName + '!' + range.getA1Notation() + '. New value: ' + e.value);
} 5 of 8
Add an installable trigger in the panel
In the Apps Script editor, click the clock icon in the left sidebar, labelled Triggers. Then click Add Trigger at the bottom right. A form opens.
Choose the function to run, choose the deployment (Head, which means your current saved code), then choose the event source: time-driven, from spreadsheet, or from form. The rest of the form changes to match. Time-driven asks for an interval from every minute through once per month. From spreadsheet asks for On open, On edit, On change, or On form submit.
Click Save. Google asks you to approve the access the function needs, because from now on it will run while you are not there. The trigger appears in the list, and you can edit or delete it from the three dots at the end of its row.
- Time-driven triggers are approximate. An hourly trigger runs some time inside the hour, not on the minute.
- An add-on can use a time-driven trigger at most once per hour, even though an ordinary Apps Script project can schedule one as often as every minute.
- One project can hold at most twenty triggers per user, at the time of writing.
6 of 8
Create a scheduled trigger in code, once
ScriptApp.newTrigger builds the same thing the panel builds. You name the function, say what starts it, and call create(). Running this kind of setup function twice makes two triggers, and then your work happens twice, so check the existing list first.
This example schedules Monday work for the nine o'clock hour. Apps Script may choose any time inside that hour, then keep a similar offset on later weeks. Run createWeeklySummaryTrigger by hand one time. Do not call it from the function it installs.
A trigger that runs every few minutes uses everyMinutes(n), and n must be 1, 5, 10, 15, or 30. Any other number makes the setup function fail. The allowed values are listed in the ClockTriggerBuilder reference at https://developers.google.com/apps-script/reference/script/clock-trigger-builder.
Give the function that an installable trigger runs a name of its own. A function named onOpen or onEdit already runs as a simple trigger, so an installable trigger on the same name makes it run twice for each event.
function createWeeklySummaryTrigger() {
const handlerName = 'logWeeklySummary';
const alreadyInstalled = ScriptApp.getProjectTriggers().some((trigger) => {
return trigger.getHandlerFunction() === handlerName;
});
if (alreadyInstalled) {
Logger.log('A trigger for ' + handlerName + ' is already installed.');
return;
}
ScriptApp.newTrigger(handlerName)
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(9)
.create();
Logger.log('Installed a Monday trigger for ' + handlerName + '.');
}
function logWeeklySummary() {
Logger.log('The weekly trigger ran.');
} 7 of 8
Who owns a trigger
An installable trigger belongs to the account that created it. It runs as that account and uses that account's approvals, whoever caused it to fire. If your trigger sends mail, the mail comes from you and counts against your daily sending limit, even when a colleague submitted the form.
Other people cannot see your triggers. The code below lists only the triggers you own in this project. A teammate looking at the same project sees their own, and yours are not there.
If you leave the team or remove the project's access at https://myaccount.google.com/permissions, your triggers stop. Somebody else has to install their own.
function listInstalledTriggers() {
const triggers = ScriptApp.getProjectTriggers();
Logger.log('Triggers you own here: ' + triggers.length);
triggers.forEach((trigger) => {
Logger.log(trigger.getHandlerFunction() + ' runs on ' + String(trigger.getEventType()) + ' (id ' + trigger.getUniqueId() + ')');
});
} 8 of 8
Why an Apps Script trigger is not running
Nobody is watching when a trigger runs, so Google emails the owner a summary of failed runs, with the project name, the function, and the error message. The mail arrives at the address that owns the trigger, which is a good reason to know whose account installed it.
Installable triggers created by add-ons are the exception: Google does not send those failure emails to add-on users. The add-on has to report its own errors to someone who checks them.
To see for yourself, open Executions in the left sidebar of the editor. Every run is listed, by hand or by trigger, with the function name, the start time, the duration, and a status of Completed or Failed. Click a row to read the log lines from that run.
A trigger whose function was renamed fails each time it fires with "Script function not found", and Executions shows the run as Failed. The trigger stores the name as text, so renaming the function in the editor breaks it. Delete the trigger and add it again with the new name.
Editor add-ons can use supported simple and installable triggers. Google Workspace add-ons use the trigger definitions in their manifest instead of generic Apps Script simple or installable triggers. If you build an add-on, check which kind it is before you copy a trigger example.
- If clicking Run fails on e.range or e.response, test through the actual edit or form submission. Clicking Run does not supply a trigger event object.
- If a script changes a cell with setValue, an edit trigger does not fire. Test onEdit by editing a cell yourself.
- If a scheduled function never runs, check Triggers under the account that installed it, then check Executions for failures.
Remember
The short version
- Simple triggers are named functions such as onEdit, need no setup, cannot use services that require authorization, and can only modify their bound file.
- Installable triggers are set up in the Triggers panel or with ScriptApp.newTrigger, and can send mail, reach Drive, or work with another file.
- The event object e carries the details: a Form submit event has e.response, while a response-spreadsheet submit event has e.values, e.namedValues, and e.range.
- A trigger runs as the account that created it, and that account normally gets the failure emails. Installable triggers created by an add-on are the exception.
- Check ScriptApp.getProjectTriggers before installing, so a setup function run twice does not double the work.
Keep going
Put it to work
- Task guide Run an Apps Script function every day
Make a function run on its own once a day. Run the setup function once, and from then on Apps Script calls the reminder function each day around the hour you chose.
- 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 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.
- Task guide Email yourself a weekly sheet summary
Get a short email every Friday with the numbers from an Orders tab: how many rows it has, the total of column C, and how many rows are not Done. Run the setup function once to install the weekly trigger.
- Basics Run your first Apps Script function
Your first script, from opening the editor to reading its output. You replace the starter function, save, click Run, and read what Logger.log printed in the Execution log.
- Basics Apps Script permissions and authorization
Before a script can read your sheet or send your mail, you give it permission to access a list of OAuth scopes. This page walks through each authorization screen, explains @OnlyCurrentDoc, and shows how to revoke access.
- Basics Execution log, Logger.log, and debugging
Most Apps Script debugging is printing a value with Logger.log or console.log and reading the error line. This page shows where the output goes and how to trace a failure.
- Diagnostic TypeError: e.range is not a function
Why calling e.range() fails in an onEdit trigger: e.range is already a Range property on the event object.
- Book chapter Install and remove time-driven triggers
Install one hourly trigger for a request tracker, check for an existing trigger first so none is duplicated, then remove it.