---
title: Google Chat
description: Google Chat adapter with service account auth and optional Pub/Sub.
tagline: Integrate with Google Chat spaces for team collaboration and automated workflows.
package: @chat-adapter/gchat
---

# Google Chat



## Install

<PackageInstall package="@chat-adapter/gchat" />

## Quick start

<Callout type="info">
  The adapter auto-detects `GOOGLE_CHAT_CREDENTIALS` (or `GOOGLE_CHAT_USE_ADC`) from the environment.
</Callout>

```typescript title="lib/bot.ts" lineNumbers
import { Chat } from "chat";
import { createGoogleChatAdapter } from "@chat-adapter/gchat";

const bot = new Chat({
  userName: "mybot",
  adapters: {
    gchat: createGoogleChatAdapter(),
  },
});

bot.onNewMention(async (thread, message) => {
  await thread.post("Hello from Google Chat!");
});
```

## Configuration

<TypeTable
  type={{
  credentials: {
    type: "ServiceAccountCredentials",
    description:
      "Service account credentials JSON. Auto-detected from `GOOGLE_CHAT_CREDENTIALS`.",
  },
  botUserId: {
    type: "string",
    description:
      "Canonical users/... resource name of this Chat app. Required for exact self-message and mention detection; auto-detected from `GOOGLE_CHAT_BOT_USER_ID`.",
  },
  useApplicationDefaultCredentials: {
    type: "boolean",
    description:
      "Use Application Default Credentials. Auto-detected from `GOOGLE_CHAT_USE_ADC`.",
  },
  pubsubTopic: {
    type: "string",
    description:
      "Pub/Sub topic for Workspace Events. Auto-detected from `GOOGLE_CHAT_PUBSUB_TOPIC`.",
  },
  pubsubAudience: {
    type: "string",
    description:
      "Expected JWT audience for Pub/Sub webhook verification.",
  },
  pubsubServiceAccountEmail: {
    type: "string",
    description:
      "Service account your Pub/Sub push subscription authenticates as. Required to accept Pub/Sub pushes, since the audience alone does not identify the caller. Without it, pushes are rejected.",
  },
  googleChatProjectNumber: {
    type: "string",
    description:
      "GCP project number for direct webhook JWT verification. Use when the Chat app's authentication audience is set to 'Project number'.",
  },
  endpointUrl: {
    type: "string",
    description:
      "Public URL of the webhook endpoint. Required for routing button click actions to your app, and used as an accepted JWT audience for direct webhooks when the Chat app's authentication audience is set to 'HTTP endpoint URL'.",
  },
  workspaceAddOnServiceAccountEmail: {
    type: "string",
    description:
      "Your own service-{projectNumber}@gcp-sa-gsuiteaddons.iam.gserviceaccount.com identity. Required to accept Workspace Add-on Chat app webhooks, which are signed by an add-on identity rather than chat@system.gserviceaccount.com. Without it, add-on tokens are rejected.",
  },
  impersonateUser: {
    type: "string",
    description:
      "User email for domain-wide delegation. Required for fetching message history and DMs.",
  },
  apiUrl: {
    type: "string",
    description: "Override the Google Chat API base URL.",
  },
}}
/>

One of `googleChatProjectNumber`, `endpointUrl`, `pubsubAudience`, or `disableSignatureVerification: true` is required — the constructor throws otherwise. Configure the verifier(s) for each transport you actually receive.

Set `botUserId` (or `GOOGLE_CHAT_BOT_USER_ID`) to the canonical `sender.name` from a verified message authored by your Chat app, such as `users/123456789`. The adapter never learns this identity from inbound mentions. When it is omitted, all `BOT` senders are conservatively treated as self to prevent reply loops.

Apps upgrading from an earlier release that rely on mention handlers must configure `botUserId` to preserve mention handling. Without it, bot mention annotations are left unchanged and the default `onNewMention` detection may no longer match them.

## Authentication

### 1. Create a GCP project

