Gmail PubSub integration
Wire Gmail inbox triggers to OpenClaw through Google Pub/Sub andgog gmail watch serve. Pub/Sub calls the watcher; the watcher forwards email data to the Gateway HTTP hook. This does not load or invoke an internal HOOK.md handler.
Not on Gmail? The IMAP email trigger plugin watches an existing IMAP mailbox without Google PubSub or a public webhook.
Prerequisites:
gcloud CLI, gog (gogcli) authorized for the watched Gmail account, OpenClaw hooks enabled, an HTTPS push endpoint reachable by Pub/Sub (Tailscale Funnel in the recommended setup), and a working sandbox backend. The example below uses the default Docker backend; build its image first by following Sandbox images and setup, or configure another supported backend.Configure a restricted Gmail reader (recommended)
Before connecting Gmail transport, merge a dedicated reader and hook policy into your existing config. Preserve the real settings on your existing agent; themain entry below only shows the required roster shape.
openclaw agents list --bindings; replace every placeholder and verify each channel owner.
Why this shape is safer:
- The explicit
mainbinding preserves existing channel ownership instead of leaving non-Gmail traffic ownerless. Use a specificaccountIdinstead of"*"when only one account belongs tomain. agentId: "mail_reader"keeps Gmail off themainagent.allowedAgentIdsprevents this hook endpoint from selecting another agent. If the Gateway serves other hook workflows, include only their intended agent ids too.scope: "session"gives each Gmail message its own sandbox;workspaceAccess: "none"keeps the host agent workspace out of that sandbox.allow: ["session_status"]is an absolute per-agent clamp, so globaltools.alsoAllowadditions cannot leak into the reader. The minimal profile and explicit deny list make the intended boundary auditable.deliver: falsedisables automatic successful announcements; completion is logged instead. To announce a summary externally after validating the reader, setdeliver: trueand add an explicitchannelandto. Agent-to-agent access is on by default: settools.agentToAgent.enabled: falseto disable cross-agent handoff, or deliberately expose the exact coordination tool and constrain permitted agent pairs withtools.agentToAgent.allow.
session_status if an earlier policy removed it. Ensure inherited policies retain session_status; an empty effective tool set aborts before the model sees the email.
If you intentionally route Gmail to a more capable agent, treat that as a security decision: keep external-content wrapping enabled, sandbox the run, and grant only the tools required by that workflow.
Authenticate the reader model
Authenticate the provider selected bymail_reader, or ensure its effective auth configuration can use a supported shared credential, then verify the route before connecting Gmail:
Connect Gmail transport
hooks.gmail transport settings, enables the Gmail preset, preserves the restricted mapping above, and defaults to Tailscale Funnel for the push endpoint (--tailscale funnel|serve|off). The wizard does not create a reader agent or session-key policy, so apply the restricted configuration first. --tailscale serve is tailnet-only; it is not a publicly reachable Pub/Sub endpoint without another ingress arrangement. Use --tailscale off --push-endpoint <url> for an externally managed endpoint. See all setup flags.
The two tokens protect different hops: hooks.gmail.pushToken authenticates Pub/Sub to the watcher, while hooks.token authenticates the watcher to OpenClaw using a header. A token-bearing Pub/Sub push URL is not an example for /hooks authentication; query-string tokens are rejected by OpenClaw. Setup output can contain these tokens, so redact it before sharing.
Verify the reader boundary
SPAM, TRASH, DRAFT, and SENT, so a sent-only message is not a useful ingress test. Confirm the selected agent is mail_reader, the run is sandboxed, and the output only summarizes the message. The mapping uses the logical hook:gmail:<message-id> key; an isolated run can be stored under a generated cron:...:run:... session instead.
Check forwarding and completion separately. A watcher success only acknowledges transport; a Gateway agent-hook 200 with a runId records admission, not a finished summary. Search for hook agent run completed with that runId: success logs status=ok at info level, while non-ok execution or explicit delivery errors produce warnings. With the configuration above, successful announcements are disabled. Inspect the actual run transcript for output and tool use. Treat attempted link navigation, file writes, shell commands, browser actions, or MCP registration as a failed boundary check.
Gateway auto-start
Whenhooks.enabled=true and hooks.gmail.account is set, the Gateway starts gog gmail watch serve on boot and auto-renews the watch. Set OPENCLAW_SKIP_GMAIL_WATCHER=1 to opt out.
With forEach: "messages", the Gateway prepares one action per email, up to the 200-item fan-out cap. Gmail-path mappings receive a larger request-body allowance derived from hooks.gmail.maxBytes, capped at 32 MiB. The upstream history page size is not a strict email count, so oversized batches can still hit limits. See the Gmail reference for the exact allowance and fan-out retry behavior.
Do not run openclaw webhooks gmail run or another gog gmail watch serve on the same listener while the Gateway-managed watcher is running. Check logs for watch-registration failures, forwarding failures, and bind conflicts; starting the serve process alone does not prove Gmail registration succeeded.
Manual one-time setup
These steps show the project, topic, publisher permission, and watch registration. They do not yet create the push subscription or start the forwarding listener. Use the setup command for the complete transport setup, then run exactly one watcher.1
Select the GCP project
Select the GCP project that owns the OAuth client used by
gog:2
Create topic and grant Gmail push access
3
Start the watch