Build a Google Chat app as a webhook

This page describes how to set up a webhook to send asynchronous messages into a Chat space using external triggers. For example, you can configure a monitoring application to notify on-call personnel on Chat when a server goes down. To send a synchronous message with a Chat app, see Send a message.

With this type of architecture design, users can't interact with the webhook or the connected external application because communication is one-way. Webhooks aren't conversational. They can't respond to or receive messages from users or Chat app interaction events. To respond to messages, build a Chat app instead of a webhook.

While a webhook isn't technically a Chat app—webhooks connect applications using standard HTTP requests—this page refers to it as a Chat app for simplification. Each webhook only works in the Chat space in which it's registered. Incoming webhooks work in direct messages, but only when all users have Chat apps enabled. You can't publish webhooks to the Google Workspace Marketplace.

The following diagram shows the architecture of a webhook connected to Chat:

Architecture for incoming webhooks to send asynchronous messages to Chat.

In the preceding diagram, a Chat app has the following flow of information:

  1. The Chat app logic receives information from external third-party services, such as a project management system or a ticketing tool.
  2. The Chat app logic is hosted in either a cloud or on-premises system that can send messages by using a webhook URL to a specific Chat space.
  3. Users can receive messages from the Chat app in that specific Chat space, but are unable to interact with the Chat app.

Prerequisites

Python

  • Python 3.10.7 or greater.
  • A Google Workspace account with access to Chat.
  • An existing Chat space.
  • The httplib2 library. If necessary, run the following command-line interface (CLI) command to install the library using pip:

    pip install httplib2
    

Node.js

Java

Apps Script

Create a webhook

To create a webhook, register it in the Chat space where you want to receive messages, and then write a script that sends messages.

Register the incoming webhook

  1. In a browser, open Chat. Webhooks aren't configurable from the Chat mobile app.
  2. Go to the space where you want to add a webhook.
  3. Next to the space title, click the expand more arrow, and then click Apps & integrations.
  4. Click Add webhooks.
  5. In the Name field, enter Quickstart Webhook.
  6. In the Avatar URL field, enter https://developers.google.com/chat/images/chat-product-icon.png.
  7. Click Save.
  8. To copy the webhook URL, click More, and then click Copy link.

Write the webhook script

The example webhook script sends a message to the space in which the webhook is registered by sending a POST request to the webhook URL. The Chat API responds with an instance of Message.

Select a language to learn how to create a webhook script:

Python

  1. In your working directory, create a file named quickstart.py.

  2. In quickstart.py, paste the following code:

    python/webhook/quickstart.py
    from json import dumps
    from httplib2 import Http
    
    # Copy the webhook URL from the Chat space where the webhook is registered.
    # The values for SPACE_ID, KEY, and TOKEN are set by Chat, and are included
    # when you copy the webhook URL.
    
    def main():
        """Google Chat incoming webhook quickstart."""
        url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN"
        app_message = {"text": "Hello from a Python script!"}
        message_headers = {"Content-Type": "application/json; charset=UTF-8"}
        http_obj = Http()
        response = http_obj.request(
            uri=url,
            method="POST",
            headers=message_headers,
            body=dumps(app_message),
        )
        print(response)
    
    
    if __name__ == "__main__":
        main()
  3. Replace the value for the url variable with the webhook URL that you copied when you registered the webhook.

Node.js

  1. In your working directory, create a file named index.js.

  2. In index.js, paste the following code:

    node/webhook/index.js
    /**
     * Sends asynchronous message to Google Chat
     * @return {Object} response
     */
    async function webhook() {
      const url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages"
      const res = await fetch(url, {
        method: "POST",
        headers: {"Content-Type": "application/json; charset=UTF-8"},
        body: JSON.stringify({text: "Hello from a Node script!"})
      });
      return await res.json();
    }
    
    webhook().then(res => console.log(res));
  3. Replace the value for the url variable with the webhook URL that you copied when you registered the webhook.

