Skip to content

Basics

Google Apps Script triggers explained

A Google Apps Script trigger runs a function when an event happens, without pressing Run. Simple triggers like onEdit and onOpen need no setup, while installable triggers run on a schedule or form submit using the OAuth scopes approved by their owner.

1 of 8

What a trigger is

A trigger is a rule that says "when this happens, run that function". The something 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 look alike from the outside. The difference is 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 authorization and thirty-second restrictions below 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.

The file-event handlers need a project with the matching editor context. onInstall belongs to the separate Editor add-on lifecycle.

  • 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.
Build a small menu whenever the spreadsheet opens
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. No GmailApp, no DriveApp, no UrlFetchApp, no CalendarApp. Try it and 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 editor’s identity 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 is a small package of facts about what happened. Which cell, what was typed, which file, which form response.

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.

A simple onEdit that records what changed
/** @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.
  • A Form submit trigger in a Form-bound project has e.response. An On form submit trigger in the responses spreadsheet has e.values, e.namedValues, and e.range, so choose the source that matches your handler.
  • 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.

Install one weekly trigger and refuse to install a second
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.

A trigger is also invisible to other people. The list below only shows 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 revoke the project at https://myaccount.google.com/permissions, your triggers stop. Somebody else has to install their own.

List the triggers you own in this project
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

When a trigger fails

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 needs its own monitored error reporting and support path.

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. Choose the add-on model before copying a trigger recipe.

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; add-on-created installable triggers are an exception.
  • Check ScriptApp.getProjectTriggers before installing, so a setup function run twice does not double the work.

Keep going

Put it to work

All basics