Open interactive dialogs

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's enabled for interactive features. To create an interactive Chat app using an HTTP service, complete this quickstart.

Python

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

Open a dialog

A dialog featuring a variety of different widgets.
Figure 1: A dialog that collects contact information.

This section explains how to respond and set up a dialog by doing the following:

  1. Trigger the dialog request from a user interaction.
  2. Handle the request by returning and opening a dialog.
  3. 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 slash 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 slash command. To trigger the request from a slash 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 its interaction to OPEN_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.
Button that triggers a dialog
Figure 2: A Chat app sends a message that prompts users to use the /addContact slash command.
The message also includes a button that users can click to trigger the command.

The following JSON 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:

{
  "buttonList": { "buttons": [{
    "text": "BUTTON_TEXT",
    "onClick": { "action": {
      "function": "FUNCTION_NAME",
      "interaction": "OPEN_DIALOG"
    }}
  }]}
}

Where BUTTON_TEXT is the text that displays in the button and FUNCTION_NAME is the function that runs to open the initial 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 its type set to DIALOG.
  • A dialogAction object. The body field contains the user interface (UI) elements to display in the card, including one or more sections 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 JSON shows how a Chat app returns a response that opens a dialog:

{ "actionResponse": {
  "type": "DIALOG",
  "dialogAction": { "dialog": { "body": { "sections": [{
    "widgets": [{
      WIDGETS,
      { "buttonList": { "buttons": [{
        "text": "BUTTON_TEXT",
        "onClick": {
          "action": {"function": "FUNCTION_NAME"}
        }
      }]}}
    }]
  }]}}}
}}

Where BUTTON_TEXT is the text that displays in the button (such as Next or Submit), WIDGETS represents one or more form input widgets, and FUNCTION_NAME is the function that runs when users click a button.

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.

Your Chat app must handle the interaction event by doing one of the following:

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 load any data that users input from the initial dialog, you must add parameters to the button that opens the next dialog, or pass the original CARD_CLICKED interaction event from the initial dialog. For details, see Transfer data to another card.

In this example, a Chat app opens a dialog that returns a second dialog before submitting. To load the input data, the Chat app passes the CARD_CLICKED interaction event as a parameter for the function that opens the next dialog:

Node.js

// Respond to button clicks on attached cards
if (event.type === "CARD_CLICKED") {

  // Open the first dialog.
  if (event.common.invokedFunction === "openDialog") {
    openDialog(event);
  }

  // Open the second dialog.
  if (event.common.invokedFunction === "openNextDialog") {
    openNextDialog(event);
  }
}

/**
* Opens and starts a dialog that lets users add details about a contact.
*
* @param {object} event the event object from Google Chat.
*
* @return {object} open a dialog.
*/
function openDialog(event) {
  res.json({ "actionResponse": {
    "type": "DIALOG",
    "dialogAction": { "dialog": { "body": { "sections": [{ "widgets": [
      WIDGETS,
      { "buttonList": { "buttons": [{
        "text": "Next",
        "onClick": { "action": {
          "function": "openNextDialog"
        }}
      }]}}
    ]}]}}}
  }});
};

/**
* Opens a second dialog that lets users add more contact details.
*
* @param {object} event the event object from Google Chat.
*
* @return {object} open a dialog.
*/
function openNextDialog(event) {
  res.json({ "actionResponse": {
    "type": "DIALOG",
    "dialogAction": { "dialog": { "body": { "sections": [{ "widgets": [
      WIDGETS,
      {
        "horizontalAlignment": "END",
        "buttonList": { "buttons": [{
          "text": "Submit",
          "onClick": { "action": {
            "function": "submitDialog"
          }}
        }]}
      }
    ]}]}}}
  }});
}

Python

from typing import Any, Mapping

import flask
import functions_framework

