Build a homepage for a Google Chat app

This page explains how to build a homepage for direct messages with your Google Chat app. A homepage, referred to as app home in the Google Chat API, is a customizable card interface that appears in the Home tab of direct message spaces between a user and a Chat app.

App home card with two widgets.
Figure 1: An example of a homepage that appears in direct messages with a Chat app.

You can use app home to share tips for interacting with the Chat app or letting users access and use an external service or tool from Chat.


Use the Card Builder to design and preview messaging and user interfaces for Chat apps:

Open the Card Builder

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.

Java

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.

Configure app home for your Chat app

To support app home, you must configure your Chat app to receive APP_HOME interaction events, Your Chat app receives this event whenever a user clicks the Home tab from a direct message with the Chat app.

To update your configuration settings in the Google Cloud console, do the following:

  1. In the Google Cloud console, go to Menu > More products > Google Workspace > Product Library > Google Chat API.

    Go to Google Chat API

  2. Click Manage, and then click the Configuration tab.

  3. Under Interactive features, go to the Functionality section to configure app home:

    1. Select the Receive 1:1 messages checkbox.
    2. Select the Support App Home checkbox.
  4. If your Chat app uses an HTTP service, go to Connection settings and specify an endpoint for the App Home URL field. You can use the same URL that you specified in the App URL field.

  5. Click Save.

Build an app home card

When a user opens app home, your Chat app must handle the APP_HOME interaction event by returning an instance of RenderActions with pushCard navigation and a Card. To create an interactive experience, the card can contain interactive widgets such as buttons or text inputs that the Chat app can process and respond to with additional cards, or a dialog.

In the following example, the Chat app displays an initial app home card that displays the time the card was created and a button. When a user clicks the button, the Chat app returns an updated card that displays the time the updated card was created.

Node.js

node/app-home/index.js
app.post('/', async (req, res) => {
  let event = req.body.chat;

  let body = {};
  if (event.type === 'APP_HOME') {
    // App home is requested
    body = { action: { navigations: [{
      pushCard: getHomeCard()
    }]}}
  } else if (event.type === 'SUBMIT_FORM') {
    // The update button from app home is clicked
    commonEvent = req.body.commonEventObject;
    if (commonEvent && commonEvent.invokedFunction === 'updateAppHome') {
      body = updateAppHome()
    }
  }

  return res.json(body);
});

// Create the app home card
function getHomeCard() {
  return { sections: [{ widgets: [
    { textParagraph: {
      text: "Here is the app home 🏠 It's " + new Date().toTimeString()
    }},
    { buttonList: { buttons: [{
      text: "Update app home",
      onClick: { action: {
        function: "updateAppHome"
      }}
    }]}}
  ]}]};
}

Python

python/app-home/main.py
@app.route('/', methods=['POST'])
def post() -> Mapping[str, Any]:
  """Handle requests from Google Chat

  Returns:
      Mapping[str, Any]: the response
  """
  event = request.get_json()
  match event['chat'].get('type'):

    case 'APP_HOME':
      # App home is requested
      body = { "action": { "navigations": [{
        "pushCard": get_home_card()
      }]}}

    case 'SUBMIT_FORM':
      # The update button from app home is clicked
      event_object = event.get('commonEventObject')
      if event_object is not None:
        if 'update_app_home' == event_object.get('invokedFunction'):
          body = update_app_home()

    case _:
      # Other response types are not supported
      body = {}

  return json.jsonify(body)


def get_home_card() -> Mapping[str, Any]:
  """Create the app home card

  Returns:
      Mapping[str, Any]: the card
  """
  return { "sections": [{ "widgets": [
    { "textParagraph": {
      "text": "Here is the app home 🏠 It's " +
        datetime.datetime.now().isoformat()
    }},
    { "buttonList": { "buttons": [{
      "text": "Update app home",
      "onClick": { "action": {
        "function": "update_app_home"
      }}
    }]}}
  ]}]}

Java

java/app-home/src/main/java/com/google/chat/app/home/App.java
/**
 * Process Google Chat events
 *
 * @param event Event from chat.
 * @return GenericJson
 * @throws Exception
 */
@PostMapping("/")
@ResponseBody
public GenericJson onEvent(@RequestBody JsonNode event) throws Exception {
  switch (event.at("/chat/type").asText()) {
    case "APP_HOME":
      // App home is requested
      GenericJson navigation = new GenericJson();
      navigation.set("pushCard", getHomeCard());

      GenericJson action = new GenericJson();
      action.set("navigations", List.of(navigation));

      GenericJson response = new GenericJson();
      response.set("action", action);
      return response;
    case "SUBMIT_FORM":
      // The update button from app home is clicked
      if (event.at("/commonEventObject/invokedFunction").asText().equals("updateAppHome")) {
        return updateAppHome();
      }
  }

  return new GenericJson();
}

