Stop copy-pasting emails: integrate Gmail with OpenClaw instead
If you searched for "OpenClaw Gmail integration setup auto-reply and sorting" you probably want two things: (1) incoming mail handled in near-real-time and (2) outgoing mail that never embarrasses you. The good news: OpenClaw’s Gmail skill covers both. The bad news: you must wrestle with Google OAuth, Gmail API scopes, Pub/Sub, and OpenClaw’s YAML before the magic happens. This tutorial walks through the whole process, including a production-ready approval flow engineers in the community have been running since OpenClaw v0.37.2.
Prerequisites and assumptions
Before running commands, make sure the following boxes are ticked:
- Node.js 22+ — OpenClaw currently targets the latest LTS.
node -vshould print at least 22.2.0. - OpenClaw 0.38.0+ — upgrade with
npm i -g openclaw@latest. - A Google Workspace or consumer Gmail account with admin rights to create a Cloud project.
- clawctl authenticated against your ClawCloud tenant if you plan to run hosted.
- A public HTTPS endpoint (Cloud Run, Fly.io, your own VPS) if running self-hosted; Gmail’s Pub/Sub will POST there.
I’ll assume you run the gateway locally on port 3100 while testing. Adjust URLs if you differ.
Create the Google Cloud project (OAuth & Gmail API)
1. Spin up a fresh project
Google mixes billing, IAM, and OAuth at the project layer. Create a new one; it keeps scopes clean.
gcloud projects create openclaw-gmail-demo --set-as-default
2. Enable the Gmail API
gcloud services enable gmail.googleapis.com
3. Configure OAuth consent
Navigate to APIs & Services → OAuth consent screen and set:
- User type: Internal (if you’re on Workspace) or External and in testing mode.
- Scopes: add
../auth/gmail.modifyand../auth/gmail.send. - Test users: add the Gmail address you’ll use for development.
You can avoid the stringent security review because we limit scopes to modify & send, not full mail.readonly. Community feedback shows Google still sends the "this app isn’t verified" screen for two days; live with it—it disappears once the brand-new project passes automated checks.
4. Create OAuth client credentials
Open Credentials → Create → OAuth client ID → Web application. Use the following redirect URIs:
http://localhost:3100/oauth/callback(dev)https://your-gateway.yourdomain.com/oauth/callback(prod)
Download the client_secret_*.json. We’ll stuff those values into OpenClaw shortly.
Wiring Gmail Pub/Sub for near-real-time delivery
Polling Gmail every minute works but costs quota. Pub/Sub pushes updates in seconds. The Gmail skill subscribes to messages.insert events and then pulls the full thread.
1. Create a Pub/Sub topic
gcloud pubsub topics create gmail-openclaw-topic
2. Grant Gmail publish rights
gcloud pubsub topics add-iam-policy-binding gmail-openclaw-topic \
--member=serviceAccount:gmail-api-push@system.gserviceaccount.com \
--role=roles/pubsub.publisher
3. Expose a push subscription
gcloud pubsub subscriptions create gmail-openclaw-sub \
--topic=gmail-openclaw-topic \
--push-endpoint=https://your-gateway.yourdomain.com/gmail/push \
--push-auth-service-account=YOUR_PROJECT_NUMBER-compute@developer.gserviceaccount.com
The push-auth-service-account must exist; if you’ve never used Cloud Functions or Cloud Run, iam.serviceAccounts.create it first.
4. Tell Gmail to watch the inbox
Once OpenClaw authenticates, we’ll call users.watch() with:
{
'topicName': 'projects/openclaw-gmail-demo/topics/gmail-openclaw-topic',
'labelIds': ['INBOX'],
'labelFilterAction': 'include'
}
We’ll automate this from the skill, but understanding the underlying call matters when debugging 403s.
Hooking the Gmail Skill into OpenClaw
OpenClaw centralizes integrations under skills/. Each skill is a Node module that the daemon hot-loads. Gmail ships in core since openclaw-skills 3.9.0.
1. Install & enable the skill
npm i @openclaw/skill-gmail@^3.9.0 --save
Add to openclaw.yaml:
skills:
gmail:
module: '@openclaw/skill-gmail'
clientId: 'YOUR_GOOGLE_CLIENT_ID'
clientSecret: 'YOUR_GOOGLE_CLIENT_SECRET'
redirectUri: 'http://localhost:3100/oauth/callback'
pubsubTopic: 'projects/openclaw-gmail-demo/topics/gmail-openclaw-topic'
subscription: 'projects/openclaw-gmail-demo/subscriptions/gmail-openclaw-sub'
watchLabel: 'INBOX'
historyStart: 'now'
autoReply:
enabled: false
sorting:
enabled: false
approval:
enabled: true
reviewers:
- 'boss@example.com'
- 'security@example.com'
Restart the daemon:
openclaw daemon restart
Hit http://localhost:3100/gmail/connect. The gateway redirects to Google’s OAuth screen. Approve, and OpenClaw stores the refresh token in $HOME/.openclaw/state.db (SQLite). Inside ClawCloud the secret lands in Vault automatically.
Writing safe auto-replies with templates and variables
The community prefers handlebars-style templates. The skill ships helpers such as {{sender.name}}, {{subject}}, and {{snippet}}.
1. Turn on auto-reply
skills:
gmail:
autoReply:
enabled: true
delaySeconds: 30
templates:
default: |
Hi {{sender.name}},
Thanks for your message about "{{subject}}".
I’m heads-down right now, expect a response in ~24h.
— My automated OpenClaw agent
outOfOffice: |
Hi,
I’m out until 2024-05-17. If urgent, contact ops@example.com.
delaySeconds prevents immediate back-and-forth loops when two bots talk. Community experiments show 30–60 s works fine.
2. Selecting a template programmatically
Inside skills/gmail/index.js you can hook beforeAutoReply:
exports.beforeAutoReply = async ({thread, labels}) => {
if (labels.includes('TRAVEL')) return 'outOfOffice';
return 'default';
};
The skill merges the chosen template with variables and queues the message. It never sends immediately when approval.enabled is true (more on that later).
Building smart sorting rules
Sorting in Gmail == labels. OpenClaw applies them via modifyThread. Why not filters? Filters live server-side and are brittle to change in code reviews. Sorting rules in the agent means git-tracked logic.
1. Enable sorting
skills:
gmail:
sorting:
enabled: true
rules:
- name: 'GitHub Notifications'
if:
from: 'notifications@github.com'
apply:
labels: ['GITHUB', 'AUTO']
archive: true
- name: 'Invoices'
if:
subject: /invoice|bill/i
apply:
labels: ['FINANCE']
star: true
- name: 'Unknown Senders'
if:
senderInContacts: false
apply:
labels: ['REVIEW']
The if block supports from, to, subject (regex allowed), message size, attachment types, and custom JavaScript predicates in rules.js. Once a rule matches, apply can add/remove labels, archive, star, or forward internally to another skill (e.g., Slack).
2. Applying rules on historical email
Pass --rehydrate to the daemon:
openclaw gmail sort --from 2024-01-01 --rule 'Invoices'
It paginates through 1000 threads/minute without hitting the default Gmail quota. For larger datasets, pass --concurrency 5 but expect occasional 429s.
Enforcing an approval flow for outbound messages
Auto-reply is great until it leaks a customer’s NDA or your phone number. The Gmail skill ships a minimal approval workflow that can pipe drafts to Slack, Discord, or your CLI for human sign-off.
1. How it works
- Skill generates the email JSON and stores it in the queue table.
- If
approval.enabledis true, the email gets status = pending. - The
reviewersare notified via the Notification skill (Slack, etc.). - Reviewer calls
/gmail/approve?id=123(HTTP), or runsopenclaw gmail approve 123. - Daemon invokes
users.messages.sendwith the RFC-822 payload. Gmail stamps SENT label.
2. Reviewer notification example (Slack)
skills:
slack:
token: 'xoxb-***'
channels:
- '#email-approvals'
gmail:
approval:
enabled: true
reviewers:
- 'U01ABCXYZ' # Slack user IDs work too
The skill posts a message:
📧 Draft ready from support@acme.com → bob@example.com
Subject: Re: Refund request – order 8342
Links hit the gateway’s REST endpoints, and the daemon streams the status back using Slack’s chat.update API. No need to open the web UI.
3. Safety checks before send
- DKIM/SPF status — the skill fetches
users.settings.sendAsto ensure the From address is authorized. - Link sanitizer — optional regex whitelist (no formsite.com etc.).
- Attachment blocklist — drop
.exe,.cmd, or >25 MB files (Gmail rejects anyway but better client-side).
Ship rules in approvalHooks.js. The agent aborts send with a human-readable reason. We’ve dodged two phishing attempts with this since March.
Deployment tips, rate limits, and common edge cases
Local testing without Pub/Sub
Set usePubSub: false in the skill config; it falls back to 30 s polling. Good enough for unit tests.
Gmail daily quota
Every user gets 86,400 users.messages.* calls/day. Auto-reply plus sorting rarely exceed 10,000, but --rehydrate will. Google resets quota at midnight PST.
"Watch request failed with 400: Invalid label IDs"
Happens when you typo INBOX. List labels via:
curl -H 'Authorization: Bearer $TOKEN' https://gmail.googleapis.com/gmail/v1/users/me/labels
Pub/Sub auth errors
Double-check the push endpoint uses HTTPS and the service account supplied in push-auth-service-account is in the roles/iam.serviceAccountTokenCreator role.
Secrets in git
Use clawctl secrets set gmail.clientSecret and reference ${{ secrets.gmail.clientSecret }} in YAML. Works both locally (stores in Keytar) and on ClawCloud.
Next step: pipe the approval queue to Discord or Telegram
You now have Gmail flowing into OpenClaw, auto-replies drafted, threads sorted, and outbound mail guarded by reviewers. The logical next step is to expose the same approval actions to whichever chat platform your team already lives in. The Notification skill supports Slack, Discord, Telegram, and WhatsApp out of the box. Point it at the same queue table and life is good.
Questions or breakage? #gmail-integration on the OpenClaw GitHub Discussions board is where maintainers hang out. PRs that improve the Pub/Sub setup docs are especially welcome.