Every Google Workspace org policy blocks the same service account. You run the gcloud pubsub topics add-iam-policy-binding command, it fails with iam.allowedPolicyMemberDomains, and you spend the next three hours searching the wrong admin console. We have deployed Gmail Pub/Sub on bare-metal VPS servers for multiple OpenClaw Google Workspace installations through ManageMyClaw, and the org policy blocker is the single issue that stops every first-time setup cold.
Gmail Pub/Sub delivers email notifications in 30 seconds flat. The org policy error that blocks it takes 3 hours to diagnose — not because the fix is hard, but because Google buries it in the wrong console.
OpenClaw is an open-source AI agent framework that automates Gmail, Calendar, and other Google Workspace services on your own VPS. Gog is the CLI tool that handles OpenClaw’s Google integration — OAuth, Gmail watch, webhook serving, and Pub/Sub plumbing. ManageMyClaw deploys and maintains these systems so you don’t debug org policies at 2 AM. Three products, one pipeline, and a single IAM binding standing between you and real-time email processing.
This guide walks through the complete Gmail Pub/Sub setup on a bare-metal VPS, from project creation to end-to-end verification. The org policy fix is in Step 3a — but if you skip ahead, you’ll miss the context that makes the fix make sense.
How Gmail Pub/Sub Delivers Real-Time Notifications
The pipeline has seven handoffs, and every one must work for a single notification to reach your OpenClaw agent. A new email arrives at your Google Workspace inbox. Gmail’s Watch API detects the change and publishes a notification to your Google Cloud Pub/Sub topic. Pub/Sub pushes a POST request to your HTTPS webhook endpoint. Nginx terminates SSL and proxies the request to gog serve running on localhost. Gog fetches the full email content via the Gmail API. Your OpenClaw agent processes the email — categorizes it, drafts a reply, fires a notification.
| Component | Purpose |
|---|---|
| Google Cloud Project | Houses Pub/Sub topic, OAuth credentials, API access |
| Gmail API | Email access and watch registration |
| Cloud Pub/Sub API | Message delivery from Gmail to your server |
| OAuth 2.0 Client (Desktop) | Headless auth for your VPS-based OpenClaw agent |
| HTTPS Webhook Endpoint | Receives Pub/Sub push notifications via nginx |
| Gog CLI | Processes notifications, fetches email, forwards to OpenClaw |
| SSL Certificate | Required — Pub/Sub only pushes to HTTPS endpoints |
Seven components to receive a push notification that polling handles with a single cron line. The trade-off is latency: 30 seconds vs. up to 5 minutes.
What You Need Before Starting
Prerequisites Checklist
- Google Workspace account (e.g.,
support@yourdomain.com) — personal Gmail accounts work but lack org policy controls - A VPS with a public IP and a domain pointing to it — bare-metal or cloud VM with systemd
- Nginx installed with SSL via Let’s Encrypt — Pub/Sub will not deliver to HTTP endpoints
gcloudCLI installed on your local machine — for GCP project management and org policy overrides- Gog CLI installed on the VPS — OAuth must be configured first
- OpenClaw agent running on the VPS with a hook endpoint for incoming email events
Create a Google Cloud Project and Enable APIs
Start at the Google Cloud Console. Create a new project (e.g., my-email-notifications) and note the Project ID — you’ll reference it in every command from here forward.
If your OpenClaw agent also handles Calendar, Drive, or Contacts, enable those APIs now too:
Enabling an API and granting an OAuth scope are two different things. Having the Gmail scope in your OAuth consent screen does not mean the Gmail API is active in your GCP project. You need both. This distinction trips up even experienced developers.
Set Up OAuth 2.0 Credentials
Create an OAuth consent screen set to “Internal” — this skips Google’s app verification process entirely, which only applies to external apps. Add all required scopes for Gmail, Calendar, Drive, and any other services your OpenClaw agent will use.
- Go to APIs & Services > OAuth consent screen. Select Internal. Set app name, support email, and developer contact.
- Go to APIs & Services > Credentials. Click Create Credentials > OAuth client ID. Type: Desktop app. Name it something like
OpenClaw VPS Watcher. - Download the
client_secret.jsonfile and upload it to your VPS:
Desktop app type, not web app. This matters for the headless OAuth flow you’ll run on the VPS in Step 6.
Create the Pub/Sub Topic and Grant Gmail Permission
This is where most setups break. Creating the topic is straightforward. Granting Gmail permission to publish to it is not.
Now grant Gmail’s internal service account permission to publish messages to your topic:
If this command succeeds, skip to Step 4. If it fails with the iam.allowedPolicyMemberDomains error, you’ve hit the org policy blocker. Keep reading.
Fixing the Org Policy Blocker — The Error That Stops Every Setup
What’s happening: Google Workspace organizations have a default policy called Domain Restricted Sharing (iam.allowedPolicyMemberDomains). This policy prevents adding IAM members from outside your organization. The problem is that gmail-api-push@system.gserviceaccount.com is a Google-owned system service account. It’s not part of your org, so the policy blocks it — even though Gmail Pub/Sub requires it.
This is configured in the Google Cloud Console (console.cloud.google.com), NOT the Google Workspace Admin Console (admin.google.com). The Workspace Admin Console has a “Google Cloud session control” page that looks related but controls a completely different setting. This distinction costs people hours.
Option A: Override via gcloud CLI (Recommended)
First, enable the Organization Policy API:
Then override the policy at the project level:
Option B: Override via GCP Console UI
- Navigate to:
console.cloud.google.com/iam-admin/orgpolicies/iam.allowedPolicyMemberDomains?project=my-email-notifications - Click “MANAGE POLICY” and select “Override parent’s policy”
- Add a rule set to “Allow All” and click Save
Your account needs the Organization Policy Administrator role at the organization level. If you don’t have it, an existing admin must grant it:
Now Retry the Permission Grant
After the IAM binding succeeds, re-tighten the org policy immediately. Existing bindings are NOT retroactively removed — the policy only checks at binding creation time. Run: gcloud org-policies reset iam.allowedPolicyMemberDomains --project=my-email-notifications
Create the Push Subscription
Four requirements for the push endpoint: must be HTTPS (not HTTP), must have a valid SSL certificate (Let’s Encrypt works), must be publicly accessible from the internet, and must respond with a 2xx status code to acknowledge messages.
Configure Nginx as a Reverse Proxy
Gog runs on localhost (port 8788 by default). Nginx terminates SSL and proxies Pub/Sub push notifications to it.
Get the SSL certificate and enable the site:
Authenticate OAuth on the VPS (Headless Flow)
Your VPS has no browser. Gog uses a 2-step remote auth flow — generate the URL on the VPS, authorize in your laptop’s browser, paste the callback URL back.
Generate the Auth URL (on VPS)
Authorize in Browser (on your laptop)
Open the auth URL in your browser. Sign in with the correct Google account. Grant all requested permissions. The browser redirects to http://127.0.0.1:XXXXX/oauth2/callback?... and shows a “can’t connect” error. This is expected behavior. Copy the full URL from the address bar.
Exchange the Code (on VPS)
URL breaks across lines when copying from SSH — widen your terminal window first. Auth codes are time-sensitive — complete the exchange within 5 minutes. And always use --force-consent when re-authenticating to ensure a fresh refresh token.
The “can’t connect” page in your browser is the part that panics every first-timer. It’s normal. The auth code is embedded in the URL parameters — you don’t need the page to actually load.
Start the Gmail Watch
Register a watch on the inbox. This tells Gmail to publish notifications to your Pub/Sub topic whenever a new email arrives.
That expiration date is not a suggestion. When it passes, Gmail silently stops publishing. No error in your logs. No alert. Your agent just stops receiving email notifications and nobody knows until someone asks why the urgent email from 3 hours ago wasn’t processed.
Set Up the Webhook Server as a Systemd Service
Run gog gmail watch serve as a systemd service so it survives reboots and auto-restarts on failure.
Start the Gmail watch (Step 7) BEFORE starting this service. The service will crash with watch state not found if the watch hasn’t been registered yet. This creates a restart loop that systemd will eventually give up on.
Set Up Watch Renewal Cron
Gmail watch expires every 7 days with zero warning. Create a daily renewal script that runs before the watch can expire. For more on cron-based email automation with OpenClaw, see our dedicated guide.
Verify the Full Pipeline
Check each component individually before running the end-to-end test.
End-to-end test: Send a test email to user@yourdomain.com from a different account. Watch your OpenClaw agent’s logs. You should see the notification arrive within approximately 30 seconds.
Do’s and Don’ts
Use --services=all when authenticating to avoid re-doing OAuth later. Set up watch renewal cron — Gmail watch expires every 7 days. Start Gmail watch BEFORE starting the webhook service. Use a dedicated webhook subdomain. Use systemd for auto-restart. Keep your OAuth consent screen as “Internal.” Use --force-consent when re-authenticating. Re-tighten the org policy after granting the Pub/Sub permission.
- Fix
iam.allowedPolicyMemberDomainsin the Workspace Admin Console — it’s in the GCP Console - Use HTTP for the push endpoint — Pub/Sub requires HTTPS
- Forget to enable APIs in the GCP project — OAuth scopes alone aren’t enough
- Use
gcloud auth logininteractively on a headless VPS — use--no-browser - Rely solely on polling when Pub/Sub is available — polling wastes API calls and adds latency
- Hardcode credentials in application code — use environment variables or keyring
When You Cannot Change the Org Policy
If corporate IT controls the org policy and won’t budge, use heartbeat polling as a fallback. It works. It adds 0-5 minutes of latency. It uses more API quota. But it doesn’t require any GCP configuration beyond OAuth.
Not elegant. Not real-time. But it works every time, on every Google Workspace setup, without touching a single org policy.
The Bottom Line
Gmail Pub/Sub is the correct architecture for real-time email processing on a VPS. The iam.allowedPolicyMemberDomains org policy blocker is the single obstacle that stops every Google Workspace setup, and it’s fixable in under 5 minutes once you know the fix is in the GCP Console, not the Workspace Admin Console. The confusion between the two consoles is what turns a 5-minute fix into a 3-hour debugging session.
The setup is not trivial — a Google Cloud project, a Pub/Sub topic, an IAM binding, a push subscription, nginx with SSL, a systemd service, and a daily cron job that must never fail. But every component serves a purpose, and once the pipeline is running, your OpenClaw agent processes email in 30 seconds instead of 5 minutes. For security-conscious deployments, the reduced API surface of event-driven processing is a meaningful improvement over constant polling.
If you can change the org policy: use Pub/Sub with heartbeat polling as a fallback. If you can’t: polling alone is perfectly fine for most email triage use cases. Either way, your OpenClaw agent processes email. The architecture is a latency decision, not a capability decision.
Frequently Asked Questions
Why does the iam.allowedPolicyMemberDomains error happen?
Google Workspace organizations have a default policy called Domain Restricted Sharing that prevents adding IAM members from outside your organization. The gmail-api-push@system.gserviceaccount.com service account is owned by Google, not your org, so the policy blocks it — even though Gmail Pub/Sub requires this exact binding to function. The fix is overriding the policy at the project level in the GCP Console (not the Workspace Admin Console), granting the binding, then re-tightening the policy.
Can I fix this in the Google Workspace Admin Console?
No. The iam.allowedPolicyMemberDomains constraint is a Google Cloud Organization Policy, managed exclusively through the GCP Console at console.cloud.google.com. The Workspace Admin Console at admin.google.com has a “Google Cloud session control” page that looks related but controls an entirely different setting. This is the #1 source of wasted debugging time.
Is it safe to set allowAll on the org policy?
Yes, temporarily. Set the override at the project level (not the organization level), grant the gmail-api-push service account its Publisher role, then immediately re-tighten the policy with gcloud org-policies reset. Existing IAM bindings are not retroactively removed — the policy only checks at binding creation time. The window of exposure is seconds, not permanent.
What if I can’t change the org policy?
Use heartbeat polling as a fallback. Set up a cron job that runs gog gmail search every 5 minutes. You lose real-time latency (0-5 minute delay instead of 30 seconds) and use more API quota, but it works on every Google Workspace setup without any GCP configuration beyond OAuth. Many OpenClaw deployments run successfully on polling alone.
Not affiliated with or endorsed by the OpenClaw open-source project.


