Universal actions are menu item elements that allow a user to open a new web page, display new UI cards, or run a specific Apps Script function when selected. In operation they are very similar to card actions , except that universal actions are always placed on every card in your add-on, regardless of the current add-on context.
By using universal actions, you can make sure the user always has access to certain functionality, regardless of which part of your add-on they interact with. Here are some example use cases for universal actions:
- Open a settings web page (or display a settings card).
- Show help information to the user.
- Start a new workflow, such as 'Add new customer'.
- Display a card that lets a user send feedback about the add-on.
Whenever you have an action that does not depend on the current context, you should consider making it a universal action.
Using universal actions
Universal actions are configured in your add-on's project manifest . Once you've configured a universal action, it is always available to users of your add-on. If the user is viewing a card, the set of universal actions you've defined always appears in the card menu, after any card actions you've defined for that card. Universal actions appear in the card menus in the same order in which they are defined in the add-on's manifest.
Configuring universal actions
You configure universal actions in your add-on's manifest; see Manifests for more details.
For each action, you specify the text that should appear in the menu for that
action. You can then specify an openLink
field indicating that the action
should directly open a web page in a new tab. Alternatively, you can specify
a runFunction
field that specifies an Apps Script callback function to
execute when the universal action is selected.
When runFunction
is used, the callback function specified usually does one
of the following:
- Builds UI cards to display immediately by returning a built
UniversalActionResponse
object. - Opens a URL, perhaps after doing other tasks, by returning a built
UniversalActionResponse
object. - Conducts background tasks that do not switch to a new card or open a URL. In this case the callback function returns nothing.
When called, the callback function is passed an event object containing information about the open card and add-on context.
Example
The following code snippet shows an example manifest excerpt for a Google Workspace add-on that uses universal actions while extending Gmail. The code explicitly sets a metadata scope so that the add-on can determine who sent the open message.
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.current.message.metadata"
],
"addOns": {
"common": {
"name": "Universal Actions Only Addon",
"logoUrl": "https://www.example.com/hosted/images/2x/my-icon.png",
"openLinkUrlPrefixes": [
"https://www.google.com",
"https://www.example.com/urlbase"
],
"universalActions": [{
"label": "Open google.com",
"openLink": "https://www.google.com"
}, {
"label": "Open contact URL",
"runFunction": "openContactURL"
}, {
"label": "Open settings",
"runFunction": "createSettingsResponse"
}, {
"label": "Run background sync",
"runFunction": "runBackgroundSync"
}],
...
},
"gmail": {
"contextualTriggers": [
{
"unconditional": {},
"onTriggerFunction": "getContextualAddOn"
}
]
},
...
},
...
The three universal actions defined in preceding example do the following:
- Open google.com opens https://www.google.com in a new tab.
- Open contact URL
runs a function that determines what URL to open
and then opens it in a new tab using an
OpenLink
object. The code builds the URL using the sender's email address. - Open settings
runs the
createSettingsCards()
function defined in the add-on script project. This function returns a validUniversalActionResponse
object containing a set of cards with add-on setting and other information. After the function finishes building this object, the UI displays the list of cards (see Returning multiple cards ). - Run background sync
runs the
runBackgroundSync()
function defined in the add-on script project. This function does not build cards; instead it performs some other background tasks that do not change the UI. Since the function doesn't return aUniversalActionResponse
, the UI does not display a new card when the function finishes. Instead the UI displays a loading indicator spinner while the function is running.
Here is an example of how you might construct the openContactURL()
, createSettingsResponse()
, and runBackgroundSync()
functions:
/**
*
Open
a
contact
URL
.
*
@
param
{
Object
}
e
an
event
object
*
@
return
{
UniversalActionResponse
}
*/
function
openContactURL
(
e
)
{
//
Activate
temporary
Gmail
scopes
,
in
this
case
so
that
the
//
open
message
metadata
can
be
read
.
var
accessToken
=
e
.
gmail
.
accessToken
;
GmailApp
.
setCurrentMessageAccessToken
(
accessToken
);
//
Build
URL
to
open
based
on
a
base
URL
and
the
sender
's email.
//
This
URL
must
be
included
in
the
openLinkUrlPrefixes
whitelist
.
var
messageId
=
e
.
gmail
.
messageId
;
var
message
=
GmailApp
.
getMessageById
(
messageId
);
var
sender
=
message
.
getFrom
();
var
url
=
"https://www.example.com/urlbase/"
+
sender
;
return
CardService
.
newUniversalActionResponseBuilder
()
.
setOpenLink
(
CardService
.
newOpenLink
()
.
setUrl
(
url
))
.
build
();
}
/**
*
Create
a
collection
of
cards
to
control
the
add
-
on
settings
and
*
present
other
information
.
These
cards
are
displayed
in
a
list
when
*
the
user
selects
the
associated
"Open settings"
universal
action
.
*
*
@
param
{
Object
}
e
an
event
object
*
@
return
{
UniversalActionResponse
}
*/
function
createSettingsResponse
(
e
)
{
return
CardService
.
newUniversalActionResponseBuilder
()
.
displayAddOnCards
(
[
createSettingCard
(),
createAboutCard
()])
.
build
();
}
/**
*
Create
and
return
a
built
settings
card
.
*
@
return
{
Card
}
*/
function
createSettingCard
()
{
return
CardService
.
newCardBuilder
()
.
setHeader
(
CardService
.
newCardHeader
()
.
setTitle
(
'Settings'
))
.
addSection
(
CardService
.
newCardSection
()
.
addWidget
(
CardService
.
newSelectionInput
()
.
setType
(
CardService
.
SelectionInputType
.
CHECK_BOX
)
.
addItem
(
"Ask before deleting contact"
,
"contact"
,
false
)
.
addItem
(
"Ask before deleting cache"
,
"cache"
,
false
)
.
addItem
(
"Preserve contact ID after deletion"
,
"contactId"
,
false
))
//
...
continue
adding
widgets
or
other
sections
here
...
)
.
build
();
//
Don
't forget to build the card!
}
/**
*
Create
and
return
a
built
'About'
informational
card
.
*
@
return
{
Card
}
*/
function
createAboutCard
()
{
return
CardService
.
newCardBuilder
()
.
setHeader
(
CardService
.
newCardHeader
()
.
setTitle
(
'About'
))
.
addSection
(
CardService
.
newCardSection
()
.
addWidget
(
CardService
.
newTextParagraph
()
.
setText
(
'This add-on manages contact information. For more '
+
'details see the <a href="https://www.example.com/help">'
+
'help page</a>.'
))
//
...
add
other
information
widgets
or
sections
here
...
)
.
build
();
//
Don
't forget to build the card!
}
/**
*
Run
background
tasks
,
none
of
which
should
alter
the
UI
.
*
Also
records
the
time
of
sync
in
the
script
properties
.
*
*
@
param
{
Object
}
e
an
event
object
*/
function
runBackgroundSync
(
e
)
{
var
props
=
PropertiesService
.
getUserProperties
();
props
.
setProperty
(
"syncTime"
,
new
Date
()
.
toString
());
syncWithContacts
();
//
Not
shown
.
updateCache
();
//
Not
shown
.
validate
();
//
Not
shown
.
//
no
return
value
tells
the
UI
to
keep
showing
the
current
card
.
}