This page explains how to build a Google Workspace Add-on that lets Google Docs users create resources, such as a support case or project task, in a third-party service from within Google Docs.
With a Google Workspace Add-on, you can add your service to the @ menu in Docs. The add-on adds menu items that let users create resources in your service through a form dialog in Docs.
How users create resources
To create a resource in your service from within a Google Docs document, users
type @
in a document and select your service from the @ menu:
When users type @
in a document and select your service, you present them with
a card that includes the form inputs that users need in order to create a
resource. After the user submits the resource creation form, your add-on should
create the resource in your service and generate a URL that points to it.
The add-on inserts a chip into the document for the created resource. When users hold the pointer over this chip, it invokes the add-on's associated link preview trigger. Make sure your add-on inserts chips with link patterns that are supported by your link preview triggers.
Prerequisites
Apps Script
- A Google Workspace Add-on that supports link previews for the link patterns of the resources that users create. To build an add-on with link previews, refer to Preview links with smart chips.
Node.js
- A Google Workspace Add-on that supports link previews for the link patterns of the resources that users create. To build an add-on with link previews, refer to Preview links with smart chips.
Python
- A Google Workspace Add-on that supports link previews for the link patterns of the resources that users create. To build an add-on with link previews, refer to Preview links with smart chips.
Java
- A Google Workspace Add-on that supports link previews for the link patterns of the resources that users create. To build an add-on with link previews, refer to Preview links with smart chips.
Set up resource creation for your add-on
This section explains how to set up resource creation for your add-on, which includes the following steps:
- Configure resource creation in your add-on's deployment resource or manifest file.
- Build the form cards that users need to create resources within your service.
- Handle form submissions so that the function that creates the resource runs when users submit the form.
Configure resource creation
To configure resource creation, specify the following sections and fields in your add-on's deployment resource or manifest file:
Under the
addOns
section in thedocs
field, implement thecreateActionTriggers
trigger that includes arunFunction
. (You define this function in the following section, Build the form cards.)To learn about what fields you can specify in the
createActionTriggers
trigger, see the reference documentation for Apps Script manifest files or deployment resources for other runtimes.In the
oauthScopes
field, add the scopehttps://www.googleapis.com/auth/workspace.linkcreate
so that users can authorize the add-on to create resources. Specifically, this scope allows the add-on to read the information that users submit to the resource creation form and insert a smart chip into the document based on that information.
As an example, see the addons
section of a deployment resource that configures
resource creation for the following support case service:
{
"oauthScopes": [
"https://www.googleapis.com/auth/workspace.linkpreview",
"https://www.googleapis.com/auth/workspace.linkcreate"
],
"addOns": {
"docs": {
"linkPreviewTriggers": [
...
],
"createActionTriggers": [
{
"id": "createCase",
"labelText": "Create support case",
"localizedLabelText": {
"es": "Crear caso de soporte"
},
"runFunction": "createCaseInputCard",
"logoUrl": "https://www.example.com/images/case.png"
},
{
"id": "createHotlist",
"labelText": "Create a hotlist",
"localizedLabelText": {
"es": "Crear una hotlist"
},
"runFunction": "createHotlistInputCard",
"logoUrl": "https://www.example.com/images/hotlist.png"
}
]
}
}
}
In the example, the Google Workspace Add-on lets users create support cases
and hotlists. Each createActionTriggers
trigger must have the following
fields:
- An unique ID
- A text label that appears in the Docs @ menu
- A logo URL pointing to an icon that appears next to the label text in the @ menu
- A callback function that references either an Apps Script function or an HTTP endpoint that returns a card
Build the form cards
To create resources in your service from the Docs @
menu, you must implement any functions
that you specified in the createActionTriggers
object.
When a user interacts with one of your menu items, the corresponding
createActionTriggers
trigger fires and its callback function presents a card
with form inputs for creating the resource.
To create the card interface, you use widgets to display information and inputs that users need in order to create the resource. Most Google Workspace Add-on widgets and actions are supported with the following exceptions:
- Card footers aren't supported.
- For navigations, only the
updateCard
navigation is supported.
Here's an example of an Apps Script callback function that displays a card when a user selects Create support case from the @ menu:
function createCaseInputCard() {
let cardSection1TextInput1 = CardService.newTextInput()
.setFieldName('caseTitle')
.setTitle('Case Title')
.setMultiline(false);
let cardSection1TextInput2 = CardService.newTextInput()
.setFieldName('description')
.setTitle('Description')
.setMultiline(true);
let cardSection1SelectionInput1 = CardService.newSelectionInput()
.setFieldName('priority')
.setTitle('Priority')
.setType(CardService.SelectionInputType.RADIO_BUTTON)
.addItem('P0', 'P0', false)
.addItem('P1', 'P1', false)
.addItem('P2', 'P2', false)
.addItem('P3', 'P3', false);
let cardSection1ButtonList1Button1Action1 = CardService.newAction()
.setFunctionName('submitCaseCreationForm')
.setParameters({});
let cardSection1ButtonList1Button1 = CardService.newTextButton()
.setText('Create case')
.setBackgroundColor('#66b73a')
.setTextButtonStyle(CardService.TextButtonStyle.FILLED)
.setOnClickAction(cardSection1ButtonList1Button1Action1);
let cardSection1ButtonList1 = CardService.newButtonSet()
.addButton(cardSection1ButtonList1Button1);
let cardSection1 = CardService.newCardSection()
.addWidget(cardSection1TextInput1)
.addWidget(cardSection1TextInput2)
.addWidget(cardSection1SelectionInput1)
.addWidget(cardSection1ButtonList1);
let card = CardService.newCardBuilder()
.addSection(cardSection1)
.build();
return card;
}
The createCaseInputCard
function renders the following card:
The card includes text inputs and a selection input. It also has a text button
with anonClick
action that refers to another run function to handle the
submission of this creation form. After the user fills out the form and clicks
Create case, the add-on sends the form fields to theonClick
action
function, at which point the add-on can use them to create the resource in the
third-party service.
Handle form submissions
After a user submits the creation form, the function associated with the
onClick
action runs. For an ideal user experience, your
add-on should handle
both successful and erroneous form submissions.
Handle successful resource creation
The onClick
function of your add-on should create the
resource in your third-party service and generate a URL that points to it.
In order to communicate the resource's URL back to Docs
for chip creation, the onClick
function should return a SubmitFormResponse
with a one-element array in renderActions.action.links
that points to a
link. The link title should represent the title of the created resource and the
URL should point to that resource. If additional elements are returned in the
renderActions.action.links
array, they are ignored.
Refer to the following example of a SubmitFormResponse
for a created resource:
{
"renderActions": {
"action": {
"links": [
{
"url": "http://example.com/resource/123",
"title": "Created Resource Name"
}
]
}
}
}
After the SubmitFormResponse
is returned, the modal dialog closes and the
add-on inserts a chip into the document.
When users hold the pointer over this chip, it invokes the associated link
preview trigger. Make sure your add-on doesn't insert
chips with link patterns not supported by your link preview triggers.
Handle errors
If a user tries to submit a form with invalid fields, instead of returning a
SubmitFormResponse
with a link, the add-on should
return a render action that displays an error above or below the invalid input
fields using an updateCard
navigation. This lets the user see what they did
wrong and try again. See updateCard(card)
for
Apps Script and updateCard
for other runtimes. Notifications and pushCard
navigations aren't supported.
Complete example: Support case add-on
The following example features a Google Workspace Add-on that previews links to a company's support cases and lets users create support cases from within Google Docs.
The example does the following:
- Previews links to support cases, such as
https://www.example.com/support/cases/1234
. The smart chip displays a support icon, and the preview card includes the case ID and a description. - Creates support cases from the @ menu and inserts them into the Docs document as smart chips.
Deployment resource
Apps Script
{
"timeZone": "America/New_York",
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/workspace.linkpreview",
"https://www.googleapis.com/auth/workspace.linkcreate"
],
"addOns": {
"common": {
"name": "Preview and create support cases",
"logoUrl": "https://developers.google.com/workspace/add-ons/images/link-icon.png",
"layoutProperties": {
"primaryColor": "#dd4b39"
}
},
"docs": {
"linkPreviewTriggers": [
{
"runFunction": "caseLinkPreview",
"patterns": [
{
"hostPattern": "example.com",
"pathPrefix": "support/cases"
},
{
"hostPattern": "*.example.com",
"pathPrefix": "cases"
},
{
"hostPattern": "cases.example.com"
}
],
"labelText": "Support case",
"localizedLabelText": {
"es": "Caso de soporte"
},
"logoUrl": "https://developers.google.com/workspace/add-ons/images/support-icon.png"
}
],
"createActionTriggers": [
{
"id": "createCase",
"labelText": "Create support case",
"localizedLabelText": {
"es": "Crear caso de soporte"
},
"runFunction": "createCaseInputCard",
"logoUrl": "https://developers.google.com/workspace/add-ons/images/support-icon.png"
}
]
}
}
}
JSON
{
"oauthScopes": [
"https://www.googleapis.com/auth/workspace.linkpreview",
"https://www.googleapis.com/auth/workspace.linkcreate"
],
"addOns": {
"common": {
"name": "Preview and create support cases",
"logoUrl": "https://developers.google.com/workspace/add-ons/images/link-icon.png",
"layoutProperties": {
"primaryColor": "#dd4b39"
}
},
"docs": {
"linkPreviewTriggers": [
{
"runFunction": "URL",
"patterns": [
{
"hostPattern": "example.com",
"pathPrefix": "support/cases"
},
{
"hostPattern": "*.example.com",
"pathPrefix": "cases"
},
{
"hostPattern": "cases.example.com"
}
],
"labelText": "Support case",
"localizedLabelText": {
"es": "Caso de soporte"
},
"logoUrl": "https://developers.google.com/workspace/add-ons/images/support-icon.png"
}],
"createActionTriggers": [
{
"id": "createCase",
"labelText": "Create support case",
"localizedLabelText": {
"es": "Crear caso de soporte"
},
"runFunction": "URL",
"logoUrl": "https://developers.google.com/workspace/add-ons/images/support-icon.png"
}
]
}
}
}
Code
Apps Script
/**
* Entry point for a support case link preview
*
* @param {!Object} event
* @return {!Card}
*/
// Creates a function that passes an event object as a parameter.
function caseLinkPreview(event) {
// If the event object URL matches a specified pattern for support case links.
if (event.docs.matchedUrl.url) {
// Uses the event object to parse the URL and identify the case ID.
const segments = event.docs.matchedUrl.url.split('/');
const caseId = segments[segments.length - 1];
// Builds a preview card with the case ID, title, and description.
const caseHeader = CardService.newCardHeader()
.setTitle(`Case ${caseId}: Title bar is broken.`);
const caseDescription = CardService.newTextParagraph()
.setText('Customer can\'t view title on mobile device.');
// Returns the card.
// Uses the text from the card's header for the title of the smart chip.
return CardService.newCardBuilder()
.setHeader(caseHeader)
.addSection(CardService.newCardSection().addWidget(caseDescription))
.build();
}
}
/**
* Entry point for creating a support case
*
* @return {!Card}
*/
function createCaseInputCard() {
let cardSection1TextInput1 = CardService.newTextInput()
.setFieldName('caseTitle')
.setTitle('Case title')
.setMultiline(false);
let cardSection1TextInput2 = CardService.newTextInput()
.setFieldName('description')
.setTitle('Description')
.setMultiline(true);
let cardSection1SelectionInput1 = CardService.newSelectionInput()
.setFieldName('priority')
.setTitle('Priority')
.setType(CardService.SelectionInputType.RADIO_BUTTON)
.addItem('P0', 'P0', false)
.addItem('P1', 'P1', false)
.addItem('P2', 'P2', false)
.addItem('P3', 'P3', false);
let cardSection1ButtonList1Button1Action1 = CardService.newAction()
.setFunctionName('submitCaseCreationForm')
.setParameters({});
let cardSection1ButtonList1Button1 = CardService.newTextButton()
.setText('Create case')
.setBackgroundColor('#66b73a')
.setTextButtonStyle(CardService.TextButtonStyle.FILLED)
.setOnClickAction(cardSection1ButtonList1Button1Action1);
let cardSection1ButtonList1 = CardService.newButtonSet()
.addButton(cardSection1ButtonList1Button1);
let cardSection1 = CardService.newCardSection()
.addWidget(cardSection1TextInput1)
.addWidget(cardSection1TextInput2)
.addWidget(cardSection1SelectionInput1)
.addWidget(cardSection1ButtonList1);
let card = CardService.newCardBuilder()
.addSection(cardSection1)
.build();
return card;
}
/**
* Form submission handler
*
* @param {!Object} event
* @return {!Object} The renderActions object
*/
function submitCaseCreationForm(event) {
const caseDetails = {
title: event.formInput.caseTitle,
description: event.formInput.description,
priority: event.formInput.priority,
id: '001'
};
return {
renderActions: {
action: {
links: [
{
url: 'https://example.com/support/cases/' + caseDetails.id,
title: caseDetails.title
}
]
}
}
};
}
Related resources
- Preview links with smart chips
- Test your add-on
- Google Docs deployment resource
- Card interfaces for link previews