To prevent context switching when users share a link in Google Chat, your Chat app can preview the link by attaching a card to their message that gives more information and lets people take action right from Google Chat.
In Google Chat, add-ons appear to users as Google Chat apps. To learn more, see the Extend Google Chat overview.
For example, imagine a Google Chat space that includes all of a company's customer service agents plus a Chat app named Case-y. Agents frequently share links to customer service cases in the Chat space, and each time they do their colleagues must open the case link to see details like assignee, status, and subject. Likewise, if someone wants to take ownership of a case or change the status, then they need to open the link.
Link previewing enables the space's resident Chat app, Case-y, to attach a card showing assignee, status, and subject whenever someone shares a case link. Buttons on the card allow agents to take ownership of the case and change the status directly from the chat stream.
How link previewing works
When someone adds a link to their message, a chip appears which lets them know that a Chat app might preview the link.
After sending the message, the link is sent to the Chat app, which then generates and attaches the card to the user's message.
Alongside the link, the card provides additional information about the link, including interactive elements like buttons. Your Chat app can update the attached card in response to user interactions, like button clicks.
If someone doesn't want the Chat app to preview their link by attaching a card to their message, they can prevent previewing by clicking
on the preview chip. Users can remove the attached card at any time by clicking Remove preview.Prerequisites
Node.js
A Google Workspace Add-on that extends Google Chat. To build one, complete the HTTP quickstart.
Apps Script
A Google Workspace Add-on that extends Google Chat. To build one, complete the Apps Script quickstart.
Configure link previews
Register specific links - like example.com
, support.example.com
, and
support.example.com/cases/
- as URL patterns on your
Chat app's configuration page in Google Cloud console so
your Chat app can preview them.
- Open the Google Cloud console.
- Next to "Google Cloud," click the Down arrow and open your Chat app's project.
- In the search field, type
Google Chat API
and click Google Chat API. - Click Manage > Configuration.
- Under Link previews, add or edit a URL pattern.
- To configure link previews for a new URL pattern, click Add URL Pattern.
- To edit the configuration for an existing URL pattern, click the Down arrow .
In the Host pattern field, enter the domain of the URL pattern. The Chat app will preview links to this domain.
To have the Chat app preview links for a specific subdomain, like
subdomain.example.com
, include the subdomain.To have the Chat app preview links for the entire domain, specify a wildcard character with an asterisk (*) as the subdomain. For example,
*.example.com
matchessubdomain.example.com
andany.number.of.subdomains.example.com
.In the Path prefix field, enter a path to append to the host pattern domain.
To match all URLs in the host pattern domain, leave Path prefix empty.
For example, if the Host pattern is
support.example.com
, to match URLs for cases hosted atsupport.example.com/cases/
, entercases/
.Click Done.
Click Save.
Now, whenever someone includes a link that matches a link preview URL pattern to a message in a Chat space that includes your Chat app, your app previews the link.
Preview a link
After you configure link previewing for a given link, your Chat app can recognize and preview the link by attaching more information to it.
Inside Chat spaces that include your
Chat app, when someone's message contains a link that
matches a link preview URL pattern, your Chat app
receives an event object with a
MessagePayload
. In the payload, the
message.matchedUrl
object contains the link that the user included in the message:
JSON
message: {
matchedUrl: {
url: "https://support.example.com/cases/case123"
},
... // other message attributes redacted
}
By checking for the presence of the matchedUrl
field in the MESSAGE
event
payload, your Chat app can add information to the
message with the previewed link. Your Chat app can
either reply with a basic text message or attach a card.
Reply with a text message
For basic responses, your Chat app can preview a link by replying with a text message to a link. This example attaches a message that repeats the link URL that matches a link preview URL pattern.
Node.js
/**
* Google Cloud Function that handles messages that have links whose
* URLs match URL patterns configured for link previewing.
*
*
* @param {Object} req Request sent from Google Chat space
* @param {Object} res Response to send back
*/
exports.previewLinks = function previewLinks(req, res) {
const chatEvent = req.body.chat;
// Handle MESSAGE events
if(chatEvent.messagePayload) {
return res.send(handlePreviewLink(chatEvent.messagePayload.message));
// Handle button clicks
} else if(chatEvent.buttonClickedPayload) {
return res.send(handleCardClick(chatEvent.buttonClickedPayload.message));
}
};
/**
* Respond to messages that have links whose URLs match URL patterns configured
* for link previewing.
*
* @param {Object} chatMessage The chat message object from Google Workspace Add On event.
* @return {Object} Response to send back depending on the matched URL.
*/
function handlePreviewLink(chatMessage) {
// If the Chat app does not detect a link preview URL pattern, reply
// with a text message that says so.
if (!chatMessage.matchedUrl) {
return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
text: 'No matchedUrl detected.'
}}}}};
}
// Reply with a text message for URLs of the subdomain "text"
if (chatMessage.matchedUrl.url.includes("text.example.com")) {
return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
text: 'event.chat.messagePayload.message.matchedUrl.url: ' + chatMessage.matchedUrl.url
}}}}};
}
}
Apps Script
/**
* Reply to messages that have links whose URLs match the pattern
* "text.example.com" configured for link previewing.
*
* @param {Object} event The event object from Google Workspace Add-on.
*
* @return {Object} The action response.
*/
function onMessage(event) {
// Stores the Google Chat event as a variable.
const chatMessage = event.chat.messagePayload.message;
// If the Chat app doesn't detect a link preview URL pattern, reply
// with a text message that says so.
if (!chatMessage.matchedUrl) {
return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
text: 'No matchedUrl detected.'
}}}}};
}
// Reply with a text message for URLs of the subdomain "text".
if (chatMessage.matchedUrl.url.includes("text.example.com")) {
return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
text: 'event.chat.messagePayload.message.matchedUrl.url: ' + chatMessage.matchedUrl.url
}}}}};
}
}
Attach a card that previews the link
To attach a card to a previewed link,
return the action DataActions
with
the ChatDataActionMarkup
object of type
UpdateInlinePreviewAction
.
In the following example, a Chat app adds a preview
card to messages that contain the URL pattern support.example.com
.
Node.js
/**
* Google Cloud Function that handles messages that have links whose
* URLs match URL patterns configured for link previewing.
*
*
* @param {Object} req Request sent from Google Chat space
* @param {Object} res Response to send back
*/
exports.previewLinks = function previewLinks(req, res) {
const chatEvent = req.body.chat;
// Handle MESSAGE events
if(chatEvent.messagePayload) {
return res.send(handlePreviewLink(chatEvent.messagePayload.message));
// Handle button clicks
} else if(chatEvent.buttonClickedPayload) {
return res.send(handleCardClick(chatEvent.buttonClickedPayload.message));
}
};
/**
* Respond to messages that have links whose URLs match URL patterns configured
* for link previewing.
*
* @param {Object} chatMessage The chat message object from Google Workspace Add On event.
* @return {Object} Response to send back depending on the matched URL.
*/
function handlePreviewLink(chatMessage) {
// Attach a card to the message for URLs of the subdomain "support"
if (chatMessage.matchedUrl.url.includes("support.example.com")) {
// A hard-coded card is used in this example. In a real-life scenario,
// the case information would be fetched and used to build the card.
return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: { cardsV2: [{
cardId: 'attachCard',
card: {
header: {
title: 'Example Customer Service Case',
subtitle: 'Case basics',
},
sections: [{ widgets: [
{ decoratedText: { topLabel: 'Case ID', text: 'case123'}},
{ decoratedText: { topLabel: 'Assignee', text: 'Charlie'}},
{ decoratedText: { topLabel: 'Status', text: 'Open'}},
{ decoratedText: { topLabel: 'Subject', text: 'It won\'t turn on...' }},
{ buttonList: { buttons: [{
text: 'OPEN CASE',
onClick: { openLink: {
url: 'https://support.example.com/orders/case123'
}},
}, {
text: 'RESOLVE CASE',
onClick: { openLink: {
url: 'https://support.example.com/orders/case123?resolved=y',
}},
}, {
text: 'ASSIGN TO ME',
// Use runtime environment variable set with self URL
onClick: { action: { function: process.env.BASE_URL }}
}]}}
]}]
}
}]}}}};
}
}
Apps Script
This example sends a card message by returning card JSON. You can also use the Apps Script card service.
/**
* Attach a card to messages that have links whose URLs match the pattern
* "support.example.com" configured for link previewing.
*
* @param {Object} event The event object from Google Workspace Add-on.
*
* @return {Object} The action response.
*/
function onMessage(event) {
// Stores the Google Chat event as a variable.
const chatMessage = event.chat.messagePayload.message;
// Attach a card to the message for URLs of the subdomain "support".
if (chatMessage.matchedUrl.url.includes("support.example.com")) {
// A hard-coded card is used in this example. In a real-life scenario,
// the case information would be fetched and used to build the card.
return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: { cardsV2: [{
cardId: 'attachCard',
card: {
header: {
title: 'Example Customer Service Case',
subtitle: 'Case summary',
},
sections: [{ widgets: [
{ decoratedText: { topLabel: 'Case ID', text: 'case123'}},
{ decoratedText: { topLabel: 'Assignee', text: 'Charlie'}},
{ decoratedText: { topLabel: 'Status', text: 'Open'}},
{ decoratedText: { topLabel: 'Subject', text: 'It won\'t turn on...' }},
{ buttonList: { buttons: [{
text: 'OPEN CASE',
onClick: { openLink: {
url: 'https://support.example.com/orders/case123'
}},
}, {
text: 'RESOLVE CASE',
onClick: { openLink: {
url: 'https://support.example.com/orders/case123?resolved=y',
}},
}, {
text: 'ASSIGN TO ME',
// Clicking this button triggers the execution of the function
// "assign" from the Apps Script project.
onClick: { action: { function: 'assign'}}
}]}}
]}]
}
}]}}}};
}
}
Update a link preview card
Your Chat app can update a link preview card when users interact with it, such as clicking a button on the card.
To update the card, your Chat app
must return the action DataActions
with
one of the following ChatDataActionMarkup
objects:
- If a user sent the message, return an
UpdateMessageAction
object. - If the Chat app sent the message, return an
UpdateInlinePreviewAction
object.
To determine who sent the message, use the event payload
(buttonClickedPayload
)
to check whether the sender (message.sender.type
) is set to HUMAN
(user) or
BOT
(Chat app).
The following example shows how a Chat app updates a link preview whenever a user clicks the Assign to Me button by updating the card's Assignee field and disabling the button.
Node.js
/**
* Google Cloud Function that handles messages that have links whose
* URLs match URL patterns configured for link previewing.
*
*
* @param {Object} req Request sent from Google Chat space
* @param {Object} res Response to send back
*/
exports.previewLinks = function previewLinks(req, res) {
const chatEvent = req.body.chat;
// Handle MESSAGE events
if(chatEvent.messagePayload) {
return res.send(handlePreviewLink(chatEvent.messagePayload.message));
// Handle button clicks
} else if(chatEvent.buttonClickedPayload) {
return res.send(handleCardClick(chatEvent.buttonClickedPayload.message));
}
};
/**
* Respond to clicks by assigning user and updating the card that was attached to a
* message with a previewed link.
*
* @param {Object} chatMessage The chat message object from Google Workspace Add On event.
* @return {Object} Action response depending on the original message.
*/
function handleCardClick(chatMessage) {
// Creates the updated card that displays "You" for the assignee
// and that disables the button.
//
// A hard-coded card is used in this example. In a real-life scenario,
// an actual assign action would be performed before building the card.
const message = { cardsV2: [{
cardId: 'attachCard',
card: {
header: {
title: 'Example Customer Service Case',
subtitle: 'Case basics',
},
sections: [{ widgets: [
{ decoratedText: { topLabel: 'Case ID', text: 'case123'}},
// The assignee is now "You"
{ decoratedText: { topLabel: 'Assignee', text: 'You'}},
{ decoratedText: { topLabel: 'Status', text: 'Open'}},
{ decoratedText: { topLabel: 'Subject', text: 'It won\'t turn on...' }},
{ buttonList: { buttons: [{
text: 'OPEN CASE',
onClick: { openLink: {
url: 'https://support.example.com/orders/case123'
}},
}, {
text: 'RESOLVE CASE',
onClick: { openLink: {
url: 'https://support.example.com/orders/case123?resolved=y',
}},
}, {
text: 'ASSIGN TO ME',
// The button is now disabled
disabled: true,
// Use runtime environment variable set with self URL
onClick: { action: { function: process.env.BASE_URL }}
}]}}
]}]
}
}]};
// Checks whether the message event originated from a human or a Chat app
// to return the adequate action response.
if(chatMessage.sender.type === 'HUMAN') {
return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: message }}};
} else {
return { hostAppDataAction: { chatDataAction: { updateMessageAction: message }}};
}
}
Apps Script
This example sends a card message by returning card JSON. You can also use the Apps Script card service.
/**
* Assigns and updates the card that's attached to a message with a
* previewed link of the pattern "support.example.com".
*
* @param {Object} event The event object from the Google Workspace Add-on.
*
* @return {Object} Action response depending on the message author.
*/
function assign(event) {
// Creates the updated card that displays "You" for the assignee
// and that disables the button.
//
// A hard-coded card is used in this example. In a real-life scenario,
// an actual assign action would be performed before building the card.
const message = { cardsV2: [{
cardId: 'attachCard',
card: {
header: {
title: 'Example Customer Service Case',
subtitle: 'Case summary',
},
sections: [{ widgets: [
{ decoratedText: { topLabel: 'Case ID', text: 'case123'}},
// The assignee is now "You"
{ decoratedText: { topLabel: 'Assignee', text: 'You'}},
{ decoratedText: { topLabel: 'Status', text: 'Open'}},
{ decoratedText: { topLabel: 'Subject', text: 'It won\'t turn on...' }},
{ buttonList: { buttons: [{
text: 'OPEN CASE',
onClick: { openLink: {
url: 'https://support.example.com/orders/case123'
}},
}, {
text: 'RESOLVE CASE',
onClick: { openLink: {
url: 'https://support.example.com/orders/case123?resolved=y',
}},
}, {
text: 'ASSIGN TO ME',
// The button is now disabled
disabled: true,
onClick: { action: { function: 'assign'}}
}]}}
]}]
}
}]};
// Use the adequate action response type. It depends on whether the message
// the preview link card is attached to was created by a human or a Chat app.
if(event.chat.buttonClickedPayload.message.sender.type === 'HUMAN') {
return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: message }}};
} else {
return { hostAppDataAction: { chatDataAction: { updateMessageAction: message }}};
}
}
Limits and considerations
As you configure link previews for your Chat app, take note of these limits and considerations:
- Each Chat app supports link previews for up to 5 URL patterns.
- Chat apps preview one link per message. If multiple previewable links are present in a single message, only the first previewable link previews.
- Chat apps only preview links that begin with
https://
, sohttps://support.example.com/cases/
previews, butsupport.example.com/cases/
does not. - Unless the message includes other information that gets sent to the Chat app, like a slash command, only the link URL is sent to the Chat app by link previews.
- If a user posts the link, a Chat app can only update
the link preview card if users interact with the card, such as with a button
click. You can't call the Chat API's
update()
method on theMessage
resource to update a user's message asynchronously. - Chat apps must preview links for everyone in the space, so
the message must omit the
privateMessageViewer
field.
Debug link previews
As you implement link previews, you might need to debug your Chat app by reading the app's logs. To read the logs, visit the Logs Explorer on Google Cloud console.