1. Go to [console.cloud.google.com](https://console.cloud.google.com) and create a project.
2. Enable **Google Chat API**, **Google Workspace Events API** (for receiving all messages), and **Cloud Pub/Sub API**.

### 2. Create a service account

1. Open **IAM & Admin** then **Service Accounts** and click **Create Service Account**.
2. After creation, open the account and go to the **Keys** tab.
3. Click **Add Key** then **Create new key** then **JSON**.
4. Copy the JSON to `GOOGLE_CHAT_CREDENTIALS`.

<Callout type="warn">
  If your organization has the `iam.disableServiceAccountKeyCreation` constraint enabled, you need to relax it or add a project exception under **IAM & Admin** then **Organization Policies**.
</Callout>

### 3. Configure the Chat app

1. Open the [Chat API configuration](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat).
2. Fill in app name, avatar, description.
3. Enable **Receive 1:1 messages** and **Join spaces and group conversations**.
4. Set connection settings to **App URL** with `https://your-domain.com/api/webhooks/gchat`.
5. Set visibility, then save.

### 4. Add the bot to a space

1. Open Google Chat.
2. In a Space, open **Manage apps & integrations** then **Add apps** and find your app.

## Advanced

### Pub/Sub for receiving all messages

By default, Google Chat only sends webhooks for @mentions. To receive all messages in a space, set up Workspace Events with Pub/Sub.

```typescript title="lib/bot.ts" lineNumbers
createGoogleChatAdapter({
  pubsubTopic: process.env.GOOGLE_CHAT_PUBSUB_TOPIC,
  impersonateUser: process.env.GOOGLE_CHAT_IMPERSONATE_USER,
});
```

**Topic & subscription:**

1. Under **Pub/Sub** then **Topics**, create a topic (e.g. `chat-events`) and copy its full name to `GOOGLE_CHAT_PUBSUB_TOPIC`.
2. Add `chat-api-push@system.gserviceaccount.com` as a Pub/Sub Publisher.
3. Create a Push subscription with endpoint `https://your-domain.com/api/webhooks/gchat`. Enable authentication on it and pick a service account, then set that account's email as `GOOGLE_CHAT_PUBSUB_SERVICE_ACCOUNT_EMAIL`.

**Domain-wide delegation:**

Required for Workspace Events subscriptions and initiating DMs.

1. In **IAM & Admin** then **Service Accounts**, edit your service account and check **Enable Google Workspace Domain-wide Delegation**. Copy the numeric **Client ID**.
2. In the [Google Admin Console](https://admin.google.com), go to **Security** then **Access and data control** then **API controls**, then **Manage Domain Wide Delegation**.
3. Add the Client ID with these comma-separated scopes:
   ```
   https://www.googleapis.com/auth/chat.spaces.readonly,
   https://www.googleapis.com/auth/chat.messages.readonly,
   https://www.googleapis.com/auth/chat.spaces,
   https://www.googleapis.com/auth/chat.spaces.create
   ```
4. Set `GOOGLE_CHAT_IMPERSONATE_USER` to an admin email.

### Webhook verification

The two transports share one HTTP endpoint, so each verifier only covers its own request shape:

* **Direct webhooks** — Google Chat sends a signed JWT in the `Authorization: Bearer …` header. The expected `aud` claim depends on how the Chat app is configured (see [Verify requests from Google Chat](https://developers.google.com/workspace/chat/verify-requests-from-chat)).
* **Pub/Sub push** — Cloud Pub/Sub sends a signed OIDC JWT whose audience is whatever you configured on the push subscription. Configure with `pubsubAudience` **and** `pubsubServiceAccountEmail`.

<Callout type="warn">
  Your push audience is a public URL, so anyone can point a Pub/Sub subscription in their own Google Cloud project at your endpoint and have Google mint a validly signed token naming it. Signature and `aud` therefore say nothing about who sent the push. Set `pubsubServiceAccountEmail` to the identity in your subscription's push auth settings and the adapter compares it exactly, as [Google requires](https://docs.cloud.google.com/pubsub/docs/authenticate-push-subscriptions). Leave it unset and Pub/Sub pushes are rejected with HTTP 401.
</Callout>

If you only configure a direct-webhook verifier, incoming Pub/Sub-shaped requests are rejected with HTTP 401 — and vice versa. Configure both transports if you receive both.

#### Which direct-webhook option do I need?

| Your Chat app                                                                                         | Token type & `aud`                         | Signed by                                                                               | Set                                                       |
| ----------------------------------------------------------------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| Standalone Chat app, **Authentication audience: Project number** (in Chat API config)                 | self-signed JWT, `aud` = project number    | `chat@system.gserviceaccount.com` (its own X.509 certs)                                 | `googleChatProjectNumber`                                 |
| Standalone Chat app, **Authentication audience: HTTP endpoint URL**                                   | Google OIDC ID token, `aud` = endpoint URL | Google (`email`: `chat@system.gserviceaccount.com`)                                     | `endpointUrl`                                             |
| **Workspace Add-on Chat app** (built via Google Workspace Marketplace SDK; the audience is hardcoded) | Google OIDC ID token, `aud` = endpoint URL | Google (`email`: `service-{projectNumber}@gcp-sa-gsuiteaddons.iam.gserviceaccount.com`) | `endpointUrl` **and** `workspaceAddOnServiceAccountEmail` |
| Mixed across envs / not sure                                                                          | varies                                     | varies                                                                                  | both `googleChatProjectNumber` and `endpointUrl`          |

The two token types are verified differently, matching [Google's reference implementation](https://developers.google.com/workspace/chat/verify-requests-from-chat): endpoint-URL tokens are standard OIDC ID tokens checked against Google's public certs plus the Chat service-account `email` claim; project-number tokens are self-signed by `chat@system.gserviceaccount.com` and checked against that service account's own X.509 certificates. When both `googleChatProjectNumber` and `endpointUrl` are set, either token type is accepted. If you don't know which mode your app uses, look at an incoming token: an `email` claim containing `gcp-sa-gsuiteaddons` means it's a Workspace Add-on (URL audience).

<Callout type="warn">
  Every Workspace Add-on project produces an `email` of the same `service-{projectNumber}@gcp-sa-gsuiteaddons` shape, so that shape identifies "some add-on", not *your* add-on. Set `workspaceAddOnServiceAccountEmail` to your own project's address and the adapter compares it exactly. Leave it unset and add-on tokens are rejected with HTTP 401, rather than accepting any add-on that happens to point at your endpoint URL. Standalone Chat apps signing as `chat@system.gserviceaccount.com` are unaffected.
</Callout>

<Callout type="info">
  Workspace Add-on Chat apps don't expose an "Authentication audience" radio; their token `aud` is always the endpoint URL. Set `endpointUrl` for these.
</Callout>

### Limitations

* **Typing indicators** — not supported by the Google Chat API.
* **Adding reactions** — the Chat API doesn't allow service-account auth for reactions. With `impersonateUser` configured, the reaction appears as coming from the impersonated user.
* **Message history (`fetchMessages`)** — requires domain-wide delegation with `impersonateUser`.

## Feature support

<FeatureSupport />
