Gmail / Google Workspace Integration Hub
Google Workspace covers Gmail (send/receive/labels/watch), Drive (upload/share/export), and Calendar (events, scheduling, Meet links) for RERI operations. The KB lives at knowledge-base/google-workspace/ (NOT gmail-google-workspace/). Two auth patterns are in use: user OAuth 2.0 for interactive flows and Service Account with domain-wide delegation for server-to-server agent use. The gmail-intake SA (Option B finding) is scoped to gmail.send + gmail.readonly for inbound lead routing. Read this hub before any email automation, document upload, or calendar scheduling work.
NAMING NOTE — KB directory
The KB directory is
knowledge-base/google-workspace/(NOTgmail-google-workspace/). Nogmail-google-workspace/directory exists. All source paths referencegoogle-workspace/. This hub is namedgmail-google-workspace.mdfor integration-hub disambiguation only.
⚠️ SA scope note: gmail-intake vs aurora-api-admin
Two service accounts are in active use for Google APIs:
| SA | Scopes | Purpose |
|---|---|---|
gmail-intake | gmail.send, gmail.readonly | Inbound lead email routing (Option B finding) |
aurora-api-admin | texttospeech, speech APIs | Voice substrate (Google Cloud TTS+STT, smoke-tested 2026-05-03) |
Rule: Do NOT use aurora-api-admin SA for Gmail. Do NOT use gmail-intake SA for voice APIs. Scope isolation is intentional per Option B security posture. SA keys stored in 1Password (op://Aurora/google/sa-key for gmail-intake; op://Aurora/google-cloud-voice/sa-key for aurora-api-admin).
Quick reference
| Field | Value |
|---|---|
| Vendor | Google Workspace / Gmail |
| URL | https://workspace.google.com |
| KB doc | API |
| Auth method | OAuth 2.0 (user) or Service Account (server) |
| Auth credential | op://Aurora/google/sa-key (gmail-intake SA) |
| OAuth client cred | op://Aurora/google/client-id, op://Aurora/google/client-secret |
| Cred-proxy port | n/a (until NemoClaw B1-B6 ratified) |
| Webhook port | :18801 (Gmail Push handler — webhooks/gmail-push-handler.js, manual systemd) |
| Webhook handler | gmail-push-handler |
| Webhook dedup table | processed_webhook_events (24h TTL) |
| Tunnel path | /webhook/gmail (register in FUNNEL-REGISTRY.md if not present) |
| Outbound API base | https://gmail.googleapis.com/gmail/v1/users/me/ |
| Drive base | https://www.googleapis.com/drive/v3/ |
| Calendar base | https://www.googleapis.com/calendar/v3/ |
| Rate limits | Gmail: 250 quota units/user/100s; Drive: 1000 req/100s/user; Calendar: 500 req/100s/user |
| Rate-limit action | 429 / 403 rateLimitExceeded → exp backoff with jitter (max 5 retries); Discord ops alert |
| Cost | Free API calls; Google Workspace Business Starter $6/user/month |
| Gmail daily send limit | 2,000 messages/day (Workspace) |
| Backup/recovery | Google-owned; Drive content in Google cloud storage; manual export quarterly |
| Discord alert channel | ops |
| Drift cadence | on-API-change |
| Status | production |
Components
workspace/knowledge-base/google-workspace/API.md— Full API reference: Gmail (send, list, get, labels, watch), Drive (list, upload, download, permissions), Calendar (events, create, free/busy)workspace/knowledge-base/google-workspace/examples/— Code examples directoryworkspace/webhooks/gmail-push-handler.js— Inbound Gmail Push (Pub/Sub) handler on port :18801- _summary — Primary agent for email intake routing and outbound email composition
- _summary — Drive uploads for dispo documents and TC reports
How it’s used
- Gmail send: Aurora composes and sends emails (lead responses, deal notifications) via gmail-intake SA; base64url-encoded RFC 2822 MIME format required
- Gmail watch:
POST /gmail/v1/users/me/watchwith Cloud Pub/Sub topic; handler at:18801receives push notifications for new INBOX messages;watch()must be renewed every 7 days - Drive upload:
betterfilesagent uploads TC documents (PDFs, inspection reports) to Google Drive; permissions set toreaderfor sharing with clients - Calendar: Atlas or betterfiles agent creates showing events with Google Meet links (
conferenceDataVersion=1);sendUpdates=allnotifies attendees - Agents involved:
aurora(email),betterfiles(Drive + Calendar),atlas(Calendar reporting) - Failure mode: OAuth refresh token expiry → 401 on all calls; service account key rotation in 1Password → redeploy
- Failure mode 2: Gmail watch expiry (7 days) → Pub/Sub notifications stop; no inbound emails processed
- Success criteria:
messages.sendreturns{id, threadId, labelIds}; Drive upload returnswebViewLink; Calendar event hashangoutLink
Key API patterns
Gmail send (MIME format)
// Email must be RFC 2822 MIME, base64url encoded
const raw = Buffer.from(mimeString).toString('base64')
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
await gmail.users.messages.send({ userId: 'me', requestBody: { raw } });Gmail watch renewal (every 7 days)
await gmail.users.watch({
userId: 'me',
requestBody: {
topicName: 'projects/PROJECT_ID/topics/PUBSUB_TOPIC',
labelIds: ['INBOX'],
labelFilterBehavior: 'INCLUDE'
}
});Service Account auth pattern
const auth = new google.auth.GoogleAuth({
keyFile: '/path/to/service-account.json', // or via 1Password CLI
scopes: ['https://www.googleapis.com/auth/gmail.send'],
clientOptions: { subject: 'intake@reri.co' } // domain-wide delegation
});Drive upload with share
const file = await drive.files.create({
requestBody: { name: 'report.pdf', parents: ['FOLDER_ID'] },
media: { mimeType: 'application/pdf', body: fs.createReadStream('./report.pdf') },
fields: 'id, webViewLink'
});
// Then share:
await drive.permissions.create({
fileId: file.data.id,
requestBody: { type: 'user', role: 'reader', emailAddress: recipientEmail }
});Gmail Push handler (port :18801)
The Gmail Push handler receives Pub/Sub notifications when new emails arrive in INBOX. Systemd unit runs manually (not auto-started). FUNNEL-REGISTRY.md must have /webhook/gmail registered. Handler must:
- Verify Pub/Sub message authenticity
- Write to
webhook_audit_log - Dedup via
processed_webhook_events - Route to
auroraagent for lead classification
Note: Port :18801 listed in CLAUDE.md port map as “Gmail Push | manual” — confirm current systemd unit status before relying on live events.
Cross-links
Agents that touch this
- _summary — email intake routing, outbound email composition, Gmail label management
- _summary — Drive uploads (TC documents, dispo PDFs); Calendar event creation for showings
- _summary — Calendar free/busy checks for scheduling; Drive report generation
Skills that invoke this
- betterfiles-dispo-emails — dispo email generation sent via Gmail
- betterfiles-cda — CDA PDF generated and uploaded to Drive, then shared via Gmail
Plans that govern this
- openclaw-fragmentation-fix-2026-05-01 — Gmail Push handler (:18801) flagged in service audit
- vendor-deep-audit-comprehensive-2026-05-02 — Google Workspace included in vendor baseline
- openclaw-self-improvement-layer-2026-05-03 —
aurora-api-adminSA provisioned for voice STT/TTS (separate from gmail-intake)
Feedback rules
- feedback_no_plaintext_creds — SA JSON key must stay in 1Password, never on disk plaintext
- feedback_credentials_via_1password_cli — use
op://Aurora/google/sa-keyreference; do not hardcode - feedback_live_over_memory — verify gmail watch is active before assuming inbound emails are flowing
KB / source docs
- API — Gmail, Drive, Calendar API reference (last verified 2026-03-04)
System maps
- auth-chain-map — OAuth + SA auth chains for Google APIs
- external-integrations — Google Workspace as comms + storage node
Related: Credential layer cluster
All credentials stored in 1Password vault Aurora. See 1password for op:// CLI access pattern. Two distinct SA keys:
gmail-intakeSA:op://Aurora/google/sa-key— Gmail scopes onlyaurora-api-adminSA:op://Aurora/google-cloud-voice/sa-key— voice APIs only
Do NOT cross-use SAs between Gmail and voice scopes. OAuth refresh tokens for user flows: op://Aurora/google/refresh-token.
Related: Webhook/tunnel cluster
Gmail Push uses Cloud Pub/Sub push subscription → HTTP POST to :18801. This is an inbound webhook path. Register in FUNNEL-REGISTRY.md as /webhook/gmail if not already present. Handler must verify Pub/Sub authenticity + write webhook_audit_log. See cloudflare for tunnel governance.
Related: Comms + CRM cluster
- slack — secondary comms channel; Gmail vs Slack routing decision is agent-level
- hubspot — Gmail threads for deal-related emails may be logged to HubSpot Communications object via HubSpot-Gmail integration; bidirectional link for lead emails
Open issues / TODOs
- Confirm Gmail watch is actively renewing every 7 days (add to cron / Atlas monitoring)
- Confirm
:18801gmail-push-handler.js systemd unit is actually running — listed as “(manual)” in CLAUDE.md port map - Register
/webhook/gmailin FUNNEL-REGISTRY.md if not present; verify signature/auth on Pub/Sub messages - Confirm
gmail-intakeSA has domain-wide delegation enabled in Google Admin Console forintake@reri.co -
aurora-api-adminSA (voice APIs) — EC2 Mac Ultra instance isimpairedsince 2026-05-02; resume next session per AWS Console reboot before relying on voice TTS/STT - Drive: confirm REPORTS_FOLDER_ID env var is set for Atlas report uploads; add to master.env if missing
Recent activity
- 2026-05-04: hub created (W3-S5, Wave 3) — SA scope note + gmail-intake vs aurora-api-admin distinction documented; TLS watch renewal gap flagged
- 2026-05-03:
aurora-api-adminSA provisioned for voice APIs (texttospeech + speech); smoke-tested successfully - 2026-03-04: API.md verified (Gmail, Drive, Calendar — status: COMPLETE)