docs/channels/googlechat.md
Google Chat runs as the official @openclaw/googlechat plugin: DMs and spaces through Google Chat API webhooks (HTTP endpoint only, no Pub/Sub).
openclaw plugins install @openclaw/googlechat
Local checkout (when running from a git repo):
openclaw plugins install ./path/to/local/googlechat-plugin
openclaw-chat).~/.openclaw/googlechat-service-account.json)./googlechat (see Public URL).<Your Domain> and enter your email address.GOOGLE_CHAT_SERVICE_ACCOUNT_FILE=/path/to/service-account.json (default account only), oropenclaw channels add --channel googlechat also accepts --audience-type, --audience, --webhook-path, and --webhook-url./googlechat).Once the gateway is running and your email is on the visibility list:
Google Chat webhooks require a public HTTPS endpoint. For security, expose only the /googlechat path to the internet and keep the OpenClaw dashboard and other endpoints private.
Use Tailscale Serve for the private dashboard and Funnel for the public webhook path.
Check what address your gateway is bound to:
ss -tlnp | grep 18789
Note the IP (e.g., 127.0.0.1, 0.0.0.0, or a Tailscale 100.x.x.x address).
Expose the dashboard to 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
Expose only the webhook path publicly:
# 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
If prompted, visit the authorization URL shown in the output to enable Funnel for this node.
Verify:
tailscale serve status
tailscale funnel status
Your public webhook URL is https://<node-name>.<tailnet>.ts.net/googlechat; the dashboard stays tailnet-only at https://<node-name>.<tailnet>.ts.net:8443/. Use the public URL (without :8443) in the Google Chat app config.
Note: This configuration persists across reboots. Remove it later with
tailscale funnel resetandtailscale serve reset.
Proxy only the webhook path:
your-domain.com {
reverse_proxy /googlechat* localhost:18789
}
Requests to your-domain.com/ are ignored or 404, while your-domain.com/googlechat routes to OpenClaw.
Configure the tunnel ingress rules to route only the webhook path:
/googlechat -> http://localhost:18789/googlechatAuthorization: Bearer <token>; the token is verified before the full body is parsed.authorizationEventObject.systemIdToken) and are read under a stricter pre-auth budget (16 KB, 3 s) before verification.audienceType + audience:
audienceType: "app-url" → audience is your HTTPS webhook URL.audienceType: "project-number" → audience is the Cloud project number.app-url additionally require appPrincipal set to the app's numeric OAuth 2.0 client ID (21 digits, not an email); otherwise verification fails with a logged warning.agent:<agentId>:googlechat:group:<spaceId>; replies go to the message thread.session.dmScope for per-peer DM sessions (see Session).openclaw pairing approve googlechat <code>USER_MENTION annotations targeting the app; set botUser (e.g., users/1234567890) if detection needs the app's user resource name.users/<id> approver is configured, OpenClaw posts a native approval card (cardsV2) in the originating space or thread. Card buttons carry opaque callback tokens; the manual /approve <id> <decision> prompt appears only when native delivery is unavailable.Use these identifiers for delivery and allowlists:
users/<userId> (recommended).spaces/<spaceId>.[email protected] is mutable and only used for allowlist matching when channels.googlechat.dangerouslyAllowNameMatching: true.users/<email> is treated as a user id, not an email allowlist entry.googlechat:, google-chat:, and gchat: are accepted and stripped.{
channels: {
googlechat: {
enabled: true,
serviceAccountFile: "/path/to/service-account.json",
// or serviceAccountRef: { 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,
dm: {
policy: "pairing",
allowFrom: ["users/1234567890"],
},
groupPolicy: "allowlist",
groups: {
"spaces/AAAA": {
enabled: true,
requireMention: true,
users: ["users/1234567890"],
systemPrompt: "Short answers only.",
},
},
actions: { reactions: true },
typingIndicator: "message",
mediaMaxMb: 20,
},
},
}
Notes:
serviceAccountFile (path), serviceAccount (inline JSON string or object), or serviceAccountRef (env/file SecretRef). Env vars GOOGLE_CHAT_SERVICE_ACCOUNT (inline JSON) and GOOGLE_CHAT_SERVICE_ACCOUNT_FILE (path) apply to the default account only. Multi-account setups use channels.googlechat.accounts.<id> with the same keys, including per-account serviceAccountRef./googlechat when webhookPath is unset; webhookUrl can supply the path instead.spaces/<spaceId>). Display-name keys are deprecated and logged as such.dangerouslyAllowNameMatching re-enables mutable email principal matching for allowlists (break-glass compatibility mode); doctor warns about email entries.reactions tool and channels action; disable with actions.reactions: false.cardsV2 button clicks, not reaction events. Approvers come from dm.allowFrom or defaultTo and must be stable numeric users/<id> values.send for text and upload-file for explicit attachment sends. upload-file accepts media / filePath / path plus optional message, filename, and thread targeting (threadId / replyTo).typingIndicator: message (default) posts a _<Bot> is typing..._ placeholder and edits it into the first reply; none disables it; reaction requires user OAuth and currently falls back to message with a logged error under service-account auth.mediaMaxMb (default 20).allowBots: true, accepted bot messages use shared bot loop protection: configure channels.defaults.botLoopProtection, then override with channels.googlechat.botLoopProtection or channels.googlechat.groups.<space>.botLoopProtection.Secrets reference details: Secrets Management.
If Google Cloud Logs Explorer shows errors like:
status code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Allowed
The webhook handler is not registered. Common causes:
Channel not configured: the channels.googlechat section is missing. Verify with:
openclaw config get channels.googlechat
If it returns "Config path not found", add the configuration (see Config highlights).
Plugin not enabled: check plugin status:
openclaw plugins list | grep googlechat
If it shows "disabled", add plugins.entries.googlechat.enabled: true to your config.
Gateway not restarted after config changes:
openclaw gateway restart
Verify the channel is running:
openclaw channels status
# Should show: Google Chat default: enabled, configured, ...
openclaw channels status --probe surfaces auth errors and missing audience config (audience and audienceType are both required).botUser to the app's user resource name and check requireMention.openclaw logs --follow while sending a test message shows whether requests reach the gateway.