@functions_framework.http
def main(req: flask.Request) -> Mapping[str, Any]:
  """Responds to a MESSAGE event in Google Chat that includes the /createContact
     slash command by opening a dialog.

  Args:
      req (flask.Request): the event object from Chat API.

  Returns:
      Mapping[str, Any]: open a Dialog in response to a card's button click.
  """

  if req.method == 'GET':
    return 'Sorry, this function must be called from a Google Chat.'

  request = req.get_json(silent=True)

  if request.get('type') == 'CARD_CLICKED':
    if invoked_function := request.get('common', dict()).get('invokedFunction'):
      if invoked_function == 'open_dialog':
        return open_dialog(request)

      elif invoked_function == 'open_next_dialog':
        return open_dialog(request)

def open_dialog(request: Mapping[str, Any]) -> Mapping[str, Any]:
  """Opens a dialog in Google Chat.

  Args:
      request (Mapping[str, Any]): the event object from Chat API.

  Returns:
      Mapping[str, Any]: open a Dialog in response to a card's button click.
  """
  return { "actionResponse": {
    "type": "DIALOG",
    "dialogAction": { "dialog": { "body": { "sections": [{ "widgets": [
      WIDGETS,
      { "buttonList": { "buttons": [{
        "text": "Next",
        "onClick": { "action": {
          "function": "open_next_dialog"
        }}
      }]}}
    ]}]}}}
  }}

def open_next_dialog(request: Mapping[str, Any]) -> Mapping[str, Any]:
  """Opens a second dialog that lets users add more contact details.

  Args:
      request (Mapping[str, Any]): the event object from Chat API.

  Returns:
      Mapping[str, Any]: open a Dialog in response to a card's button click.
  """
  return { "actionResponse": {
    "type": "DIALOG",
    "dialogAction": { "dialog": { "body": { "sections": [{ "widgets": [
      WIDGETS,
      {
        "horizontalAlignment": "END",
        "buttonList": { "buttons": [{
          "text": "Submit",
          "onClick": { "action": {
            "function": "submit_dialog"
          }}
        }]}
      }
    ]}]}}}
  }}

Apps Script

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

/**
* Responds to a CARD_CLICKED event in Google Chat.
*
* @param {Object} event the event object from Google Chat
*/
function onCardClick(event) {

  // When a user clicks a card, the Chat app checks to see which function to run.
  if (event.common.invokedFunction === "openDialog") {
    return openDialog(event);
  }

  if (event.common.invokedFunction === "openNextDialog") {
    return openNextDialog(event);
  }
}

/**
* Opens and starts a dialog that lets users add details about a contact.
*
* @param {object} event the event object from Google Chat.
*
* @return {object} open a dialog.
*/
function openDialog(event) {
  return { "actionResponse": {
    "type": "DIALOG",
    "dialogAction": { "dialog": { "body": { "sections": [{ "widgets": [
      WIDGETS,
      { "buttonList": { "buttons": [{
        "text": "Next",
        "onClick": { "action": {
          "function": "openNextDialog"
        }}
      }]}}
    ]}]}}}
  }};
}

/**
* Opens a second dialog that lets users add more contact details.
*
* @param {object} event the event object from Google Chat.
*
* @return {object} open a dialog.
*/
function openNextDialog(event) {
  return { "actionResponse": {
    "type": "DIALOG",
    "dialogAction": { "dialog": { "body": { "sections": [{ "widgets": [
      WIDGETS,
      {
        "horizontalAlignment": "END",
        "buttonList": { "buttons": [{
          "text": "Submit",
          "onClick": { "action": {
            "function": "submitDialog"
          }}
        }]}
      }
    ]}]}}}
  }};
}

Where WIDGETS represents one or more form input widgets.

Close the dialog

When users click a button on a dialog, your Chat app receives an interaction event with the following information:

The following sections explain how to validate the data that users input and close the dialog.

Validate user input data and close the dialog

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.

If the user omits a required field, or inputs incorrect values, the Chat app can respond with an error by returning an ActionResponse that has "actionStatus": "ERROR MESSAGE".

