Receive and respond to Google Chat events

This page describes how your Google Chat app can receive, process, and respond to events from Google Chat.

When users interact with Google Chat apps, the app synchronously receives and can respond to an event. Examples of event types include messages (including slash commands and @mentions), card clicks, and getting added to or removed from a space.

Chat apps can respond to these events in many ways. For example, Chat apps can send a text message or card message, each of which is represented as a JSON object.

Text messages

Text messages are perfect for simple notifications. They support @mentions and basic formatting like bold, italics, and code.

For example, an app might use a text message to notify software developers that code freeze is approaching:

Example text message in Google Chat that announces code freeze
Figure 1: A text message alerts a Chat space about a code freeze.

To learn more, see Send a text message.

Card messages

Card messages support a defined layout, interactive UI elements like buttons, and rich media like images. Use card messages to present detailed information, gather information from users, and guide users to take a next step. Card messages can appear in a conversation stream as a message on its own or appended to a text message, or as dialog windows that open over a conversation.

For example, an app might use a card message to run a poll:

Running a poll in a Chat space with a card message
Figure 2: A card message lets people in a Chat space vote in a poll.

To help users complete multi-step processes, like filling in form data, cards can be strung together sequentially in a dialog. Dialogs open in windows that let apps interact with a user directly.

For example, an app might start a dialog to gather contact details:

A dialog featuring a variety of different widgets.
Figure 3: An open dialog prompting a user to add a contact.

To learn more, see Send a card message.

Endpoint types

To receive and respond to Google Chat events, you specify a service endpoint in your Google Chat app's configuration. You can use any of the following endpoint types:

  • HTTPS endpoints present your app as a web service. You must set up a web server to use as an interface for your app's implementation. Your app can respond synchronously or asynchronously to these events.
  • Google Cloud Pub/Sub endpoints use a topic on Google Cloud Pub/Sub to relay an event to your app's implementation. This is useful when your implementation is behind a firewall. Apps that use pub/sub endpoints can only respond asynchronously and require a service account.
  • DialogFlow endpoints let your app utilize the natural language processing (NLP) capabilities of DialogFlow. For details, see the DialogFlow documentation.

For a simple, straightforward app architecture, try implementing an app using an HTTPS endpoint (a web service, essentially) that responds synchronously, always enclosing its payload in the HTTPS POST response. This approach does not involve authorization, so it doesn't need a service account. See the simple app implementation section below for an example of this style of app.

Apps that respond asynchronously, which includes all apps on pub/sub endpoints, require a service account to authorize with Google Chat. You may need to take a more complex approach if your app is behind a firewall or sends unsolicited messages such as alarms or other notifications to Google Chat.

A very simple app implementation

The following code implements a simple app in Python using the Flask web framework.

#!/usr/bin/env python3
"""Example app that returns a synchronous response."""

from flask import Flask, request, json


app = Flask(__name__)


@app.route('/', methods=['POST'])
def on_event():
  """Handles an event from Google Chat."""
  event = request.get_json()
  if event['type'] == 'ADDED_TO_SPACE' and not event['space']['singleUserBotDm']:
    text = 'Thanks for adding me to "%s"!' % (event['space']['displayName'] if event['space']['displayName'] else 'this chat')
  elif event['type'] == 'MESSAGE':
    text = 'You said: `%s`' % event['message']['text']
  else:
    return
  return json.jsonify({'text': text})


if __name__ == '__main__':
  app.run(port=8080, debug=True)

Because it's a web service, the app presents an HTTPS endpoint and doesn't need to use Cloud Pub/Sub to relay events to it. And because it always returns its response payload within the JSON response, it doesn't need to authenticate using a service account.

Handle events from Google Chat

This section describes how to receive and process events that your app receives from Google Chat.

Register the app

Before your app can receive events from Google Chat, you must specify its endpoint in the Chat API configuration tab when you publish your app.

Once you've registered the endpoint and published your app, Google Chat will recognize events addressed to your app and dispatch them to the specified endpoint.

Verify app authenticity

Once you've registered your HTTPS app, you need a way for your implementation to verify that the request is actually coming from Google.

Google Chat includes a bearer token in the Authorization header of every HTTPS Request to a app. For example:

POST
Host: yourappurl.com
Authorization: Bearer AbCdEf123456
Content-Type: application/json
User-Agent: Google-Dynamite

The string AbCdEf123456 in the example above is the bearer authorization token. This is a cryptographic token produced by Google. You can verify your bearer token using an open source Google API client library:

All bearer tokens sent with requests from Google Chat will have chat@system.gserviceaccount.com as the issuee, with the audience field specifying the target app's project number from the Google Cloud console. For example, if the request is for a app with the project number 1234567890, then the audience is 1234567890.

You should verify that the request is coming from Google and is intended for the target app. If the token doesn't verify, the app should respond to the request with an HTTPS response code 401 (Unauthorized).

Java

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;

import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.googleapis.auth.oauth2.GooglePublicKeysManager;
import com.google.api.client.http.apache.ApacheHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;

/** Tool for verifying JWT Tokens for Apps in Google Chat. */
public class JWTVerify {
  // Bearer Tokens received by apps will always specify this issuer.
  static String CHAT_ISSUER = "chat@system.gserviceaccount.com";

  // Url to obtain the public certificate for the issuer.
  static String PUBLIC_CERT_URL_PREFIX =
      "https://www.googleapis.com/service_accounts/v1/metadata/x509/";

