Support interactive dialogs
Stay organized with collections
Save and categorize content based on your preferences.
AI-generated Key Takeaways
-
Google Chat apps use dialogs, card-based interfaces, to collect information from users in a multi-step process, triggered by user interactions like button clicks.
-
Dialogs are built using cards with sections for headers, widgets (like text fields and buttons), and actions, allowing for dynamic content and user input.
-
Data is transferred between dialog steps using parameters, enabling the app to maintain context and user progress.
-
Upon submission, the app processes the data, potentially sending a confirmation message and closing the dialog, or presenting another dialog for further interaction.
-
Developers can use provided code snippets in Node.js, Python, Java, and Apps Script to implement dialog functionality and handle user interactions within their Google Chat apps.
This page describes how your Chat app can open dialogs to respond to users.
Dialogs are windowed, card-based interfaces that open from a Chat space or message. The dialog and its contents are only visible to the user that opened it.
Chat apps can use dialogs to request and collect information from Chat users, including multi-step forms. For more details on building form inputs, see Collect and process information from users.
Prerequisites
Node.js
A Google Chat app that receives and responds to interaction events. To create an interactive Chat app using an HTTP service, complete this quickstart.Python
A Google Chat app that receives and responds to interaction events. To create an interactive Chat app using an HTTP service, complete this quickstart.Java
A Google Chat app that receives and responds to interaction events. To create an interactive Chat app using an HTTP service, complete this quickstart.Apps Script
A Google Chat app that receives and responds to interaction events. To create an interactive Chat app in Apps Script, complete this quickstart.Open a dialog
This section explains how to respond and set up a dialog by doing the following:
- Trigger the dialog request from a user interaction.
- Handle the request by returning and opening a dialog.
- After users submit information, process the submission by either closing the dialog or returning another dialog.
Trigger a dialog request
A Chat app can only open dialogs to respond to a user interaction, such as a command or a button click from a message in a card.
To respond to users with a dialog, a Chat app must build an interaction that triggers the dialog request, such as the following:
- Respond to a command. To trigger the request from a command, you must check the Opens a dialog checkbox when configuring the command.
- Respond to a button click in a
message ,
either as part of a card or at the bottom of the message. To trigger the
request from a button in a message, you configure the
button's
onClick
action by setting itsinteraction
toOPEN_DIALOG
. - Respond to a button click in a Chat app homepage. To learn about opening dialogs from homepages, see Build a homepage for your Google Chat app.
/addContact
slash command. The message also includes a button that users can click to trigger the command.
The following code sample shows how to trigger a dialog request from a button in
a card message. To open the dialog, the
button.interaction
field is set to OPEN_DIALOG
:
Node.js
buttonList:{buttons:[{ text:"Add Contact", onClick:{action:{ function:"openInitialDialog", interaction:"OPEN_DIALOG" }} }]}
Python
'buttonList': { 'buttons': [{ 'text': "Add Contact", 'onClick': { 'action': { 'function': "openInitialDialog", 'interaction': "OPEN_DIALOG" }} }]}
Java
.setButtonList(newGoogleAppsCardV1ButtonList().setButtons(List.of(newGoogleAppsCardV1Button() .setText("Add Contact") .setOnClick(newGoogleAppsCardV1OnClick().setAction(newGoogleAppsCardV1Action() .setFunction("openInitialDialog") .setInteraction("OPEN_DIALOG"))))))));
Apps Script
This example sends a card message by returning card JSON. You can also use the Apps Script card service.
buttonList:{buttons:[{ text:"Add Contact", onClick:{action:{ function:"openInitialDialog", interaction:"OPEN_DIALOG" }} }]}
Open the initial dialog
When a user triggers a dialog request, your Chat app
receives an interaction event, represented as an
event
type in the
Chat API. If the interaction triggers a dialog request, the event's
dialogEventType
field is set to REQUEST_DIALOG
.
To open a dialog, your Chat app can respond to the
request by returning an
actionResponse
object with the type
set to DIALOG
and
Message
object. To specify the contents of the dialog, you include the following
objects:
- An
actionResponse
object, with itstype
set toDIALOG
. - A
dialogAction
object. Thebody
field contains the user interface (UI) elements to display in the card, including one or moresections
of widgets. To collect information from users, you can specify form input widgets and a button widget. To learn more about designing form inputs, see Collect and process information from users.
The following code sample shows how a Chat app returns a response that opens a dialog:
Node.js
/** * Opens the initial step of the dialog that lets users add contact details. * * @return {Object} a message with an action response to open a dialog. */ functionopenInitialDialog(){ return{actionResponse:{ type:"DIALOG", dialogAction:{dialog:{body:{sections:[{ header:"Add new contact", widgets:CONTACT_FORM_WIDGETS.concat([{ buttonList:{buttons:[{ text:"Review and submit", onClick:{action:{function:"openConfirmation"}} }]} }]) }]}}} }}; }
Python
defopen_initial_dialog() -> dict: """Opens the initial step of the dialog that lets users add contact details.""" return { 'actionResponse': { 'type': "DIALOG", 'dialogAction': { 'dialog': { 'body': { 'sections': [{ 'header': "Add new contact", 'widgets': CONTACT_FORM_WIDGETS + [{ 'buttonList': { 'buttons': [{ 'text': "Review and submit", 'onClick': { 'action': { 'function': "openConfirmation" }} }]} }] }]}}} }}
Java
// Opens the initial step of the dialog that lets users add contact details. MessageopenInitialDialog(){ returnnewMessage().setActionResponse(newActionResponse() .setType("DIALOG") .setDialogAction(newDialogAction().setDialog(newDialog().setBody(newGoogleAppsCardV1Card() .setSections(List.of(newGoogleAppsCardV1Section() .setHeader("Add new contact") .setWidgets(Stream.concat( CONTACT_FORM_WIDGETS.stream(), List.of(newGoogleAppsCardV1Widget() .setButtonList(newGoogleAppsCardV1ButtonList().setButtons(List.of(newGoogleAppsCardV1Button() .setText("Review and submit") .setOnClick(newGoogleAppsCardV1OnClick().setAction(newGoogleAppsCardV1Action() .setFunction("openConfirmation"))))))).stream()).collect(Collectors.toList())))))))); }
Apps Script
This example sends a card message by returning card JSON. You can also use the Apps Script card service.
/** * Opens the initial step of the dialog that lets users add contact details. * * @return {Object} a message with an action response to open a dialog. */ functionopenInitialDialog(){ return{actionResponse:{ type:"DIALOG", dialogAction:{dialog:{body:{sections:[{ header:"Add new contact", widgets:CONTACT_FORM_WIDGETS.concat([{ buttonList:{buttons:[{ text:"Review and submit", onClick:{action:{function:"openConfirmation"}} }]} }]) }]}}} }}; }
Handle the dialog submission
When users click a button that submits a dialog, your
Chat app receives
a CARD_CLICKED
interaction
event where the dialogEventType
is SUBMIT_DIALOG
. To understand how to collect and process the information in
the dialog, see
Collect and process information from Chat users.
Your Chat app must respond to the interaction event by doing one of the following:
- Return another dialog to populate another card or form.
- Close the dialog after validating the data the user submitted, and optionally, send a confirmation message.
Optional: Return another dialog
After users submit the initial dialog, Chat apps can return one or more additional dialogs to help users review information before submitting, complete multi-step forms, or populate form content dynamically.
To process the data that users input, the Chat app
uses the
event.common.formInputs
object. To learn more about retrieving values from input widgets, see
Collect and process information from users.
To keep track of any data that users input from the initial dialog, you must add parameters to the button that opens the next dialog. For details, see Transfer data to another card.
In this example, a Chat app opens an initial dialog that leads to a second dialog for confirmation before submitting:
Node.js
/** * Responds to CARD_CLICKED interaction events in Google Chat. * * @param {Object} event the CARD_CLICKED interaction event from Google Chat. * @return {Object} message responses specific to the dialog handling. */ functiononCardClick(event){ // Initial dialog form page if(event.common.invokedFunction==="openInitialDialog"){ returnopenInitialDialog(); // Confirmation dialog form page }elseif(event.common.invokedFunction==="openConfirmation"){ returnopenConfirmation(event); // Submission dialog form page }elseif(event.common.invokedFunction==="submitForm"){ returnsubmitForm(event); } } /** * Opens the initial step of the dialog that lets users add contact details. * * @return {Object} a message with an action response to open a dialog. */ functionopenInitialDialog(){ return{actionResponse:{ type:"DIALOG", dialogAction:{dialog:{body:{sections:[{ header:"Add new contact", widgets:CONTACT_FORM_WIDGETS.concat([{ buttonList:{buttons:[{ text:"Review and submit", onClick:{action:{function:"openConfirmation"}} }]} }]) }]}}} }}; } /** * Returns the second step as a dialog or card message that lets users confirm details. * * @param {Object} event the interactive event with form inputs. * @return {Object} returns a dialog or private card message. */ functionopenConfirmation(event){ constname=fetchFormValue(event,"contactName")??""; constbirthdate=fetchFormValue(event,"contactBirthdate")??""; consttype=fetchFormValue(event,"contactType")??""; constcardConfirmation={ header:"Your contact", widgets:[{ textParagraph:{text:"Confirm contact information and submit:"}},{ textParagraph:{text:"<b>Name:</b> "+name}},{ textParagraph:{ text:"<b>Birthday:</b> "+convertMillisToDateString(birthdate) }},{ textParagraph:{text:"<b>Type:</b> "+type}},{ buttonList:{buttons:[{ text:"Submit", onClick:{action:{ function:"submitForm", parameters:[{ key:"contactName",value:name},{ key:"contactBirthdate",value:birthdate},{ key:"contactType",value:type }] }} }]} }] }; // Returns a dialog with contact information that the user input. if(event.isDialogEvent){ return{action_response:{ type:"DIALOG", dialogAction:{dialog:{body:{sections:[cardConfirmation]}}} }}; } // Updates existing card message with contact information that the user input. return{ actionResponse:{type:"UPDATE_MESSAGE"}, privateMessageViewer:event.user, cardsV2:[{ card:{sections:[cardConfirmation]} }] } }
Python
defon_card_click(event: dict) -> dict: """Responds to CARD_CLICKED interaction events in Google Chat.""" # Initial dialog form page if "openInitialDialog" == event.get('common').get('invokedFunction'): return open_initial_dialog() # Confirmation dialog form page elif "openConfirmation" == event.get('common').get('invokedFunction'): return open_confirmation(event) # Submission dialog form page elif "submitForm" == event.get('common').get('invokedFunction'): return submit_form(event) defopen_initial_dialog() -> dict: """Opens the initial step of the dialog that lets users add contact details.""" return { 'actionResponse': { 'type': "DIALOG", 'dialogAction': { 'dialog': { 'body': { 'sections': [{ 'header': "Add new contact", 'widgets': CONTACT_FORM_WIDGETS + [{ 'buttonList': { 'buttons': [{ 'text': "Review and submit", 'onClick': { 'action': { 'function': "openConfirmation" }} }]} }] }]}}} }} defopen_confirmation(event: dict) -> dict: """Returns the second step as a dialog or card message that lets users confirm details.""" name = fetch_form_value(event, "contactName") or "" birthdate = fetch_form_value(event, "contactBirthdate") or "" type = fetch_form_value(event, "contactType") or "" card_confirmation = { 'header': "Your contact", 'widgets': [{ 'textParagraph': { 'text': "Confirm contact information and submit:" }}, { 'textParagraph': { 'text': "<b>Name:</b> " + name }}, { 'textParagraph': { 'text': "<b>Birthday:</b> " + convert_millis_to_date_string(birthdate) }}, { 'textParagraph': { 'text': "<b>Type:</b> " + type }}, { 'buttonList': { 'buttons': [{ 'text': "Submit", 'onClick': { 'action': { 'function': "submitForm", 'parameters': [{ 'key': "contactName", 'value': name }, { 'key': "contactBirthdate", 'value': birthdate }, { 'key': "contactType", 'value': type }] }} }]} }] } # Returns a dialog with contact information that the user input. if event.get('isDialogEvent'): return { 'action_response': { 'type': "DIALOG", 'dialogAction': { 'dialog': { 'body': { 'sections': [card_confirmation] }}} }} # Updates existing card message with contact information that the user input. return { 'actionResponse': { 'type': "UPDATE_MESSAGE" }, 'privateMessageViewer': event.get('user'), 'cardsV2': [{ 'card': { 'sections': [card_confirmation] } }] }
Java
// Responds to CARD_CLICKED interaction events in Google Chat. MessageonCardClick(JsonNodeevent){ StringinvokedFunction=event.at("/common/invokedFunction").asText(); // Initial dialog form page if("openInitialDialog".equals(invokedFunction)){ returnopenInitialDialog(); // Confirmation dialog form page }elseif("openConfirmation".equals(invokedFunction)){ returnopenConfirmation(event); // Submission dialog form page }elseif("submitForm".equals(invokedFunction)){ returnsubmitForm(event); } returnnull; } // Opens the initial step of the dialog that lets users add contact details. MessageopenInitialDialog(){ returnnewMessage().setActionResponse(newActionResponse() .setType("DIALOG") .setDialogAction(newDialogAction().setDialog(newDialog().setBody(newGoogleAppsCardV1Card() .setSections(List.of(newGoogleAppsCardV1Section() .setHeader("Add new contact") .setWidgets(Stream.concat( CONTACT_FORM_WIDGETS.stream(), List.of(newGoogleAppsCardV1Widget() .setButtonList(newGoogleAppsCardV1ButtonList().setButtons(List.of(newGoogleAppsCardV1Button() .setText("Review and submit") .setOnClick(newGoogleAppsCardV1OnClick().setAction(newGoogleAppsCardV1Action() .setFunction("openConfirmation"))))))).stream()).collect(Collectors.toList())))))))); } // Returns the second step as a dialog or card message that lets users confirm details. MessageopenConfirmation(JsonNodeevent){ Stringname=fetchFormValue(event,"contactName")!=null? fetchFormValue(event,"contactName"):""; Stringbirthdate=fetchFormValue(event,"contactBirthdate")!=null? fetchFormValue(event,"contactBirthdate"):""; Stringtype=fetchFormValue(event,"contactType")!=null? fetchFormValue(event,"contactType"):""; GoogleAppsCardV1SectioncardConfirmationSection=newGoogleAppsCardV1Section() .setHeader("Your contact") .setWidgets(List.of( newGoogleAppsCardV1Widget().setTextParagraph(newGoogleAppsCardV1TextParagraph() .setText("Confirm contact information and submit:")), newGoogleAppsCardV1Widget().setTextParagraph(newGoogleAppsCardV1TextParagraph() .setText("<b>Name:</b> "+name)), newGoogleAppsCardV1Widget().setTextParagraph(newGoogleAppsCardV1TextParagraph() .setText("<b>Birthday:</b> "+convertMillisToDateString(birthdate))), newGoogleAppsCardV1Widget().setTextParagraph(newGoogleAppsCardV1TextParagraph() .setText("<b>Type:</b> "+type)), newGoogleAppsCardV1Widget().setButtonList(newGoogleAppsCardV1ButtonList().setButtons(List.of(newGoogleAppsCardV1Button() .setText("Submit") .setOnClick(newGoogleAppsCardV1OnClick().setAction(newGoogleAppsCardV1Action() .setFunction("submitForm") .setParameters(List.of( newGoogleAppsCardV1ActionParameter().setKey("contactName").setValue(name), newGoogleAppsCardV1ActionParameter().setKey("contactBirthdate").setValue(birthdate), newGoogleAppsCardV1ActionParameter().setKey("contactType").setValue(type)))))))))); // Returns a dialog with contact information that the user input. if(event.at("/isDialogEvent")!=null && event.at("/isDialogEvent").asBoolean()){ returnnewMessage().setActionResponse(newActionResponse() .setType("DIALOG") .setDialogAction(newDialogAction().setDialog(newDialog().setBody(newGoogleAppsCardV1Card() .setSections(List.of(cardConfirmationSection)))))); } // Updates existing card message with contact information that the user input. returnnewMessage() .setActionResponse(newActionResponse() .setType("UPDATE_MESSAGE")) .setPrivateMessageViewer(newUser().setName(event.at("/user/name").asText())) .setCardsV2(List.of(newCardWithId().setCard(newGoogleAppsCardV1Card() .setSections(List.of(cardConfirmationSection))))); }
Apps Script
This example sends a card message by returning card JSON. You can also use the Apps Script card service.
/** * Responds to CARD_CLICKED interaction events in Google Chat. * * @param {Object} event the CARD_CLICKED interaction event from Google Chat. * @return {Object} message responses specific to the dialog handling. */ functiononCardClick(event){ // Initial dialog form page if(event.common.invokedFunction==="openInitialDialog"){ returnopenInitialDialog(); // Confirmation dialog form page }elseif(event.common.invokedFunction==="openConfirmation"){ returnopenConfirmation(event); // Submission dialog form page }elseif(event.common.invokedFunction==="submitForm"){ returnsubmitForm(event); } } /** * Opens the initial step of the dialog that lets users add contact details. * * @return {Object} a message with an action response to open a dialog. */ functionopenInitialDialog(){ return{actionResponse:{ type:"DIALOG", dialogAction:{dialog:{body:{sections:[{ header:"Add new contact", widgets:CONTACT_FORM_WIDGETS.concat([{ buttonList:{buttons:[{ text:"Review and submit", onClick:{action:{function:"openConfirmation"}} }]} }]) }]}}} }}; } /** * Returns the second step as a dialog or card message that lets users confirm details. * * @param {Object} event the interactive event with form inputs. * @return {Object} returns a dialog or private card message. */ functionopenConfirmation(event){ constname=fetchFormValue(event,"contactName")??""; constbirthdate=fetchFormValue(event,"contactBirthdate")??""; consttype=fetchFormValue(event,"contactType")??""; constcardConfirmation={ header:"Your contact", widgets:[{ textParagraph:{text:"Confirm contact information and submit:"}},{ textParagraph:{text:"<b>Name:</b> "+name}},{ textParagraph:{ text:"<b>Birthday:</b> "+convertMillisToDateString(birthdate) }},{ textParagraph:{text:"<b>Type:</b> "+type}},{ buttonList:{buttons:[{ text:"Submit", onClick:{action:{ function:"submitForm", parameters:[{ key:"contactName",value:name},{ key:"contactBirthdate",value:birthdate},{ key:"contactType",value:type }] }} }]} }] }; // Returns a dialog with contact information that the user input. if(event.isDialogEvent){ return{action_response:{ type:"DIALOG", dialogAction:{dialog:{body:{sections:[cardConfirmation]}}} }}; } // Updates existing card message with contact information that the user input. return{ actionResponse:{type:"UPDATE_MESSAGE"}, privateMessageViewer:event.user, cardsV2:[{ card:{sections:[cardConfirmation]} }] } }
Close the dialog
When users click a button on a dialog, your Chat app executes its associated action and provides the event object with the following information:
eventType
isCARD_CLICKED
.dialogEventType
isSUBMIT_DIALOG
.
The Chat app should return an
ActionResponse
object with its type
set to DIALOG
, and dialogAction
populated. If the
action did not fail then the dialogAction.actionStatus
should be OK
like in
the following example:
Node.js
// The Chat app indicates that it received form data from the dialog or card. // Sends private text message that confirms submission. constconfirmationMessage="✅ "+contactName+" has been added to your contacts."; if(event.dialogEventType==="SUBMIT_DIALOG"){ return{ actionResponse:{ type:"DIALOG", dialogAction:{actionStatus:{ statusCode:"OK", userFacingMessage:"Success "+contactName }} } }; }
Python
# The Chat app indicates that it received form data from the dialog or card. # Sends private text message that confirms submission. confirmation_message = "✅ " + contact_name + " has been added to your contacts."; if "SUBMIT_DIALOG" == event.get('dialogEventType'): return { 'actionResponse': { 'type': "DIALOG", 'dialogAction': { 'actionStatus': { 'statusCode': "OK", 'userFacingMessage': "Success " + contact_name }} } }
Java
// The Chat app indicates that it received form data from the dialog or card. // Sends private text message that confirms submission. StringconfirmationMessage="✅ "+contactName+" has been added to your contacts."; if(event.at("/dialogEventType")!=null && "SUBMIT_DIALOG".equals(event.at("/dialogEventType").asText())){ returnnewMessage().setActionResponse(newActionResponse() .setType("DIALOG") .setDialogAction(newDialogAction().setActionStatus(newActionStatus() .setStatusCode("OK") .setUserFacingMessage("Success "+contactName)))); }
Apps Script
This example sends a card message by returning card JSON. You can also use the Apps Script card service.
// The Chat app indicates that it received form data from the dialog or card. // Sends private text message that confirms submission. constconfirmationMessage="✅ "+contactName+" has been added to your contacts."; if(event.dialogEventType==="SUBMIT_DIALOG"){ return{ actionResponse:{ type:"DIALOG", dialogAction:{actionStatus:{ statusCode:"OK", userFacingMessage:"Success "+contactName }} } }; }
Optional: Display a temporary notification
When you close the dialog, you can also display a temporary text notification to the user that is interacting with the app.
The Chat app can respond with a success or error
notification by returning an
ActionResponse
with actionStatus
set.
The following example checks that parameters are valid and closes the dialog with text notification when invalid:
Node.js
constcontactName=event.common.parameters["contactName"]; // Checks to make sure the user entered a contact name. // If no name value detected, returns an error message. consterrorMessage="Don't forget to name your new contact!"; if(!contactName && event.dialogEventType==="SUBMIT_DIALOG"){ return{actionResponse:{ type:"DIALOG", dialogAction:{actionStatus:{ statusCode:"INVALID_ARGUMENT", userFacingMessage:errorMessage }} }}; }
Python
contact_name = event.get('common').get('parameters')["contactName"] # Checks to make sure the user entered a contact name. # If no name value detected, returns an error message. error_message = "Don't forget to name your new contact!" if contact_name == "" and "SUBMIT_DIALOG" == event.get('dialogEventType'): return { 'actionResponse': { 'type': "DIALOG", 'dialogAction': { 'actionStatus': { 'statusCode': "INVALID_ARGUMENT", 'userFacingMessage': error_message }} }}
Java
StringcontactName=event.at("/common/parameters/contactName").asText(); // Checks to make sure the user entered a contact name. // If no name value detected, returns an error message. StringerrorMessage="Don't forget to name your new contact!"; if(contactName.isEmpty() && event.at("/dialogEventType")!=null && "SUBMIT_DIALOG".equals(event.at("/dialogEventType").asText())){ returnnewMessage().setActionResponse(newActionResponse() .setType("DIALOG") .setDialogAction(newDialogAction().setActionStatus(newActionStatus() .setStatusCode("INVALID_ARGUMENT") .setUserFacingMessage(errorMessage)))); }
Apps Script
This example sends a card message by returning card JSON. You can also use the Apps Script card service.
constcontactName=event.common.parameters["contactName"]; // Checks to make sure the user entered a contact name. // If no name value detected, returns an error message. consterrorMessage="Don't forget to name your new contact!"; if(!contactName && event.dialogEventType==="SUBMIT_DIALOG"){ return{actionResponse:{ type:"DIALOG", dialogAction:{actionStatus:{ statusCode:"INVALID_ARGUMENT", userFacingMessage:errorMessage }} }}; }
For details about passing parameters between dialogs, see Transfer data to another card.
Optional: Send a confirmation Chat message
When you close the dialog, you can also send a new Chat message, or update an existing one.
To send a new message, return an
ActionResponse
object with the type
set to NEW_MESSAGE
. The following example closes the
dialog with confirmation text message:
Node.js
return{ actionResponse:{type:"NEW_MESSAGE"}, privateMessageViewer:event.user, text:confirmationMessage };
Python
return { 'actionResponse': { 'type': "NEW_MESSAGE" }, 'privateMessageViewer': event.get('user'), 'text': confirmation_message }
Java
returnnewMessage() .setActionResponse(newActionResponse().setType("NEW_MESSAGE")) .setPrivateMessageViewer(newUser().setName(event.at("/user/name").asText())) .setText(confirmationMessage);
Apps Script
This example sends a card message by returning card JSON. You can also use the Apps Script card service.
return{ actionResponse:{type:"NEW_MESSAGE"}, privateMessageViewer:event.user, text:confirmationMessage };
To update a message, return an actionResponse
object that contains the
updated message and sets the type
to one of the following:
UPDATE_MESSAGE
: Updates the message that triggered the dialog request.UPDATE_USER_MESSAGE_CARDS
: Updates the card from a link preview.
Troubleshoot
When a Google Chat app or card returns an error, the Chat interface surfaces a message saying "Something went wrong." or "Unable to process your request." Sometimes the Chat UI doesn't display any error message, but the Chat app or card produces an unexpected result; for example, a card message might not appear.
Although an error message might not display in the Chat UI, descriptive error messages and log data are available to help you fix errors when error logging for Chat apps is turned on. For help viewing, debugging, and fixing errors, see Troubleshoot and fix Google Chat errors.
Related topics
- View the Contact Manager sample, which is a Chat app that uses dialogs to collect contact information.
- Open dialogs from a Google Chat app homepage.
- Respond to Google Chat app commands
- Process information inputted by users