The following example checks that a user inputs a value for a widget that accepts strings (stringInputs), such as a textInput widget. If absent, the Chat app returns an error. If present, the Chat app acknowledges dialog submission and closes the dialog:

Node.js

/**
* Checks for a form input error, the absence of
* a "name" value, and returns an error if absent.
* Otherwise, confirms successful receipt of a dialog.
*
* Confirms successful receipt of a dialog.
*
* @param {Object} event the event object from Chat API.
*
* @return {Object} open a Dialog in Google Chat.
*/
function submitDialog(event) {

  // Checks to make sure the user entered a value
  // in a dialog. If no value detected, returns
  // an error message. Any "actionStatus" value other than "OK"
  // gets returned as an error.
  if (event.common.formInputs.WIDGET_NAME.stringInputs.value[0] === "") {
    res.json({
      "actionResponse": {
        "type": "DIALOG",
        "dialogAction": {
          "actionStatus": "ERROR_MESSAGE"
        }
      }
    });

    // Otherwise the Chat app indicates that it received
    // form data from the dialog. An "actionStatus" of "OK" is
    // interpreted as code 200, and the dialog closes.
  } else {
    res.json({
      "actionResponse": {
        "type": "DIALOG",
        "dialogAction": {
          "actionStatus": "OK"
        }
      }
    });
  }
}

Python

def receive_dialog(event: Mapping[str, Any]) -> Mapping[str, Any]:
  """Checks for a form input error, the absence of a "name" value, and returns
     an error if absent. Otherwise, confirms successful receipt of a dialog.

  Args:
      event (Mapping[str, Any]): the event object from Chat API.

  Returns:
      Mapping[str, Any]: the response.
  """

  if common := event.get('common'):
    if form_inputs := common.get('formInputs'):
      if contact_name := form_inputs.get('WIDGET_NAME'):
        if string_inputs := contact_name.get('stringInputs'):
          if name := string_inputs.get('value')[0]:
            return {
              'actionResponse': {
                'type': 'DIALOG',
                'dialogAction': {
                  'actionStatus': 'OK'
                }
              }
            }
          else:
            return {
              'actionResponse': {
                'type': 'DIALOG',
                'dialogAction': {
                  'actionStatus': 'ERROR_MESSAGE'
                }
              }
            }

Apps Script

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

/**
* Checks for a form input error, the absence of
* a "name" value, and returns an error if absent.
* Otherwise, confirms successful receipt of a dialog.
*
* Confirms successful receipt of a dialog.
*
* @param {Object} event the event object from Chat API.
*
* @return {object} open a Dialog in Google Chat.
*/
function submitDialog(event) {

  // Checks to make sure the user entered a value
  // in a dialog. If no value detected, returns
  // an error message. Any "actionStatus" value other than "OK"
  // gets returned as an error.
  if (event.common.formInputs.WIDGET_NAME[""].stringInputs.value[0] === "") {
    return {
      "actionResponse": {
        "type": "DIALOG",
        "dialogAction": {
          "actionStatus": "ERROR_MESSAGE"
        }
      }
    };

    // Otherwise the Chat app indicates that it received
    // form data from the dialog. An "actionStatus" of "OK" is
    // interpreted as code 200, and the dialog closes.
  } else {
    return {
      "actionResponse": {
        "type": "DIALOG",
        "dialogAction": {
          "actionStatus": "OK"
        }
      }
    };
  }
}

In this example, WIDGET_NAME represents the name field of the widget (such as contactName) and the ERROR_MESSAGE represents the content of the error message (such as Don't forget to name your contact). For details about processing input data from widgets, see Receive data from interactive widgets.

Optional: Send a confirmation message

When you close the dialog, you can also send a new message, or update an existing one.

To send a new message, return an ActionResponse object with the type set to NEW_MESSAGE. For example, to close the dialog and send a text message, return the following:

  {
    "actionResponse": {
      "type": "NEW_MESSAGE",
    },
    "text": "Your information has been submitted."
  }

To update a message, return an actionResponse object that contains the updated message and sets the type to one of the following:

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.