Installable Triggers

Like simple triggers , installable triggers let Apps Script run a function automatically when a certain event, such as opening a document, occurs. Installable triggers, however, offer more flexibility than simple triggers: they can call services that require authorization , they offer several additional types of events including time-driven (clock) triggers, and they can be controlled programmatically. For both simple and installable triggers, Apps Script passes the triggered function an event object that contains information about the context in which the event occurred.

Restrictions

Even though installable triggers offer more flexibility than simple triggers, they are still subject to several restrictions:

  • They don't run if a file is opened in read-only (view or comment) mode. For standalone scripts, users need at least view access to the script file in order for triggers to run properly.
  • Script executions and API requests do not cause triggers to run. For example, calling FormResponse.submit() to submit a new form response does not cause the form's submit trigger to run.

  • Installable triggers always run under the account of the person who created them. For example, if you create an installable open trigger, it runs when your colleague opens the document (if your colleague has edit access), but it runs as your account. This means that if you create a trigger to send an email when a document is opened, the email is always sent from your account, not necessarily the account that opened the document. However, you could create an installable trigger for each account, which would result in one email sent from each account.

  • A given account cannot see triggers installed from a second account, even though the first account can still activate those triggers.

  • Installable triggers are subject to Apps Script trigger quota limits .

Time-driven triggers

A time-driven trigger (also called a clock trigger) is similar to a cron job in Unix. Time-driven triggers let scripts execute at a particular time or on a recurring interval, as frequently as every minute or as infrequently as once per month. (Note that an add-on can use a time-driven trigger once per hour at most.) The time might be slightly randomized—for example, if you create a recurring 9 AM trigger, Apps Script chooses a time between 9 AM and 10 AM, then keeps that timing consistent from day to day so that 24 hours elapse before the trigger fires again.

The following is an example of a Google Chat app that posts a message every minute to every space that the app is in:

  // 
  
 Example 
  
 app 
  
 for 
  
 Google 
  
 Chat 
  
 that 
  
 demonstrates 
  
 app 
 - 
 initiated 
  
 messages 
 // 
  
 by 
  
 spamming 
  
 the 
  
 user 
  
 every 
  
 minute 
 . 
 // 
 // 
  
 This 
  
 app 
  
 makes 
  
 use 
  
 of 
  
 the 
  
 Apps 
  
 Script 
  
 OAuth2 
  
 library 
  
 at 
 : 
 // 
  
 https 
 : 
 // 
 github 
 . 
 com 
 / 
 googlesamples 
 / 
 apps 
 - 
 script 
 - 
 oauth2 
 // 
 // 
  
 Follow 
  
 the 
  
 instructions 
  
 there 
  
 to 
  
 add 
  
 the 
  
 library 
  
 to 
  
 your 
  
 script 
 . 
 // 
  
 When 
  
 added 
  
 to 
  
 a 
  
 space 
 , 
  
 we 
  
 store 
  
 the 
  
 space's 
  
 ID 
  
 in 
  
 ScriptProperties 
 . 
 function 
  
 onAddToSpace 
 ( 
 e 
 ) 
  
 { 
  
 PropertiesService 
 . 
 getScriptProperties 
 () 
  
 . 
 setProperty 
 ( 
 e 
 . 
 space 
 . 
 name 
 , 
  
'' ); 
  
 return 
  
 { 
  
' text 
' : 
  
' Hi 
 ! 
  
 I 
\' ll 
  
 post 
  
 a 
  
 message 
  
 here 
  
 every 
  
 minute 
 . 
 ' 
 + 
  
' Please 
  
 remove 
  
 me 
  
 after 
  
 testing 
  
 or 
  
 I 
\' ll 
  
 keep 
  
 spamming 
  
 you 
 ! 
'  
 }; 
 } 
 // 
  
 When 
  
 removed 
  
 from 
  
 a 
  
 space 
 , 
  
 we 
  
 remove 
  
 the 
  
 space's 
  
 ID 
  
 from 
  
 ScriptProperties 
 . 
 function 
  
 onRemoveFromSpace 
 ( 
 e 
 ) 
  
 { 
  
 PropertiesService 
 . 
 getScriptProperties 
 () 
  
 . 
 deleteProperty 
 ( 
 e 
 . 
 space 
 . 
 name 
 ); 
 } 
 // 
  
 Add 
  
 a 
  
 trigger 
  
 that 
  
 invokes 
  
 this 
  
 function 
  
 every 
  
 minute 
  
 in 
  
 the 
 // 
  
