# Gmail Intake Automation Playbook (OpenClaw + gog + Pub/Sub)

Last updated: 2026-02-06

## Goal
Monitor a dedicated Gmail inbox and trigger OpenClaw (Telegram ping) when a message is labeled **ToAssistant**.

Pipeline:
Gmail watch (label) → GCP Pub/Sub push → `gog gmail watch serve` (local) → OpenClaw webhook `/hooks/gmail` → deliver to Telegram.

## Components
- Gmail account: `srvdeskops@gmail.com`
- Labels:
  - `ToReview` → `Label_2339208877417859055`
  - `ToAssistant` → `Label_7941783733970397893`
- GCP project: `srvdeskops-intake`
- Pub/Sub topic: `projects/srvdeskops-intake/topics/gog-gmail-watch`
- Public push endpoint (Tailscale Funnel):
  - `https://desktop-40j477e.tail98ac3b.ts.net/gmail-pubsub?token=d417f7fba89458367a6c5789635395d8`
- Local listener:
  - `127.0.0.1:8788/gmail-pubsub`
- OpenClaw webhook:
  - `http://127.0.0.1:18789/hooks/gmail`
  - Hook token: stored in OpenClaw config (`hooks.token`)

## Key lessons learned
### 1) OAuth client and consent screen can block you
- Error 401 `disabled_client` means the OAuth client was disabled or blocked.
- Fix: re-enable client or create a new OAuth client.
- If consent screen is in Testing, add the Gmail account as a test user.

### 2) Correct credentials.json format matters
- `gog auth credentials` expects a Google OAuth client JSON with top-level `installed` or `web`.
- A JSON containing only `client_id` and `client_secret` fails.

### 3) WSL localhost callback vs Windows browser
- OAuth redirect to `127.0.0.1:<port>` fails if the listener is not reachable from the browser.
- Use `gog auth add --manual` and paste the redirect URL.
- You may see `ERR_UNSAFE_PORT` for `localhost:1`. That is expected. Copy the URL anyway.

### 4) IPv6 caused OAuth token exchange timeouts
Symptom:
- `exchange code: Post https://oauth2.googleapis.com/token: context deadline exceeded`
- `curl -6 https://oauth2.googleapis.com/token` fails, while `curl -4` works

Fix:
- Disable IPv6 preference in WSL:
  - sysctl disable IPv6
  - prefer IPv4 (gai.conf)
  - restart WSL

### 5) Encrypted keyring breaks non-interactive services
Symptom:
- In systemd, `gog` fails with: `no TTY available for keyring file backend password prompt; set GOG_KEYRING_PASSWORD`

Fix:
- Set `GOG_KEYRING_PASSWORD` in systemd service environments.
- Important: set it for both:
  - `gog-gmail-watch-serve.service` (push handler)
  - `openclaw-gateway.service` (so OpenClaw-triggered `gog` calls do not hang)

### 6) Gmail watch expires
- Watch had an explicit expiration (roughly weekly).
- Fix: set an automatic renewal job.

## Working commands (reference)
### Labels
```bash
gog gmail labels list --account srvdeskops@gmail.com --plain
```

### Start watch
```bash
gog gmail watch start \
  --account srvdeskops@gmail.com \
  --label Label_7941783733970397893 \
  --topic projects/srvdeskops-intake/topics/gog-gmail-watch \
  --plain
```

### Serve push handler
```bash
gog gmail watch serve \
  --account srvdeskops@gmail.com \
  --bind 127.0.0.1 \
  --port 8788 \
  --path /gmail-pubsub \
  --token d417f7fba89458367a6c5789635395d8 \
  --hook-url http://127.0.0.1:18789/hooks/gmail \
  --hook-token <OPENCLAW_HOOK_TOKEN> \
  --include-body \
  --max-bytes 20000
```

### Systemd service for serve
- `~/.config/systemd/user/gog-gmail-watch-serve.service`
- Add drop-in `~/.config/systemd/user/gog-gmail-watch-serve.service.d/env.conf` containing `GOG_KEYRING_PASSWORD`

### Auto-renew watch
- Script: `~/.local/bin/gog-renew-gmail-watch.sh`
- systemd timer: `gog-gmail-watch-renew.timer`

## Operational checklist for a new business
- Use a dedicated Gmail per client, or per business line
- Use separate OAuth clients and GCP projects if you need isolation
- Always set:
  - cost guardrails (budgets, alerts)
  - secrets handling policy (no pasting keys in chat)
  - non-interactive keyring strategy (`GOG_KEYRING_PASSWORD` or unencrypted keyring in a locked-down environment)
- Build a repeatable onboarding script and a rollback plan