Java

  1. In your working directory, create a file named pom.xml.

  2. In pom.xml, copy and paste the following:

    java/webhook/pom.xml
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.google.chat.webhook</groupId>
      <artifactId>java-webhook-app</artifactId>
      <version>0.1.0</version>
    
      <name>java-webhook-app</name>
      <url>https://github.com/googleworkspace/google-chat-samples/tree/main/java/webhook</url>
    
      <properties>
        <maven.compiler.target>11</maven.compiler.target>
        <maven.compiler.source>11</maven.compiler.source>
      </properties>
    
      <dependencies>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.9.1</version>
        </dependency>
      </dependencies>
    
      <build>
        <pluginManagement>
          <plugins>
            <plugin>
              <artifactId>maven-compiler-plugin</artifactId>
              <version>3.8.0</version>
            </plugin>
          </plugins>
        </pluginManagement>
      </build>
    </project>
  3. In your working directory, create the following directory structure src/main/java.

  4. In the src/main/java directory, create a file named App.java.

  5. In App.java, paste the following code:

    java/webhook/src/main/java/com/google/chat/webhook/App.java
    import com.google.gson.Gson;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.util.Map;
    import java.net.URI;
    
    public class App {
      private static final String URL = "https://chat.googleapis.com/v1/spaces/AAAAGCYeSRY/messages";
      private static final Gson gson = new Gson();
      private static final HttpClient client = HttpClient.newHttpClient();
    
      public static void main(String[] args) throws Exception {
        String message = gson.toJson(Map.of("text", "Hello from Java!"));
    
        HttpRequest request = HttpRequest.newBuilder(
            URI.create(URL))
            .header("accept", "application/json; charset=UTF-8")
            .POST(HttpRequest.BodyPublishers.ofString(message))
            .build();
    
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    
        System.out.println(response.body());
      }
    }
  6. Replace the value for the URL variable with the webhook URL that you copied when you registered the webhook.

Apps Script

  1. In a browser, go to Apps Script.

  2. Click New Project

  3. Paste the following code:

    apps-script/webhook/webhook.gs
    function webhook() {
      const url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages"
      const options = {
        "method": "post",
        "headers": {"Content-Type": "application/json; charset=UTF-8"},
        "payload": JSON.stringify({"text": "Hello from Apps Script!"})
      };
      const response = UrlFetchApp.fetch(url, options);
      console.log(response);
    }
  4. Replace the value for the url variable with the webhook URL that you copied when you registered the webhook.

Run the webhook script

In a CLI, run the script:

Python

  python3 quickstart.py

Node.js

  node index.js

Java

  mvn compile exec:java -Dexec.mainClass=App

Apps Script

  • Click Run.

When you run the code, the webhook sends a message to the space in which you registered it.

Start or reply to a message thread

  1. Specify spaces.messages.thread.threadKey as part of the message request body. Depending on whether you're starting or replying to a thread, use the following values for threadKey:

    • If starting a thread, set the threadKey to an arbitrary string, but make a note of this value to post a reply to the thread.

    • If replying to a thread, specify the threadKey that was set when the thread was started. For example, to post a reply to the thread where the initial message used MY-THREAD, set MY-THREAD.

  2. Define the thread behavior if the specified threadKey isn't found:

    • Reply to a thread or start a new thread. Add the messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD parameter to the webhook URL. Passing this URL parameter causes Chat to look for an existing thread using the specified threadKey. If one is found, then the message posts as a reply to that thread. If none is found, then the message starts a new thread corresponding to that threadKey.

    • Reply to a thread or do nothing. Add the messageReplyOption=REPLY_MESSAGE_OR_FAIL parameter to the webhook URL. Passing this URL parameter causes Chat to look for an existing thread using the specified threadKey. If one is found, then the message posts as a reply to that thread. If none is found, then the message isn't sent.

    To learn more, see messageReplyOption.

The following code sample starts or replies to a message thread:

Python

python/webhook/thread-reply.py
from json import dumps
from httplib2 import Http

# Copy the webhook URL from the Chat space where the webhook is registered.
# The values for SPACE_ID, KEY, and TOKEN are set by Chat, and are included
# when you copy the webhook URL.
#
# Then, append messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD to the
# webhook URL.


def main():
    """Google Chat incoming webhook that starts or replies to a message thread."""
    url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD"
    app_message = {
        "text": "Hello from a Python script!",
        # To start a thread, set threadKey to an arbitratry string.
        # To reply to a thread, specify that thread's threadKey value.
        "thread": {"threadKey": "THREAD_KEY_VALUE"},
    }
    message_headers = {"Content-Type": "application/json; charset=UTF-8"}
    http_obj = Http()
    response = http_obj.request(
        uri=url,
        method="POST",
        headers=message_headers,
        body=dumps(app_message),
    )
    print(response)


if __name__ == "__main__":
    main()

Node.js

node/webhook/thread-reply.js
/**
 * Sends asynchronous message to Google Chat
 * @return {Object} response
 */
async function webhook() {
  const url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD"
  const res = await fetch(url, {
    method: "POST",
    headers: {"Content-Type": "application/json; charset=UTF-8"},
    body: JSON.stringify({
      text: "Hello from a Node script!",
      thread: {threadKey: "THREAD_KEY_VALUE"}
    })
  });
  return await res.json();
}

webhook().then(res => console.log(res));

Apps Script

apps-script/webhook/thread-reply.gs
function webhook() {
  const url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD"
  const options = {
    "method": "post",
    "headers": {"Content-Type": "application/json; charset=UTF-8"},
    "payload": JSON.stringify({
      "text": "Hello from Apps Script!",
      "thread": {"threadKey": "THREAD_KEY_VALUE"}
    })
  };
  const response = UrlFetchApp.fetch(url, options);
  console.log(response);
}