" Edit 
 > 
 Current 
  
 Project's 
  
 Triggers 
"  
 menu 
 . 
  
 When 
  
 it 
  
 runs 
 , 
  
 it 
 // 
  
 posts 
  
 in 
  
 each 
  
 space 
  
 the 
  
 app 
  
 was 
  
 added 
  
 to 
 . 
 function 
  
 onTrigger 
 () 
  
 { 
  
 var 
  
 spaceIds 
  
 = 
  
 PropertiesService 
 . 
 getScriptProperties 
 () 
  
 . 
 getKeys 
 (); 
  
 var 
  
 message 
  
 = 
  
 { 
  
' text 
' : 
  
' Hi 
 ! 
  
 It 
\' s 
  
 now 
 ' 
 + 
  
 ( 
 new 
  
 Date 
 ()) 
  
 }; 
  
 for 
  
 ( 
 var 
  
 i 
  
 = 
  
 0 
 ; 
  
 i 
 < 
 spaceIds 
 . 
 length 
 ; 
  
 ++ 
 i 
 ) 
  
 { 
  
 postMessage 
 ( 
 spaceIds 
 [ 
 i 
 ], 
  
 message 
 ); 
  
 } 
 } 
 var 
  
 SCOPE 
  
 = 
  
' https 
 : 
 // 
 www 
 . 
 googleapis 
 . 
 com 
 / 
 auth 
 / 
 chat 
 . 
 bot 
' ; 
 // 
  
 The 
  
 values 
  
 below 
  
 are 
  
 copied 
  
 from 
  
 the 
  
 JSON 
  
 file 
  
 downloaded 
  
 upon 
 // 
  
 service 
  
 account 
  
 creation 
 . 
 // 
  
 For 
  
 SERVICE_ACCOUNT_PRIVATE_KEY 
 , 
  
 remember 
  
 to 
  
 include 
  
 the 
  
 BEGIN 
  
 and 
  
 END 
  
 lines 
 // 
  
 of 
  
 the 
  
 private 
  
 key 
 var 
  
 SERVICE_ACCOUNT_PRIVATE_KEY 
  
 = 
  
' ... 
' ; 
 var 
  
 SERVICE_ACCOUNT_EMAIL 
  
 = 
  
' service 
 - 
 account 
 @ 
 project 
 - 
 id 
 . 
 iam 
 . 
 gserviceaccount 
 . 
 com 
