Google Chat Integration: Setup and Configuration Guide

Covers the official @openclaw/googlechat plugin for Google Chat, including DMs and spaces via API webhooks. Written for gateway operators who need to install and configure the integration.

Read this when

  • Working on Google Chat channel features

Google Chat is available as the official @openclaw/googlechat plugin, handling DMs and spaces via Google Chat API webhooks (HTTP endpoint only, no Pub/Sub).

Install

openclaw plugins install @openclaw/googlechat

When you're working from a git repo, use a local checkout:

openclaw plugins install ./path/to/local/googlechat-plugin

Quick setup (beginner)

  1. Set up a Google Cloud project, then turn on the Google Chat API.
  2. Set up a Service Account:
    • Choose Create Credentials > Service Account.
    • Pick any name you like (for instance, openclaw-chat).
    • Skip permissions and principals (Continue, then Done).
  3. Generate and save the JSON key:
    • Select the service account you just made > Keys tab > Add Key > Create new key > JSON > Create.
  4. Place the downloaded JSON file somewhere on your gateway host (for example, ~/.openclaw/googlechat-service-account.json).
  5. In the Google Cloud Console Chat Configuration, build a Google Chat app:
    • Complete Application info (name, avatar URL, description).
    • Turn on Interactive features.
    • In Functionality, tick Join spaces and group conversations.
    • In Connection settings, choose HTTP endpoint URL.
    • In Triggers, pick Use a common HTTP endpoint URL for all triggers, then point it at your public gateway URL with /googlechat appended (refer to Public URL).
    • In Visibility, tick Make this Chat app available to specific people and groups in <Your Domain> and fill in your email address.
    • Hit Save.
  6. Turn on the app status: reload the page, locate App status, switch it to Live - available to users, then Save once more.
  7. Set up OpenClaw with the service account and webhook audience (this has to line up with the Chat app config):
    • Env: GOOGLE_CHAT_SERVICE_ACCOUNT_FILE=/path/to/service-account.json (default account only), or
    • Config: refer to Config highlights. openclaw channels add --channel googlechat also takes --audience-type, --audience, --webhook-path, and --webhook-url.
  8. Launch the gateway. Google Chat will POST to your webhook path (default /googlechat).

Add to Google Chat

With the gateway up and your email listed under visibility:

  1. Open Google Chat.
  2. Hit the + (plus) icon beside Direct Messages.
  3. Look up the App name you set in the Google Cloud Console.
    • Since it's a private app, the bot won't show up in the Marketplace browse list. Find it by searching its name.
  4. Pick the bot, press Add or Chat, then send a message.

Public URL (Webhook-only)

A public HTTPS endpoint is mandatory for Google Chat webhooks. To stay safe, expose only the /googlechat path to the internet, and leave the OpenClaw dashboard plus every other endpoint private.

Option A: Tailscale Funnel (Recommended)

For the private dashboard use Tailscale Serve, and for the public webhook path use Funnel.

  1. Find out which address your gateway is bound to:

    # Linux (iproute2):
    ss -tlnp | grep 18789
    
    # macOS (no ss):
    lsof -iTCP:18789 -sTCP:LISTEN
    

    Take note of the IP (such as 127.0.0.1, 0.0.0.0, or a Tailscale 100.x.x.x address).

  2. Make the dashboard reachable on the tailnet only (port 8443):

    # If bound to localhost (127.0.0.1 or 0.0.0.0):
    tailscale serve --bg --https 8443 http://127.0.0.1:18789
    
    # If bound to a Tailscale IP only:
    tailscale serve --bg --https 8443 http://100.x.x.x:18789
    
  3. Make just the webhook path publicly reachable:

    # If bound to localhost (127.0.0.1 or 0.0.0.0):
    tailscale funnel --bg --set-path /googlechat http://127.0.0.1:18789/googlechat
    
    # If bound to a Tailscale IP only:
    tailscale funnel --bg --set-path /googlechat http://100.x.x.x:18789/googlechat
    
  4. If you're asked, open the authorization URL printed in the output to turn on Funnel for this node.

  5. Confirm it works:

    tailscale serve status
    tailscale funnel status
    

Your public webhook URL is https://<node-name>.<tailnet>.ts.net/googlechat. The dashboard remains tailnet-only at https://<node-name>.<tailnet>.ts.net:8443/. In the Google Chat app config, use the public URL (drop :8443).