  // Intended audience of the token, which will be the project number of the app.
  static String AUDIENCE = "1234567890";

  // Get this value from the request's Authorization HTTPS header.
  // For example, for "Authorization: Bearer AbCdEf123456" use "AbCdEf123456"
  static String BEARER_TOKEN = "AbCdEf123456";

  public static void main(String[] args) throws GeneralSecurityException, IOException {
    JsonFactory factory = new JacksonFactory();

    GooglePublicKeysManager.Builder keyManagerBuilder =
        new GooglePublicKeysManager.Builder(new ApacheHttpTransport(), factory);

    String certUrl = PUBLIC_CERT_URL_PREFIX + CHAT_ISSUER;
    keyManagerBuilder.setPublicCertsEncodedUrl(certUrl);

    GoogleIdTokenVerifier.Builder verifierBuilder =
        new GoogleIdTokenVerifier.Builder(keyManagerBuilder.build());
    verifierBuilder.setIssuer(CHAT_ISSUER);
    GoogleIdTokenVerifier verifier = verifierBuilder.build();

    GoogleIdToken idToken = GoogleIdToken.parse(factory, BEARER_TOKEN);
    if (idToken == null) {
      System.out.println("Token cannot be parsed");
      System.exit(-1);
    }

    // Verify valid token, signed by CHAT_ISSUER.
    if (!verifier.verify(idToken)
        || !idToken.verifyAudience(Collections.singletonList(AUDIENCE))
        || !idToken.verifyIssuer(CHAT_ISSUER)) {
      System.out.println("Invalid token");
      System.exit(-1);
    }

    // Token originates from Google and is targeted to a specific client.
    System.out.println("The token is valid");
  }
}

Python

import sys

from oauth2client import client

# Bearer Tokens received by apps will always specify this issuer.
CHAT_ISSUER = 'chat@system.gserviceaccount.com'

# Url to obtain the public certificate for the issuer.
PUBLIC_CERT_URL_PREFIX = 'https://www.googleapis.com/service_accounts/v1/metadata/x509/'

# Intended audience of the token, which will be the project number of the app.
AUDIENCE = '1234567890'

# Get this value from the request's Authorization HTTPS header.
# For example, for 'Authorization: Bearer AbCdEf123456' use 'AbCdEf123456'.
BEARER_TOKEN = 'AbCdEf123456'

try:
  # Verify valid token, signed by CHAT_ISSUER, intended for a third party.
  token = client.verify_id_token(
      BEARER_TOKEN, AUDIENCE, cert_uri=PUBLIC_CERT_URL_PREFIX + CHAT_ISSUER)

  if token['iss'] != CHAT_ISSUER:
    sys.exit('Invalid issuee')
except:
  sys.exit('Invalid token')

# Token originates from Google and is targeted to a specific client.
print 'The token is valid'

Event payload

When your app receives an event from Google Chat, the event includes a request body: this is the JSON payload that represents the event. The request body always includes the following information:

  • type: A string that specifies the type of the event.
  • eventTime: A string containing the event timestamp.

Additional information contained in the request body depends on the event type. The following example shows a possible payload:

{
  "type": "MESSAGE",
  "eventTime": "2017-03-02T19:02:59.910959Z",
  "space": {
    "name": "spaces/AAAAAAAAAAA",
    "displayName": "Best Dogs Discussion Space",
    "type": "ROOM"
  },
  "message": {
    "name": "spaces/AAAAAAAAAAA/messages/CCCCCCCCCCC",
    "sender": {
      "name": "users/12345678901234567890",
      "displayName": "Chris Corgi",
      "avatarUrl": "https://lh3.googleusercontent.com/.../photo.jpg",
      "email": "chriscorgi@example.com"
    },
    "createTime": "2017-03-02T19:02:59.910959Z",
    "text": "I mean is there any good reason their legs should be longer?",
    "thread": {
      "name": "spaces/AAAAAAAAAAA/threads/BBBBBBBBBBB"
    }
  }
}

See the event formats reference for details of the different event types and their request formats.

Process the event

When your app receives an event from Google Chat, what it does with that event depends on your implementation. The app can look up some information from a data source, record the event information, or just about anything else. This processing behavior is essentially what defines the app.

Google Chat apps usually process the information contained in the event and generate a response back to the thread that issued the event. The following diagram describes a typical interaction with an app in a Chat space:

Architecture of the events processing of an app.

There are three kinds of events shown in the above diagram: ADDED_TO_SPACE, MESSAGE, and REMOVED_FROM_SPACE. An app can't respond after being removed from a space, but it can respond to the other two types.

Respond synchronously

An app can respond to an event synchronously by returning a JSON-formatted message payload in the HTTPS response. The deadline for a synchronous response is 30 seconds.

A synchronous response from an app is always posted in the thread that generated the event to the app.

Respond asynchronously

If an app needs to respond to a user message beyond the 30-second deadline (for example, it may need to report back after completing a long-running task), it can respond asynchronously by creating a message with Google Chat API.

Retry

If an HTTPS request to your app fails (such as timeout, temporary network failure, or a non-2xx HTTPS status code), Google Chat retries delivery twice, with at least a ten-second delay between each retry. As a result, an app might receive the same message up to three times in certain situations. If the request completes successfully but returns an invalid message payload, Google Chat doesn't retry the request.