' ; 
 // 
  
 Posts 
  
 a 
  
 message 
  
 into 
  
 the 
  
 given 
  
 space 
  
 ID 
  
 via 
  
 the 
  
 API 
 , 
  
 using 
 // 
  
 service 
  
 account 
  
 authentication 
 . 
 function 
  
 postMessage 
 ( 
 spaceId 
 , 
  
 message 
 ) 
  
 { 
  
 var 
  
 service 
  
 = 
  
 OAuth2 
 . 
 createService 
 ( 
' chat 
' ) 
  
 . 
 setTokenUrl 
 ( 
' https 
 : 
 // 
 accounts 
 . 
 google 
 . 
 com 
 / 
 o 
 / 
 oauth2 
 / 
 token 
' ) 
  
 . 
 setPrivateKey 
 ( 
 SERVICE_ACCOUNT_PRIVATE_KEY 
 ) 
  
 . 
 setClientId 
 ( 
 SERVICE_ACCOUNT_EMAIL 
 ) 
  
 . 
 setPropertyStore 
 ( 
 PropertiesService 
 . 
 getUserProperties 
 ()) 
  
 . 
 setScope 
 ( 
 SCOPE 
 ); 
  
 if 
  
 ( 
 ! 
 service 
 . 
 hasAccess 
 ()) 
  
 { 
  
 Logger 
 . 
 log 
 ( 
' Authentication 
  
 error 
 : 
  
 % 
 s 
' , 
  
 service 
 . 
 getLastError 
 ()); 
  
 return 
 ; 
  
 } 
  
 var 
  
 url 
  
 = 
  
' https 
 : 
 // 
 chat 
 . 
 googleapis 
 . 
 com 
 / 
 v1 
 / 
'  
 + 
  
 spaceId 
  
 + 
  
' / 
 messages 
' ; 
  
 UrlFetchApp 
 . 
 fetch 
 ( 
 url 
 , 
  
 { 
  
 method 
 : 
  
' post 
' , 
  
 headers 
 : 
  
 { 
  
' Authorization 
' : 
  
' Bearer 
 ' 
 + 
  
 service 
 . 
 getAccessToken 
 () 
  
 }, 
  
 contentType 
 : 
  
' application 
 / 
 json 
' , 
  
 payload 
 : 
  
 JSON 
 . 
 stringify 
 ( 
 message 
 ), 
  
 }); 
 } 
 

Event-driven triggers

Installable event-driven triggers are conceptually similar to simple triggers like onOpen() , but they can respond to additional events, and they behave differently.

For example, the installable open trigger for Google Sheets activates whenever the spreadsheet is opened by any user who has edit access, just like the simple onOpen() trigger. However, the installable version can call services that require authorization . The installable version runs with the authorization of the user who created the trigger, even if another user with edit access opens the spreadsheet.

There are several installable triggers for Google Workspace applications:

  • An installable opentrigger runs when a user opens a spreadsheet, document, or form that they have permission to edit.
  • An installable edittrigger runs when a user modifies a value in a spreadsheet.
  • An installable changetrigger runs when a user modifies the structure of a spreadsheet itself—for example, by adding a new sheet or removing a column.
  • An installable form submittrigger runs when a user responds to a form. There are two versions of the form-submit trigger, one for Google Forms itself and one for Sheets if the form submits to a spreadsheet .
  • An installable calendar eventtrigger runs when a user's calendar events are updated—created, edited, or deleted.

You can use installable triggers in standalone and bound scripts. For example, a standalone script can programmatically create an installable trigger for an arbitrary Google Sheets file by calling TriggerBuilder.forSpreadsheet(key) and passing in the spreadsheet's ID.

Manage triggers manually

To manually create an installable trigger in the script editor, follow these steps:

  1. Open your Apps Script project.
  2. At the left, click Triggers .
  3. At the bottom right, click Add Trigger.
  4. Select and configure the type of trigger you want to create.
  5. Click Save.

Manage triggers programmatically

You can also create and delete triggers programmatically with the Script service . Start by calling ScriptApp.newTrigger(functionName) , which returns a TriggerBuilder .

The following example shows how to create two time-driven triggers—one that fires every 6 hours, and one that fires every Monday at 9 a.m. (in the time zone that your script is set to).

triggers/triggers.gs
 /** 
 * Creates two time-driven triggers. 
 * @see https://developers.google.com/apps-script/guides/triggers/installable#time-driven_triggers 
 */ 
 function 
  
 createTimeDrivenTriggers 
 () 
  
 { 
  
 // Trigger every 6 hours. 
  
 ScriptApp 
 . 
 newTrigger 
 ( 
' myFunction 
' ) 
  
 . 
 timeBased 
 () 
  
 . 
 everyHours 
 ( 
 6 
 ) 
  
 . 
 create 
 (); 
  
 // Trigger every Monday at 09:00. 
  
 ScriptApp 
 . 
 newTrigger 
 ( 
' myFunction 
' ) 
  
 . 
 timeBased 
 () 
  
 . 
 onWeekDay 
 ( 
 ScriptApp 
 . 
 WeekDay 
 . 
 MONDAY 
 ) 
  
 . 
 atHour 
 ( 
 9 
 ) 
  
 . 
 create 
 (); 
 } 

This next example shows how to create an installable open trigger for a spreadsheet. Note that, unlike for a simple onOpen() trigger, the script for the installable trigger does not need to be bound to the spreadsheet. To create this trigger from a standalone script, simply replace SpreadsheetApp.getActive() with a call to SpreadsheetApp.openById(id) .

triggers/triggers.gs
 /** 
 * Creates a trigger for when a spreadsheet opens. 
 * @see https://developers.google.com/apps-script/guides/triggers/installable 
 */ 
 function 
  
 createSpreadsheetOpenTrigger 
 () 
  
 { 
  
 const 
  
 ss 
  
 = 
  
 SpreadsheetApp 
 . 
 getActive 
 (); 
  
 ScriptApp 
 . 
 newTrigger 
 ( 
' myFunction 
' ) 
  
 . 
 forSpreadsheet 
 ( 
 ss 
 ) 
  
 . 
 onOpen 
 () 
  
 . 
 create 
 (); 
 } 

To programmatically modify an existing installable trigger, you must delete it and create a new one. If you have previously stored the ID of a trigger, you can delete it by passing the ID as an argument to the function below.

triggers/triggers.gs
 /** 
 * Deletes a trigger. 
 * @param {string} triggerId The Trigger ID. 
 * @see https://developers.google.com/apps-script/guides/triggers/installable 
 */ 
 function 
  
 deleteTrigger 
 ( 
 triggerId 
 ) 
  
 { 
  
 // Loop over all triggers. 
  
 const 
  
 allTriggers 
  
 = 
  
 ScriptApp 
 . 
 getProjectTriggers 
 (); 
  
 for 
  
 ( 
 let 
  
 index 
  
 = 
  
 0 
 ; 
  
 index 
 < 
 allTriggers 
 . 
 length 
 ; 
  
 index 
 ++) 
  
 { 
  
 // If the current trigger is the correct one, delete it. 
  
 if 
  
 ( 
 allTriggers 
 [ 
 index 
 ]. 
 getUniqueId 
 () 
  
 === 
  
 triggerId 
 ) 
  
 { 
  
 ScriptApp 
 . 
 deleteTrigger 
 ( 
 allTriggers 
 [ 
 index 
 ]); 
  
 break 
 ; 
  
 } 
  
 } 
 } 

Errors in triggers

When an installable trigger fires but the function throws an exception or otherwise fails to run successfully, you don't see an error message on your screen. After all, when a time-driven trigger runs or another user activates your form-submit trigger, you might not even be at your computer.

Instead, Apps Script sends you an email like the following:

From: noreply-apps-scripts-notifications@google.com
Subject: Summary of failures for Google Apps Script
Your script has recently failed to finish successfully.
A summary of the failure(s) is shown below.

The email includes a link to deactivate or reconfigure the trigger. If the script is bound to a Google Sheets, Docs, or Forms file, the email also includes a link to that file. These links let you deactivate the trigger or edit the script to fix the bug.

To review all of the triggers that are associated with your Google Account and deactivate the triggers you no longer need, follow these steps:

  1. Go to script.google.com .
  2. At the left, click My Triggers.
  3. To delete a trigger, at the right of the trigger, click More > Delete trigger.

Triggers in add-ons

In addition to installable triggers, you can use manifest triggers in add-ons. For more information, see Triggers for Google Workspace Add-ons .