// Create the app home card
GoogleAppsCardV1Card getHomeCard() {
  GoogleAppsCardV1TextParagraph textParagraph = new GoogleAppsCardV1TextParagraph();
  textParagraph.setText("Here is the app home 🏠 It's " + new Date());

  GoogleAppsCardV1Widget textParagraphWidget = new GoogleAppsCardV1Widget();
  textParagraphWidget.setTextParagraph(textParagraph);

  GoogleAppsCardV1Action action = new GoogleAppsCardV1Action();
  action.setFunction("updateAppHome");

  GoogleAppsCardV1OnClick onClick = new GoogleAppsCardV1OnClick();
  onClick.setAction(action);

  GoogleAppsCardV1Button button = new GoogleAppsCardV1Button();
  button.setText("Update app home");
  button.setOnClick(onClick);

  GoogleAppsCardV1ButtonList buttonList = new GoogleAppsCardV1ButtonList();
  buttonList.setButtons(List.of(button));

  GoogleAppsCardV1Widget buttonListWidget = new GoogleAppsCardV1Widget();
  buttonListWidget.setButtonList(buttonList);

  GoogleAppsCardV1Section section = new GoogleAppsCardV1Section();
  section.setWidgets(List.of(textParagraphWidget, buttonListWidget));

  GoogleAppsCardV1Card card = new GoogleAppsCardV1Card();
  card.setSections(List.of(section));

  return card;
}

Apps Script

Implement the onAppHome function that is called after all APP_HOME interaction events:

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

apps-script/app-home/app-home.gs
/**
 * Responds to a APP_HOME event in Google Chat.
 */
function onAppHome() {
  return { action: { navigations: [{
    pushCard: getHomeCard()
  }]}};
}

/**
 * Returns the app home card.
 */
function getHomeCard() {
  return { sections: [{ widgets: [
    { textParagraph: {
      text: "Here is the app home 🏠 It's " + new Date().toTimeString()
    }},
    { buttonList: { buttons: [{
      text: "Update app home",
      onClick: { action: {
        function: "updateAppHome"
      }}
    }]}}
  ]}]};
}

Respond to app home interactions

If your initial app home card contains interactive widgets, such as buttons or selection inputs, your Chat app must handle the related interaction events by returning an instance of RenderActions with updateCard navigation. To learn more about handling interactive widgets, see Process information inputted by users.

In the previous example, the initial app home card included a button. Whenever a user clicks the button, a CARD_CLICKED interaction event triggers the function updateAppHome to refresh the app home card, as shown in the following code:

Node.js

node/app-home/index.js
// Update the app home
function updateAppHome() {
  return { renderActions: { action: { navigations: [{
    updateCard: getHomeCard()
  }]}}}
};

Python

python/app-home/main.py
def update_app_home() -> Mapping[str, Any]:
  """Update the app home

  Returns:
      Mapping[str, Any]: the update card render action
  """
  return { "renderActions": { "action": { "navigations": [{
    "updateCard": get_home_card()
  }]}}}

Java

java/app-home/src/main/java/com/google/chat/app/home/App.java
// Update the app home
GenericJson updateAppHome() {
  GenericJson navigation = new GenericJson();
  navigation.set("updateCard", getHomeCard());

  GenericJson action = new GenericJson();
  action.set("navigations", List.of(navigation));

  GenericJson renderActions = new GenericJson();
  renderActions.set("action", action);

  GenericJson response = new GenericJson();
  response.set("renderActions", renderActions);
  return response;
}

Apps Script

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

apps-script/app-home/app-home.gs
/**
 * Updates the home app.
 */
function updateAppHome() {
  return { renderActions: { action: { navigations: [{
    updateCard: getHomeCard()
  }]}}};
}

Open dialogs

Your Chat app can also respond to interactions in app home by opening dialogs.

A dialog featuring a variety of different widgets.
Figure 3: A dialog that prompts a user to add a contact.

To open a dialog from app home, process the related interaction event by returning renderActions with updateCard navigation that contains a Card object. In the following example, a Chat app responds to a button click from an app home card by processing the CARD_CLICKED interaction event and opening a dialog:

{ renderActions: { action: { navigations: [{ updateCard: { sections: [{
  header: "Add new contact",
  widgets: [{ "textInput": {
    label: "Name",
    type: "SINGLE_LINE",
    name: "contactName"
  }}, { textInput: {
    label: "Address",
    type: "MULTIPLE_LINE",
    name: "address"
  }}, { decoratedText: {
    text: "Add to favorites",
    switchControl: {
      controlType: "SWITCH",
      name: "saveFavorite"
    }
  }}, { decoratedText: {
    text: "Merge with existing contacts",
    switchControl: {
      controlType: "SWITCH",
      name: "mergeContact",
      selected: true
    }
  }}, { buttonList: { buttons: [{
    text: "Next",
    onClick: { action: { function: "openSequentialDialog" }}
  }]}}]
}]}}]}}}

To close a dialog, process the following interaction events:

  • CLOSE_DIALOG: Closes the dialog and returns to the Chat app's initial app home card.
  • CLOSE_DIALOG_AND_EXECUTE: Closes the dialog and refreshes the app home card.

The following code sample uses CLOSE_DIALOG to close a dialog and return to the app home card:

{ renderActions: { action: {
  navigations: [{ endNavigation: { action: "CLOSE_DIALOG" }}]
}}}

To collect information from users, you can also build sequential dialogs. To learn how to build sequential dialogs, see Open and respond to dialogs.