Preview links

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.

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.

When someone adds a link to their message, a chip appears which lets them know that a Chat app might preview the link.

Chip indicating that a Chat app might preview a 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.

Chat app previewing a link by attaching a card to the 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 Chat app that's enabled for interactive features. To create an interactive Chat app using an HTTP service, complete this quickstart .

Apps Script

A Google Chat app that's enabled for interactive features. To create an interactive Chat app in Apps Script, complete this quickstart .

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.

Link previews configuration menu

  1. Open the Google Cloud console .
  2. Next to "Google Cloud," click the Down arrow and open your Chat app's project.
  3. In the search field, type Google Chat API and click Google Chat API.
  4. Click Manage > Configuration.
  5. Under Link previews, add or edit a URL pattern.
    1. To configure link previews for a new URL pattern, click Add URL Pattern.
    2. To edit the configuration for an existing URL pattern, click the Down arrow .
  6. In the Host patternfield, 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 matches subdomain.example.com and any.number.of.subdomains.example.com .

  7. In the Path prefixfield, enter a path to append to the host pattern domain.

    To match all URLs in the host pattern domain, leave Path prefixempty.

    For example, if Host pattern is support.example.com , to match URLs for cases hosted at support.example.com/cases/ , enter cases/ .

  8. Click Done.

  9. 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.

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 a MESSAGE interaction event . The JSON payload for the interaction event contains the matchedUrl field:

JSON

 " message 
