{
  "title": "Gmail Pub/Sub -> OpenClaw",
  "content": "Goal: Gmail watch -> Pub/Sub push -> `gog gmail watch serve` -> OpenClaw webhook.\n\n* `gcloud` installed and logged in ([install guide](https://docs.cloud.google.com/sdk/docs/install-sdk)).\n* `gog` (gogcli) installed and authorized for the Gmail account ([gogcli.sh](https://gogcli.sh/)).\n* OpenClaw hooks enabled (see [Webhooks](/automation/webhook)).\n* `tailscale` logged in ([tailscale.com](https://tailscale.com/)). Supported setup uses Tailscale Funnel for the public HTTPS endpoint.\n  Other tunnel services can work, but are DIY/unsupported and require manual wiring.\n  Right now, Tailscale is what we support.\n\nExample hook config (enable Gmail preset mapping):\n\nTo deliver the Gmail summary to a chat surface, override the preset with a mapping\nthat sets `deliver` + optional `channel`/`to`:\n\nIf you want a fixed channel, set `channel` + `to`. Otherwise `channel: \"last\"`\nuses the last delivery route (falls back to WhatsApp).\n\nTo force a cheaper model for Gmail runs, set `model` in the mapping\n(`provider/model` or alias). If you enforce `agents.defaults.models`, include it there.\n\nTo set a default model and thinking level specifically for Gmail hooks, add\n`hooks.gmail.model` / `hooks.gmail.thinking` in your config:\n\n* Per-hook `model`/`thinking` in the mapping still overrides these defaults.\n* Fallback order: `hooks.gmail.model` → `agents.defaults.model.fallbacks` → primary (auth/rate-limit/timeouts).\n* If `agents.defaults.models` is set, the Gmail model must be in the allowlist.\n* Gmail hook content is wrapped with external-content safety boundaries by default.\n  To disable (dangerous), set `hooks.gmail.allowUnsafeExternalContent: true`.\n\nTo customize payload handling further, add `hooks.mappings` or a JS/TS transform module\nunder `hooks.transformsDir` (see [Webhooks](/automation/webhook)).\n\n## Wizard (recommended)\n\nUse the OpenClaw helper to wire everything together (installs deps on macOS via brew):\n\n* Uses Tailscale Funnel for the public push endpoint.\n* Writes `hooks.gmail` config for `openclaw webhooks gmail run`.\n* Enables the Gmail hook preset (`hooks.presets: [\"gmail\"]`).\n\nPath note: when `tailscale.mode` is enabled, OpenClaw automatically sets\n`hooks.gmail.serve.path` to `/` and keeps the public path at\n`hooks.gmail.tailscale.path` (default `/gmail-pubsub`) because Tailscale\nstrips the set-path prefix before proxying.\nIf you need the backend to receive the prefixed path, set\n`hooks.gmail.tailscale.target` (or `--tailscale-target`) to a full URL like\n`http://127.0.0.1:8788/gmail-pubsub` and match `hooks.gmail.serve.path`.\n\nWant a custom endpoint? Use `--push-endpoint <url>` or `--tailscale off`.\n\nPlatform note: on macOS the wizard installs `gcloud`, `gogcli`, and `tailscale`\nvia Homebrew; on Linux install them manually first.\n\nGateway auto-start (recommended):\n\n* When `hooks.enabled=true` and `hooks.gmail.account` is set, the Gateway starts\n  `gog gmail watch serve` on boot and auto-renews the watch.\n* Set `OPENCLAW_SKIP_GMAIL_WATCHER=1` to opt out (useful if you run the daemon yourself).\n* Do not run the manual daemon at the same time, or you will hit\n  `listen tcp 127.0.0.1:8788: bind: address already in use`.\n\nManual daemon (starts `gog gmail watch serve` + auto-renew):\n\n1. Select the GCP project **that owns the OAuth client** used by `gog`.\n\nNote: Gmail watch requires the Pub/Sub topic to live in the same project as the OAuth client.\n\n4. Allow Gmail push to publish:\n\nSave the `history_id` from the output (for debugging).\n\n## Run the push handler\n\nLocal example (shared token auth):\n\n* `--token` protects the push endpoint (`x-gog-token` or `?token=`).\n* `--hook-url` points to OpenClaw `/hooks/gmail` (mapped; isolated run + summary to main).\n* `--include-body` and `--max-bytes` control the body snippet sent to OpenClaw.\n\nRecommended: `openclaw webhooks gmail run` wraps the same flow and auto-renews the watch.\n\n## Expose the handler (advanced, unsupported)\n\nIf you need a non-Tailscale tunnel, wire it manually and use the public URL in the push\nsubscription (unsupported, no guardrails):\n\nUse the generated URL as the push endpoint:\n\nProduction: use a stable HTTPS endpoint and configure Pub/Sub OIDC JWT, then run:\n\nSend a message to the watched inbox:\n\nCheck watch state and history:\n\n* `Invalid topicName`: project mismatch (topic not in the OAuth client project).\n* `User not authorized`: missing `roles/pubsub.publisher` on the topic.\n* Empty messages: Gmail push only provides `historyId`; fetch via `gog gmail history`.",
  "code_samples": [
    {
      "code": "To deliver the Gmail summary to a chat surface, override the preset with a mapping\nthat sets `deliver` + optional `channel`/`to`:",
      "language": "unknown"
    },
    {
      "code": "If you want a fixed channel, set `channel` + `to`. Otherwise `channel: \"last\"`\nuses the last delivery route (falls back to WhatsApp).\n\nTo force a cheaper model for Gmail runs, set `model` in the mapping\n(`provider/model` or alias). If you enforce `agents.defaults.models`, include it there.\n\nTo set a default model and thinking level specifically for Gmail hooks, add\n`hooks.gmail.model` / `hooks.gmail.thinking` in your config:",
      "language": "unknown"
    },
    {
      "code": "Notes:\n\n* Per-hook `model`/`thinking` in the mapping still overrides these defaults.\n* Fallback order: `hooks.gmail.model` → `agents.defaults.model.fallbacks` → primary (auth/rate-limit/timeouts).\n* If `agents.defaults.models` is set, the Gmail model must be in the allowlist.\n* Gmail hook content is wrapped with external-content safety boundaries by default.\n  To disable (dangerous), set `hooks.gmail.allowUnsafeExternalContent: true`.\n\nTo customize payload handling further, add `hooks.mappings` or a JS/TS transform module\nunder `hooks.transformsDir` (see [Webhooks](/automation/webhook)).\n\n## Wizard (recommended)\n\nUse the OpenClaw helper to wire everything together (installs deps on macOS via brew):",
      "language": "unknown"
    },
    {
      "code": "Defaults:\n\n* Uses Tailscale Funnel for the public push endpoint.\n* Writes `hooks.gmail` config for `openclaw webhooks gmail run`.\n* Enables the Gmail hook preset (`hooks.presets: [\"gmail\"]`).\n\nPath note: when `tailscale.mode` is enabled, OpenClaw automatically sets\n`hooks.gmail.serve.path` to `/` and keeps the public path at\n`hooks.gmail.tailscale.path` (default `/gmail-pubsub`) because Tailscale\nstrips the set-path prefix before proxying.\nIf you need the backend to receive the prefixed path, set\n`hooks.gmail.tailscale.target` (or `--tailscale-target`) to a full URL like\n`http://127.0.0.1:8788/gmail-pubsub` and match `hooks.gmail.serve.path`.\n\nWant a custom endpoint? Use `--push-endpoint <url>` or `--tailscale off`.\n\nPlatform note: on macOS the wizard installs `gcloud`, `gogcli`, and `tailscale`\nvia Homebrew; on Linux install them manually first.\n\nGateway auto-start (recommended):\n\n* When `hooks.enabled=true` and `hooks.gmail.account` is set, the Gateway starts\n  `gog gmail watch serve` on boot and auto-renews the watch.\n* Set `OPENCLAW_SKIP_GMAIL_WATCHER=1` to opt out (useful if you run the daemon yourself).\n* Do not run the manual daemon at the same time, or you will hit\n  `listen tcp 127.0.0.1:8788: bind: address already in use`.\n\nManual daemon (starts `gog gmail watch serve` + auto-renew):",
      "language": "unknown"
    },
    {
      "code": "## One-time setup\n\n1. Select the GCP project **that owns the OAuth client** used by `gog`.",
      "language": "unknown"
    },
    {
      "code": "Note: Gmail watch requires the Pub/Sub topic to live in the same project as the OAuth client.\n\n2. Enable APIs:",
      "language": "unknown"
    },
    {
      "code": "3. Create a topic:",
      "language": "unknown"
    },
    {
      "code": "4. Allow Gmail push to publish:",
      "language": "unknown"
    },
    {
      "code": "## Start the watch",
      "language": "unknown"
    },
    {
      "code": "Save the `history_id` from the output (for debugging).\n\n## Run the push handler\n\nLocal example (shared token auth):",
      "language": "unknown"
    },
    {
      "code": "Notes:\n\n* `--token` protects the push endpoint (`x-gog-token` or `?token=`).\n* `--hook-url` points to OpenClaw `/hooks/gmail` (mapped; isolated run + summary to main).\n* `--include-body` and `--max-bytes` control the body snippet sent to OpenClaw.\n\nRecommended: `openclaw webhooks gmail run` wraps the same flow and auto-renews the watch.\n\n## Expose the handler (advanced, unsupported)\n\nIf you need a non-Tailscale tunnel, wire it manually and use the public URL in the push\nsubscription (unsupported, no guardrails):",
      "language": "unknown"
    },
    {
      "code": "Use the generated URL as the push endpoint:",
      "language": "unknown"
    },
    {
      "code": "Production: use a stable HTTPS endpoint and configure Pub/Sub OIDC JWT, then run:",
      "language": "unknown"
    },
    {
      "code": "## Test\n\nSend a message to the watched inbox:",
      "language": "unknown"
    },
    {
      "code": "Check watch state and history:",
      "language": "unknown"
    },
    {
      "code": "## Troubleshooting\n\n* `Invalid topicName`: project mismatch (topic not in the OAuth client project).\n* `User not authorized`: missing `roles/pubsub.publisher` on the topic.\n* Empty messages: Gmail push only provides `historyId`; fetch via `gog gmail history`.\n\n## Cleanup",
      "language": "unknown"
    }
  ],
  "headings": [
    {
      "level": "h2",
      "text": "Prereqs",
      "id": "prereqs"
    },
    {
      "level": "h2",
      "text": "Wizard (recommended)",
      "id": "wizard-(recommended)"
    },
    {
      "level": "h2",
      "text": "One-time setup",
      "id": "one-time-setup"
    },
    {
      "level": "h2",
      "text": "Start the watch",
      "id": "start-the-watch"
    },
    {
      "level": "h2",
      "text": "Run the push handler",
      "id": "run-the-push-handler"
    },
    {
      "level": "h2",
      "text": "Expose the handler (advanced, unsupported)",
      "id": "expose-the-handler-(advanced,-unsupported)"
    },
    {
      "level": "h2",
      "text": "Test",
      "id": "test"
    },
    {
      "level": "h2",
      "text": "Troubleshooting",
      "id": "troubleshooting"
    },
    {
      "level": "h2",
      "text": "Cleanup",
      "id": "cleanup"
    }
  ],
  "url": "llms-txt#gmail-pub/sub-->-openclaw",
  "links": []
}