Note: This setup survives reboots. To undo it later, run tailscale funnel reset and tailscale serve reset.

Option B: Reverse Proxy (Caddy)

Forward only the webhook path:

your-domain.com {
    reverse_proxy /googlechat* localhost:18789
}

Anything hitting your-domain.com/ gets ignored or returns 404, whereas your-domain.com/googlechat goes through to OpenClaw.

Option C: Cloudflare Tunnel

Set the tunnel ingress rules so that only the webhook path is routed:

  • Path: /googlechat -> http://localhost:18789/googlechat
  • Default rule: HTTP 404 (Not Found)

How it works

  1. Google Chat sends JSON to the gateway webhook path (POST only, JSON content type required, rate limited per IP).
  2. OpenClaw verifies each request before dispatching it:
    • Chat app events include Authorization: Bearer <token>. Verification of the token happens before the full body is parsed.
    • For Google Workspace Add-on events, the token arrives in the body (authorizationEventObject.systemIdToken). OpenClaw reads these under a stricter pre-auth budget (16 KB, 3 s) ahead of verification.
  3. The token is validated against audienceType + audience:
    • audienceType: "app-url" → your HTTPS webhook URL is the audience.
    • audienceType: "project-number" → the Cloud project number is the audience.
    • Add-on tokens under app-url also demand that appPrincipal be set to the app's numeric OAuth 2.0 client ID (21 digits, not an email). If not, verification fails and a warning is logged.
  4. Routing of messages happens by space:
    • Each space gets its own session agent:<agentId>:googlechat:group:<spaceId>. Replies land in the message thread.
    • By default, DMs fold into the agent's main session. For per-peer DM sessions, set session.dmScope (see Session).
  5. DM access defaults to pairing. A pairing code is sent to unknown senders. To approve, use:
    • openclaw pairing approve googlechat <code>
  6. Group spaces need an @-mention by default. Detection relies on Chat USER_MENTION annotations that target the app. If detection requires the app's user resource name, set botUser (for example, users/1234567890).
  7. When an exec or plugin approval originates from Google Chat and a stable users/<id> approver is configured, OpenClaw places a native approval card (cardsV2) in the space or thread where it started. Opaque callback tokens ride along on the card buttons. The manual /approve <id> <decision> prompt shows up only if native delivery is not possible.

Inbound durability

Once a request is authenticated, OpenClaw clears the add-on authorization object from storage and durably queues Google Chat MESSAGE events prior to returning 200. Should persistence fail, 503 is returned, which lets Google Chat retry rather than acknowledge an event that might be lost. A durably queued 200 carries x-openclaw-delivery-accepted: durable. Non-message action acks and error responses leave the marker out, so reverse proxies can demand it to tell durable acceptance apart from a generic 200.

Messages that are pending or retryable outlive a Gateway restart, stay serialized per space, and rely on the Google Chat message resource name to avoid duplicate queue entries while the active or retained completion record is present. Non-message actions follow their existing detached webhook path and are not covered by this durable-queue guarantee. Across the queue-to-agent boundary, delivery stays at least once, so a crash mid-handoff may replay a turn.

Targets

For delivery and allowlists, use the identifiers below:

  • Direct messages: users/<userId> (recommended).
  • Spaces: spaces/<spaceId>.
  • Raw email name@example.com can change and is only matched against allowlists when channels.googlechat.dangerouslyAllowNameMatching: true.
  • Deprecated: users/<email> counts as a user id, not as an email allowlist entry.
  • The prefixes googlechat:, google-chat:, and gchat: are accepted and removed.

Config highlights

{
  channels: {
    googlechat: {
      enabled: true,
      serviceAccountFile: "/path/to/service-account.json",
      // or serviceAccount: { source: "file", provider: "filemain", id: "/channels/googlechat/serviceAccount" }
      audienceType: "app-url",
      audience: "https://gateway.example.com/googlechat",
      appPrincipal: "123456789012345678901", // add-on verification only; numeric OAuth client ID
      webhookPath: "/googlechat",
      botUser: "users/1234567890", // optional; helps mention detection
      allowBots: false,
      dmPolicy: "pairing",
      allowFrom: ["users/1234567890"],
      groupPolicy: "allowlist",
      groups: {
        "spaces/AAAA": {
          enabled: true,
          requireMention: true,
          users: ["users/1234567890"],
          systemPrompt: "Short answers only.",
        },
      },
      typingIndicator: "message",
      mediaMaxMb: 20,
    },
  },
}