" : 
  
 { 
  
 . 
  
 . 
  
 . 
  
 // other message attributes redacted 
  
" 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 simple text message or attach a card.

Reply with a text message

For simple responses, your Chat app can preview a link by replying with a simple text message to a link. This example attaches a message that repeats the link URL that matches a link preview URL pattern.

Node.js

node/preview-link/simple-text-message.js
 /** 
 * Responds to messages that have links whose URLs match URL patterns 
 * configured for link previewing. 
 * 
 * @param {Object} req Request sent from Google Chat. 
 * @param {Object} res Response to send back. 
 */ 
 exports 
 . 
 onMessage 
  
 = 
  
 ( 
 req 
 , 
  
 res 
 ) 
  
 = 
>  
 { 
  
 if 
  
 ( 
 req 
 . 
 method 
  
 === 
  
' GET 
'  
 || 
  
 ! 
 req 
 . 
 body 
 . 
 message 
 ) 
  
 { 
  
 return 
  
 res 
 . 
 send 
 ( 
  
' Hello 
 ! 
  
 This 
  
 function 
  
 is 
  
 meant 
  
 to 
  
 be 
  
 used 
  
 in 
  
 a 
  
 Google 
  
 Chat 
  
 Space 
 .'); 
  
 } 
  
 // Checks for the presence of event.message.matchedUrl and responds with a 
  
 // text message if present 
  
 if 
  
 ( 
 req 
 . 
 body 
 . 
 message 
 . 
 matchedUrl 
 ) 
  
 { 
  
 return 
  
 res 
 . 
 json 
 ({ 
  
' text 
' : 
  
' req 
 . 
 body 
 . 
 message 
 . 
 matchedUrl 
 . 
 url 
 : 
 ' 
 + 
  
 req 
 . 
 body 
 . 
 message 
 . 
 matchedUrl 
 . 
 url 
 , 
  
 }); 
  
 } 
  
 // If the Chat app doesn’t detect a link preview URL pattern, it says so. 
  
 return 
  
 res 
 . 
 json 
 ({ 
' text 
' : 
  
' No 
  
 matchedUrl 
  
 detected 
 .'}); 
 }; 

Apps Script

apps-script/preview-link/simple-text-message.gs
 /** 
 * Responds to messages that have links whose URLs match URL patterns 
 * configured for link previewing. 
 * 
 * @param {Object} event The event object from Chat API. 
 * 
 * @return {Object} Response from the Chat app attached to the message with 
 * the previewed link. 
 */ 
 function 
  
 onMessage 
 ( 
 event 
 ) 
  
 { 
  
 // Checks for the presence of event.message.matchedUrl and responds with a 
  
 // text message if present 
  
 if 
  
 ( 
 event 
 . 
 message 
 . 
 matchedUrl 
 ) 
  
 { 
  
 return 
  
 { 
  
' text 
' : 
  
' event 
 . 
 message 
 . 
 matchedUrl 
 . 
 url 
 : 
 ' 
 + 
  
 event 
 . 
 message 
 . 
 matchedUrl 
 . 
 url 
 , 
  
 }; 
  
 } 
  
 // If the Chat app doesn’t detect a link preview URL pattern, it says so. 
  
 return 
  
 { 
' text 
' : 
  
' No 
  
 matchedUrl 
  
 detected 
 .'}; 
 } 

Attach a card

To attach a card to a previewed link, return an ActionResponse of type UPDATE_USER_MESSAGE_CARDS . This example attaches a simple card.

Chat app previewing a link by attaching a card to the message

Node.js

node/preview-link/attach-card.js
 /** 
 * Responds to messages that have links whose URLs match URL patterns 
 * configured for link previewing. 
 * 
 * @param {Object} req Request sent from Google Chat. 
 * @param {Object} res Response to send back. 
 */ 
 exports 
 . 
 onMessage 
  
 = 
  
 ( 
 req 
 , 
  
 res 
 ) 
  
 = 
>  
 { 
  
 if 
  
 ( 
 req 
 . 
 method 
  
 === 
  
' GET 
'  
 || 
  
 ! 
 req 
 . 
 body 
 . 
 message 
 ) 
  
 { 
  
 return 
  
 res 
 . 
 send 
 ( 
  
' Hello 
 ! 
  
 This 
  
 function 
  
 is 
  
 meant 
  
 to 
  
 be 
  
 used 
  
 in 
  
 a 
  
 Google 
  
 Chat 
  
 Space 
 .'); 
  
 } 
  
 // Checks for the presence of event.message.matchedUrl and attaches a card 
  
 // if present 
  
 if 
  
 ( 
 req 
 . 
 body 
 . 
 message 
 . 
 matchedUrl 
 ) 
  
 { 
  
 return 
  
 res 
 . 
 json 
 ({ 
  
' actionResponse 
' : 
  
 { 
' type 
' : 
  
' UPDATE_USER_MESSAGE_CARDS 
' }, 
  
' cardsV2 
' : 
  
 [ 
  
 { 
  
' cardId 
' : 
  
' attachCard 
' , 
  
' card 
' : 
  
 { 
  
' header 
' : 
  
 { 
  
' title 
' : 
  
' Example 
  
 Customer 
  
 Service 
  
 Case 
' , 
  
' subtitle 
' : 
  
' Case 
  
 basics 
' , 
  
 }, 
  
' sections 
' : 
  
 [ 
  
 { 
  
' widgets 
' : 
  
 [ 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Case 
  
 ID 
' , 
  
' content 
' : 
  
' case123 
' }}, 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Assignee 
' , 
  
' content 
' : 
  
' Charlie 
' }}, 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Status 
' , 
  
' content 
' : 
  
' Open 
' }}, 
  
 { 
  
' keyValue 
' : 
  
 { 
  
' topLabel 
' : 
  
' Subject 
' , 
  
' content 
' : 
  
' It 
  
 won"t 
  
 turn 
  
 on 
 ...', 
  
 } 
  
 }, 
  
 ], 
  
 }, 
  
 { 
  
' widgets 
' : 
  
 [ 
  
 { 
  
' buttons 
' : 
  
 [ 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' OPEN 
  
 CASE 
' , 
  
' onClick 
' : 
  
 { 
  
' openLink 
' : 
  
 { 
  
' url 
' : 
  
' https 
 : 
 //support.example.com/orders/case123', 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' RESOLVE 
  
 CASE 
' , 
  
' onClick 
' : 
  
 { 
  
' openLink 
' : 
  
 { 
  
' url 
' : 
  
' https 
 : 
 //support.example.com/orders/case123?resolved=y', 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' ASSIGN 
  
 TO 
  
 ME 
' , 
  
' onClick 
' : 
  
 { 
  
' action 
' : 
  
 { 
  
' actionMethodName 
' : 
  
' assign 
' , 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 ], 
  
 }, 
  
 ], 
  
 }, 
  
 ], 
  
 }, 
  
 }, 
  
 ], 
  
 }); 
  
 } 
  
 // If the Chat app doesn’t detect a link preview URL pattern, it says so. 
  
 return 
  
 res 
 . 
 json 
 ({ 
' text 
' : 
  
' No 
  
 matchedUrl 
  
 detected 
 .'}); 
 }; 

Apps Script

This example sends a card message by returning card JSON . You can also use the Apps Script card service .

apps-script/preview-link/attach-card.gs
 /** 
 * Responds to messages that have links whose URLs match URL patterns 
 * configured for link previewing. 
 * 
 * @param {Object} event The event object from Chat API. 
 * @return {Object} Response from the Chat app attached to the message with 
 * the previewed link. 
 */ 
 function 
  
 onMessage 
 ( 
 event 
 ) 
  
 { 
  
 // Checks for the presence of event.message.matchedUrl and attaches a card 
  
 // if present 
  
 if 
  
 ( 
 event 
 . 
 message 
 . 
 matchedUrl 
 ) 
  
 { 
  
 return 
  
 { 
  
' actionResponse 
' : 
  
 { 
  
' type 
' : 
  
' UPDATE_USER_MESSAGE_CARDS 
' , 
  
 }, 
  
' cardsV2 
' : 
  
 [{ 
  
' cardId 
' : 
  
' attachCard 
' , 
  
' card 
' : 
  
 { 
  
' header 
' : 
  
 { 
  
' title 
' : 
  
' Example 
  
 Customer 
  
 Service 
  
 Case 
' , 
  
' subtitle 
' : 
  
' Case 
  
 basics 
' , 
  
 }, 
  
' sections 
' : 
  
 [{ 
  
' widgets 
' : 
  
 [ 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Case 
  
 ID 
' , 
  
' content 
' : 
  
' case123 
' }}, 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Assignee 
' , 
  
' content 
' : 
  
' Charlie 
' }}, 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Status 
' , 
  
' content 
' : 
  
' Open 
' }}, 
  
 { 
  
' keyValue 
' : 
  
 { 
  
' topLabel 
' : 
  
' Subject 
' , 
  
' content 
' : 
  
' It 
  
 won 
 \ 
' t 
  
 turn 
  
 on 
 ...', 
  
 }, 
  
 }, 
  
 ], 
  
 }, 
  
 { 
  
' widgets 
' : 
  
 [{ 
  
' buttons 
' : 
  
 [ 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' OPEN 
  
 CASE 
' , 
  
' onClick 
' : 
  
 { 
  
' openLink 
' : 
  
 { 
  
' url 
' : 
  
' https 
 : 
 //support.example.com/orders/case123', 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' RESOLVE 
  
 CASE 
' , 
  
' onClick 
' : 
  
 { 
  
' openLink 
' : 
  
 { 
  
' url 
' : 
  
' https 
 : 
 //support.example.com/orders/case123?resolved=y', 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' ASSIGN 
  
 TO 
  
 ME 
' , 
  
' onClick 
' : 
  
 { 
' action 
' : 
  
 { 
' actionMethodName 
' : 
  
' assign 
' }}, 
  
 }, 
  
 }, 
  
 ], 
  
 }], 
  
 }], 
  
 }, 
  
 }], 
  
 }; 
  
 } 
  
 // If the Chat app doesn’t detect a link preview URL pattern, it says so. 
  
 return 
  
 { 
' text 
' : 
  
' No 
  
 matchedUrl 
  
 detected 
 .'}; 
 } 

Update a card

To update the card attached to a previewed link, return an ActionResponse of type UPDATE_USER_MESSAGE_CARDS . Chat apps can only update cards that preview links as a response to a Chat app interaction event . Chat apps can't update these cards by calling Chat API asynchronously.

Link previewing doesn't support returning an ActionResponse of type UPDATE_MESSAGE . Because UPDATE_MESSAGE updates the entire message instead of just the card, it only works if the Chat app created the original message. Link previewing attaches a card to a user-created message, so the Chat app doesn't have permission to update it.

To ensure a function updates both user-created and app-created cards in the Chat stream, dynamically set the ActionResponse based on whether the Chat app or a user created the message.

  • If a user created the message, set the ActionResponse to UPDATE_USER_MESSAGE_CARDS .
  • If a Chat app created the message, set the ActionResponse to UPDATE_MESSAGE .

There are two ways to do this: specifying and checking for a custom actionMethodName as part of the onclick property of the attached card (which identifies the message as user-created) or checking to see if the message was created by a user.

Option 1: Check for actionMethodName

To use actionMethodName to properly handle CARD_CLICKED interaction events on previewed cards, set a custom actionMethodName as part of the onclick property of the attached card:

JSON

  . 
  
 . 
  
 . 
  
 // Preview card details 
 { 
  
" textButton 
" : 
  
 { 
  
" text 
" : 
  
" ASSIGN 
  
 TO 
  
 ME 
" , 
  
" onClick 
" : 
  
 { 
  
 // actionMethodName identifies the button to help determine the 
  
 // appropriate ActionResponse. 
  
" action 
" : 
  
 { 
  
" actionMethodName 
" : 
  
" assign 
" , 
  
 } 
  
 } 
  
 } 
 } 
 . 
  
 . 
  
 . 
  
 // Preview card details 
 

With "actionMethodName": "assign" identifying the button as part of a link preview, it's possible to dynamically return the correct ActionResponse by checking for a matching actionMethodName :

Node.js

node/preview-link/update-card.js
 /** 
 * Responds to messages that have links whose URLs match URL patterns 
 * configured for link previewing. 
 * 
 * @param {Object} req Request sent from Google Chat. 
 * @param {Object} res Response to send back. 
 */ 
 exports 
 . 
 onMessage 
  
 = 
  
 ( 
 req 
 , 
  
 res 
 ) 
  
 = 
>  
 { 
  
 if 
  
 ( 
 req 
 . 
 method 
  
 === 
  
' GET 
'  
 || 
  
 ! 
 req 
 . 
 body 
 . 
 message 
 ) 
  
 { 
  
 return 
  
 res 
 . 
 send 
 ( 
  
' Hello 
 ! 
  
 This 
  
 function 
  
 is 
  
 meant 
  
 to 
  
 be 
  
 used 
  
 in 
  
 a 
  
 Google 
  
 Chat 
  
 Space 
 .'); 
  
 } 
  
 // Respond to button clicks on attached cards 
  
 if 
  
 ( 
 req 
 . 
 body 
 . 
 type 
  
 === 
  
' CARD_CLICKED 
' ) 
  
 { 
  
 // Checks for the presence of "actionMethodName": "assign" and sets 
  
 // actionResponse.type to "UPDATE_USER"MESSAGE_CARDS" if present or 
  
 // "UPDATE_MESSAGE" if absent. 
  
 const 
  
 actionResponseType 
  
 = 
  
 req 
 . 
 body 
 . 
 action 
 . 
 actionMethodName 
  
 === 
  
' assign 
'  
 ? 
  
' UPDATE_USER_MESSAGE_CARDS 
'  
 : 
  
' UPDATE_MESSAGE 
' ; 
  
 if 
  
 ( 
 req 
 . 
 body 
 . 
 action 
 . 
 actionMethodName 
  
 === 
  
' assign 
' ) 
  
 { 
  
 return 
  
 res 
 . 
 json 
 ({ 
  
' actionResponse 
' : 
  
 { 
  
 // Dynamically returns the correct actionResponse type. 
  
' type 
' : 
  
 actionResponseType 
 , 
  
 }, 
  
 // Preview card details 
  
' cardsV2 
' : 
  
 [{}], 
  
 }); 
  
 } 
  
 } 
 }; 

Apps Script

This example sends a card message by returning card JSON . You can also use the Apps Script card service .

apps-script/preview-link/update-card.gs
 /** 
 * Updates a card that was attached to a message with a previewed link. 
 * 
 * @param {Object} event The event object from Chat API. 
 * @return {Object} Response from the Chat app. Either a new card attached to 
 * the message with the previewed link, or an update to an existing card. 
 */ 
 function 
  
 onCardClick 
 ( 
 event 
 ) 
  
 { 
  
 // Checks for the presence of "actionMethodName": "assign" and sets 
  
 // actionResponse.type to "UPDATE_USER"MESSAGE_CARDS" if present or 
  
 // "UPDATE_MESSAGE" if absent. 
  
 const 
  
 actionResponseType 
  
 = 
  
 event 
 . 
 action 
 . 
 actionMethodName 
  
 === 
  
' assign 
'  
 ? 
  
' UPDATE_USER_MESSAGE_CARDS 
'  
 : 
  
' UPDATE_MESSAGE 
' ; 
  
 if 
  
 ( 
 event 
 . 
 action 
 . 
 actionMethodName 
  
 === 
  
' assign 
' ) 
  
 { 
  
 return 
  
 assignCase 
 ( 
 actionResponseType 
 ); 
  
 } 
 } 
 /** 
 * Updates a card to say that "You" are the assignee after clicking the Assign 
 * to Me button. 
 * 
 * @param {String} actionResponseType Which actionResponse the Chat app should 
 * use to update the attached card based on who created the message. 
 * @return {Object} Response from the Chat app. Updates the card attached to 
 * the message with the previewed link. 
 */ 
 function 
  
 assignCase 
 ( 
 actionResponseType 
 ) 
  
 { 
  
 return 
  
 { 
  
' actionResponse 
' : 
  
 { 
  
 // Dynamically returns the correct actionResponse type. 
  
' type 
' : 
  
 actionResponseType 
 , 
  
 }, 
  
 // Preview card details 
  
' cardsV2 
' : 
  
 [{}], 
  
 }; 
 } 

Option 2: Check the sender type

Check to see if message.sender.type is HUMAN or BOT . If HUMAN , set ActionResponse to UPDATE_USER_MESSAGE_CARDS , otherwise set ActionResponse to UPDATE_MESSAGE . Here's how:

Node.js

node/preview-link/sender-type.js
 /** 
 * Responds to messages that have links whose URLs match URL patterns 
 * configured for link previewing. 
 * 
 * @param {Object} req Request sent from Google Chat. 
 * @param {Object} res Response to send back. 
 */ 
 exports 
 . 
 onMessage 
  
 = 
  
 ( 
 req 
 , 
  
 res 
 ) 
  
 = 
>  
 { 
  
 if 
  
 ( 
 req 
 . 
 method 
  
 === 
  
' GET 
'  
 || 
  
 ! 
 req 
 . 
 body 
 . 
 message 
 ) 
  
 { 
  
 return 
  
 res 
 . 
 send 
 ( 
  
' Hello 
 ! 
  
 This 
  
 function 
  
 is 
  
 meant 
  
 to 
  
 be 
  
 used 
  
 in 
  
 a 
  
 Google 
  
 Chat 
  
 Space 
 .'); 
  
 } 
  
 // Respond to button clicks on attached cards 
  
 if 
  
 ( 
 req 
 . 
 body 
 . 
 type 
  
 === 
  
' CARD_CLICKED 
' ) 
  
 { 
  
 // Checks whether the message event originated from a human or a Chat app 
  
 // and sets actionResponse.type to "UPDATE_USER_MESSAGE_CARDS if human or 
  
 // "UPDATE_MESSAGE" if Chat app. 
  
 const 
  
 actionResponseType 
  
 = 
  
 req 
 . 
 body 
 . 
 action 
 . 
 actionMethodName 
  
 === 
  
' HUMAN 
'  
 ? 
  
' UPDATE_USER_MESSAGE_CARDS 
'  
 : 
  
' UPDATE_MESSAGE 
' ; 
  
 return 
  
 res 
 . 
 json 
 ({ 
  
' actionResponse 
' : 
  
 { 
  
 // Dynamically returns the correct actionResponse type. 
  
' type 
' : 
  
 actionResponseType 
 , 
  
 }, 
  
 // Preview card details 
  
' cardsV2 
' : 
  
 [{}], 
  
 }); 
  
 } 
 }; 

Apps Script

This example sends a card message by returning card JSON . You can also use the Apps Script card service .

apps-script/preview-link/sender-type.gs
 /** 
 * Updates a card that was attached to a message with a previewed link. 
 * 
 * @param {Object} event The event object from Chat API. 
 * @return {Object} Response from the Chat app. Either a new card attached to 
 * the message with the previewed link, or an update to an existing card. 
 */ 
 function 
  
 onCardClick 
 ( 
 event 
 ) 
  
 { 
  
 // Checks whether the message event originated from a human or a Chat app 
  
 // and sets actionResponse.type to "UPDATE_USER_MESSAGE_CARDS if human or 
  
 // "UPDATE_MESSAGE" if Chat app. 
  
 const 
  
 actionResponseType 
  
 = 
  
 event 
 . 
 message 
 . 
 sender 
 . 
 type 
  
 === 
  
' HUMAN 
'  
 ? 
  
' UPDATE_USER_MESSAGE_CARDS 
'  
 : 
  
' UPDATE_MESSAGE 
' ; 
  
 return 
  
 assignCase 
 ( 
 actionResponseType 
 ); 
 } 
 /** 
 * Updates a card to say that "You" are the assignee after clicking the Assign 
 * to Me button. 
 * 
 * @param {String} actionResponseType Which actionResponse the Chat app should 
 * use to update the attached card based on who created the message. 
 * @return {Object} Response from the Chat app. Updates the card attached to 
 * the message with the previewed link. 
 */ 
 function 
  
 assignCase 
 ( 
 actionResponseType 
 ) 
  
 { 
  
 return 
  
 { 
  
' actionResponse 
' : 
  
 { 
  
 // Dynamically returns the correct actionResponse type. 
  
' type 
' : 
  
 actionResponseType 
 , 
  
 }, 
  
 // Preview card details 
  
' cardsV2 
' : 
  
 [{}], 
  
 }; 
 } 

A typical reason to update a card is in response to a button click. Recall the Assign to Mebutton from the prior section, Attach a card . The following complete example updates the card so that it says that it's assigned to "You" after a user clicks Assign to Me. The example dynamically sets ActionResponse by checking the sender type.

Complete example: Case-y the customer service Chat app

Here's the complete code for Case-y, a Chat app that previews links to cases shared in a Chat space that customer service agents collaborate in.

Node.js

node/preview-link/preview-link.js
 /** 
 * Responds to messages that have links whose URLs match URL patterns 
 * configured for link previewing. 
 * 
 * @param {Object} req Request sent from Google Chat. 
 * @param {Object} res Response to send back. 
 */ 
 exports 
 . 
 onMessage 
  
 = 
  
 ( 
 req 
 , 
  
 res 
 ) 
  
 = 
>  
 { 
  
 if 
  
 ( 
 req 
 . 
 method 
  
 === 
  
' GET 
'  
 || 
  
 ! 
 req 
 . 
 body 
 . 
 message 
 ) 
  
 { 
  
 return 
  
 res 
 . 
 send 
 ( 
  
' Hello 
 ! 
  
 This 
  
 function 
  
 is 
  
 meant 
  
 to 
  
 be 
  
 used 
  
 in 
  
 a 
  
 Google 
  
 Chat 
  
 Space 
 .'); 
  
 } 
  
 // Respond to button clicks on attached cards 
  
 if 
  
 ( 
 req 
 . 
 body 
 . 
 type 
  
 === 
  
' CARD_CLICKED 
' ) 
  
 { 
  
 // Checks whether the message event originated from a human or a Chat app 
  
 // and sets actionResponse.type to "UPDATE_USER_MESSAGE_CARDS if human or 
  
 // "UPDATE_MESSAGE" if Chat app. 
  
 const 
  
 actionResponseType 
  
 = 
  
 req 
 . 
 body 
 . 
 action 
 . 
 actionMethodName 
  
 === 
  
' HUMAN 
'  
 ? 
  
' UPDATE_USER_MESSAGE_CARDS 
'  
 : 
  
' UPDATE_MESSAGE 
' ; 
  
 if 
  
 ( 
 req 
 . 
 body 
 . 
 action 
 . 
 actionMethodName 
  
 === 
  
' assign 
' ) 
  
 { 
  
 return 
  
 res 
 . 
 json 
 ( 
 createMessage 
 ( 
 actionResponseType 
 , 
  
' You 
' )); 
  
 } 
  
 } 
  
 // Checks for the presence of event.message.matchedUrl and attaches a card 
  
 // if present 
  
 if 
  
 ( 
 req 
 . 
 body 
 . 
 message 
 . 
 matchedUrl 
 ) 
  
 { 
  
 return 
  
 res 
 . 
 json 
 ( 
 createMessage 
 ()); 
  
 } 
  
 // If the Chat app doesn’t detect a link preview URL pattern, it says so. 
  
 return 
  
 res 
 . 
 json 
 ({ 
' text 
' : 
  
' No 
  
 matchedUrl 
  
 detected 
 .'}); 
 }; 
 /** 
 * Message to create a card with the correct response type and assignee. 
 * 
 * @param {string} actionResponseType 
 * @param {string} assignee 
 * @return {Object} a card with URL preview 
 */ 
 function 
  
 createMessage 
 ( 
  
 actionResponseType 
  
 = 
  
' UPDATE_USER_MESSAGE_CARDS 
' , 
  
 assignee 
  
 = 
  
' Charlie 
' ) 
  
 { 
  
 return 
  
 { 
  
' actionResponse 
' : 
  
 { 
' type 
' : 
  
 actionResponseType 
 }, 
  
' cardsV2 
' : 
  
 [ 
  
 { 
  
' cardId 
' : 
  
' previewLink 
' , 
  
' card 
' : 
  
 { 
  
' header 
' : 
  
 { 
  
' title 
' : 
  
' Example 
  
 Customer 
  
 Service 
  
 Case 
' , 
  
' subtitle 
' : 
  
' Case 
  
 basics 
' , 
  
 }, 
  
' sections 
' : 
  
 [ 
  
 { 
  
' widgets 
' : 
  
 [ 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Case 
  
 ID 
' , 
  
' content 
' : 
  
' case123 
' }}, 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Assignee 
' , 
  
' content 
' : 
  
 assignee 
 }}, 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Status 
' , 
  
' content 
' : 
  
' Open 
' }}, 
  
 { 
  
' keyValue 
' : 
  
 { 
  
' topLabel 
' : 
  
' Subject 
' , 
  
' content 
' : 
  
' It 
  
 won"t 
  
 turn 
  
 on 
 ...', 
  
 }, 
  
 }, 
  
 ], 
  
 }, 
  
 { 
  
' widgets 
' : 
  
 [ 
  
 { 
  
' buttons 
' : 
  
 [ 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' OPEN 
  
 CASE 
' , 
  
' onClick 
' : 
  
 { 
  
' openLink 
' : 
  
 { 
  
' url 
' : 
  
' https 
 : 
 //support.example.com/orders/case123', 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' RESOLVE 
  
 CASE 
' , 
  
' onClick 
' : 
  
 { 
  
' openLink 
' : 
  
 { 
  
' url 
' : 
  
' https 
 : 
 //support.example.com/orders/case123?resolved=y', 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' ASSIGN 
  
 TO 
  
 ME 
' , 
  
' onClick 
' : 
  
 { 
  
' action 
' : 
  
 { 
  
' actionMethodName 
' : 
  
' assign 
' , 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 ], 
  
 }, 
  
 ], 
  
 }, 
  
 ], 
  
 } 
  
 }, 
  
 ], 
  
 }; 
 } 

Apps Script

This example sends a card message by returning card JSON . You can also use the Apps Script card service .

apps-script/preview-link/preview-link.gs
 /** 
 * Responds to messages that have links whose URLs match URL patterns 
 * configured for link previews. 
 * 
 * @param {Object} event The event object from Chat API. 
 * @return {Object} Response from the Chat app attached to the message with 
 * the previewed link. 
 */ 
 function 
  
 onMessage 
 ( 
 event 
 ) 
  
 { 
  
 // Checks for the presence of event.message.matchedUrl and attaches a card 
  
 // if present 
  
 if 
  
 ( 
 event 
 . 
 message 
 . 
 matchedUrl 
 ) 
  
 { 
  
 return 
  
 { 
  
' actionResponse 
' : 
  
 { 
  
' type 
' : 
  
' UPDATE_USER_MESSAGE_CARDS 
' , 
  
 }, 
  
' cardsV2 
' : 
  
 [{ 
  
' cardId 
' : 
  
' previewLink 
' , 
  
' card 
' : 
  
 { 
  
' header 
' : 
  
 { 
  
' title 
' : 
  
' Example 
  
 Customer 
  
 Service 
  
 Case 
' , 
  
' subtitle 
' : 
  
' Case 
  
 basics 
' , 
  
 }, 
  
' sections 
' : 
  
 [{ 
  
' widgets 
' : 
  
 [ 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Case 
  
 ID 
' , 
  
' content 
' : 
  
' case123 
' }}, 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Assignee 
' , 
  
' content 
' : 
  
' Charlie 
' }}, 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Status 
' , 
  
' content 
' : 
  
' Open 
' }}, 
  
 { 
  
' keyValue 
' : 
  
 { 
  
' topLabel 
' : 
  
' Subject 
' , 
  
' content 
' : 
  
' It 
  
 won 
 \ 
' t 
  
 turn 
  
 on 
 ...', 
  
 } 
  
 }, 
  
 ], 
  
 }, 
  
 { 
  
' widgets 
' : 
  
 [{ 
  
' buttons 
' : 
  
 [ 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' OPEN 
  
 CASE 
' , 
  
' onClick 
' : 
  
 { 
  
' openLink 
' : 
  
 { 
  
' url 
' : 
  
' https 
 : 
 //support.example.com/orders/case123', 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' RESOLVE 
  
 CASE 
' , 
  
' onClick 
' : 
  
 { 
  
' openLink 
' : 
  
 { 
  
' url 
' : 
  
' https 
 : 
 //support.example.com/orders/case123?resolved=y', 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' ASSIGN 
  
 TO 
  
 ME 
' , 
  
' onClick 
' : 
  
 { 
' action 
' : 
  
 { 
' actionMethodName 
' : 
  
' assign 
' }} 
  
 }, 
  
 }, 
  
 ], 
  
 }], 
  
 }], 
  
 }, 
  
 }], 
  
 }; 
  
 } 
  
 // If the Chat app doesn’t detect a link preview URL pattern, it says so. 
  
 return 
  
 { 
' text 
' : 
  
' No 
  
 matchedUrl 
  
 detected 
 .'}; 
 } 
 /** 
 * Updates a card that was attached to a message with a previewed link. 
 * 
 * @param {Object} event The event object from Chat API. 
 * @return {Object} Response from the Chat app. Either a new card attached to 
 * the message with the previewed link, or an update to an existing card. 
 */ 
 function 
  
 onCardClick 
 ( 
 event 
 ) 
  
 { 
  
 // Checks whether the message event originated from a human or a Chat app 
  
 // and sets actionResponse to "UPDATE_USER_MESSAGE_CARDS if human or 
  
 // "UPDATE_MESSAGE" if Chat app. 
  
 const 
  
 actionResponseType 
  
 = 
  
 event 
 . 
 message 
 . 
 sender 
 . 
 type 
  
 === 
  
' HUMAN 
'  
 ? 
  
' UPDATE_USER_MESSAGE_CARDS 
'  
 : 
  
' UPDATE_MESSAGE 
' ; 
  
 // To respond to the correct button, checks the button's actionMethodName. 
  
 if 
  
 ( 
 event 
 . 
 action 
 . 
 actionMethodName 
  
 === 
  
' assign 
' ) 
  
 { 
  
 return 
  
 assignCase 
 ( 
 actionResponseType 
 ); 
  
 } 
 } 
 /** 
 * Updates a card to say that "You" are the assignee after clicking the Assign 
 * to Me button. 
 * 
 * @param {String} actionResponseType Which actionResponse the Chat app should 
 * use to update the attached card based on who created the message. 
 * @return {Object} Response from the Chat app. Updates the card attached to 
 * the message with the previewed link. 
 */ 
 function 
  
 assignCase 
 ( 
 actionResponseType 
 ) 
  
 { 
  
 return 
  
 { 
  
' actionResponse 
' : 
  
 { 
  
 // Dynamically returns the correct actionResponse type. 
  
' type 
' : 
  
 actionResponseType 
 , 
  
 }, 
  
' cardsV2 
' : 
  
 [{ 
  
' cardId 
' : 
  
' assignCase 
' , 
  
' card 
' : 
  
 { 
  
' header 
' : 
  
 { 
  
' title 
' : 
  
' Example 
  
 Customer 
  
 Service 
  
 Case 
' , 
  
' subtitle 
' : 
  
' Case 
  
 basics 
' , 
  
 }, 
  
' sections 
' : 
  
 [{ 
  
' widgets 
' : 
  
 [ 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Case 
  
 ID 
' , 
  
' content 
' : 
  
' case123 
' }}, 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Assignee 
' , 
  
' content 
' : 
  
' You 
' }}, 
  
 { 
' keyValue 
' : 
  
 { 
' topLabel 
' : 
  
' Status 
' , 
  
' content 
' : 
  
' Open 
' }}, 
  
 { 
  
' keyValue 
' : 
  
 { 
  
' topLabel 
' : 
  
' Subject 
' , 
  
' content 
' : 
  
' It 
  
 won 
 \ 
' t 
  
 turn 
  
 on 
 ...', 
  
 } 
  
 }, 
  
 ], 
  
 }, 
  
 { 
  
' widgets 
' : 
  
 [{ 
  
' buttons 
' : 
  
 [ 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' OPEN 
  
 CASE 
' , 
  
' onClick 
' : 
  
 { 
  
' openLink 
' : 
  
 { 
  
' url 
' : 
  
' https 
 : 
 //support.example.com/orders/case123', 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' RESOLVE 
  
 CASE 
' , 
  
' onClick 
' : 
  
 { 
  
' openLink 
' : 
  
 { 
  
' url 
' : 
  
' https 
 : 
 //support.example.com/orders/case123?resolved=y', 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 { 
  
' textButton 
' : 
  
 { 
  
' text 
' : 
  
' ASSIGN 
  
 TO 
  
 ME 
' , 
  
' onClick 
' : 
  
 { 
' action 
' : 
  
 { 
' actionMethodName 
' : 
  
' assign 
' }}, 
  
 }, 
  
 }, 
  
 ], 
  
 }], 
  
 }], 
  
 }, 
  
 }], 
  
 }; 
 } 

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:// , so https://support.example.com/cases/ previews, but support.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.
  • Cards attached to previewed links only support an ActionResponse of type UPDATE_USER_MESSAGE_CARDS , and only in response to a Chat app interaction event . Link previews don't support UPDATE_MESSAGE or asynchronous requests to update cards attached to a previewed link via the Chat API . To learn more, see Update a card .
  • Chat apps must preview links for everyone in the space, so the message must omit the privateMessageViewer field.

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.