Notes:

  • Service account credentials: serviceAccountFile (path) or serviceAccount (inline JSON string, object, or env/file/exec/store SecretRef). The env vars GOOGLE_CHAT_SERVICE_ACCOUNT (inline JSON) and GOOGLE_CHAT_SERVICE_ACCOUNT_FILE (path) only apply to the default account. For setups with multiple accounts, use channels.googlechat.accounts.<id> with the same keys, which includes per-account serviceAccount SecretRefs.
  • When account dmPolicy and groupPolicy are left out, they take after the channel root. Policies set explicitly on an account take precedence. The root falls back to pairing and allowlist respectively. Settings shared through accounts.default rank below the root. Named accounts do not inherit its credentials, enabled, or dangerouslyAllowNameMatching.
  • With webhookPath unset, the webhook path defaults to /googlechat. Alternatively, webhookUrl can provide the path.
  • Group keys need to be stable space ids (spaces/<spaceId>). Keys based on display names are deprecated, and a log entry marks them as such.
  • dangerouslyAllowNameMatching turns mutable email principal matching back on for allowlists (a break-glass compatibility mode). Doctor raises a warning for email entries.
  • Reaction actions for Google Chat are not available. Service-account authentication is what the plugin uses, whereas Google Chat reaction endpoints demand user authentication. Use openclaw doctor --fix to strip out legacy reaction settings that are unsupported.
  • Native approval cards rely on Google Chat cardsV2 button clicks rather than reaction events. Approvers are drawn from allowFrom or defaultTo, and each must be a stable numeric users/<id> value.
  • Only text send is offered through message actions. Uploading Google Chat attachments demands user authentication, but this plugin authenticates with a service account, so outbound file upload is not available.
  • typingIndicator: message (the default) posts a _<Bot> is typing..._ placeholder, then edits it into the first reply. none turns this off. reaction needs user OAuth and at present falls back to message with a logged error when service-account auth is in use.
  • For each message, OpenClaw fetches the first inbound attachment via the Chat API and feeds it into the media pipeline. That download is capped by mediaMaxMb (default 20). Files on Google Drive are skipped. In their place, the agent gets an unavailable-attachment notice that requests a direct file upload. The same upload guidance goes to other unsupported attachment sources. When a message carries several attachments, a counted notice names the extra ones that went unprocessed. Attachments that exceed the size limit keep their size-limit notice.
  • Messages written by bots are skipped unless you opt in. Once allowBots: true is set, accepted bot messages fall under shared bot loop protection: set up channels.defaults.botLoopProtection, then override it with channels.googlechat.botLoopProtection or channels.googlechat.groups.<space>.botLoopProtection.

Listing custom emoji is not possible, since Google Chat's customEmojis.list endpoint needs user authentication carrying the chat.customemojis or chat.customemojis.readonly scope. This plugin authenticates only as a service account with the chat.bot scope, which has no access to that endpoint.

For secrets reference details, see Secrets Management.

Troubleshooting

405 Method Not Allowed

When Google Cloud Logs Explorer reports errors along these lines:

status code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Allowed

Then the webhook handler has not been registered. Typical causes:

  1. Channel not configured: the channels.googlechat section is absent. Check with:

    openclaw config get channels.googlechat
    

    Should it return "Config path not found", add the configuration (see Config highlights).

  2. Plugin not enabled: verify plugin status:

    openclaw plugins list | grep googlechat
    

    Should it read "disabled", execute openclaw plugins enable googlechat and review the application result.

  3. Configuration not applied: inspect hot reload status along with Gateway logs. Bring the Gateway up if it is offline. Restart the Gateway after changing the service environment so the change is loaded.

Confirm the channel is running:

openclaw channels status
# Should show: Google Chat default: enabled, configured, ...

Other issues

  • Auth errors and missing audience config surface through openclaw channels status --probe (audience and audienceType are both required).
  • Should no messages show up, double-check the Chat app's webhook URL and trigger configuration.
  • Should mention gating stop replies, point botUser at the app's user resource name and inspect requireMention.
  • Running openclaw logs --follow during a test message reveals whether requests are reaching the gateway.
2,039 words · updated Sep 18, 2026