Integrations
Gmail Integration — Email Intelligence
Connect Gmail to auto-classify emails (task vs invoice vs meeting), extract tasks with deadlines, and keep provenance. Covers OAuth, vault, and ingestion — beginner-friendly.
What Gmail Integration Does
Beyond Inbox Dump
It doesn't copy all emails as tasks. The Email Agent classifies each email:
task_request→ create task (e.g., “Update pricing by Thursday”)invoice→ Finance (subscriptions, receipts)meeting→ Calendar eventpersonal→ low priority, no auto-task
OAuth Setup (Step-by-Step)
1. Enable APIs in Google Cloud
Console → APIs & Services → Enable: Gmail API, Google Drive API, Calendar API.
2. Configure Consent Screen
- User Type: External (for personal use, add test user = your email)
- App name: DLRS Personal OS
- Scopes:
.../auth/gmail.readonly(read-only, safer),.../auth/drive.readonly,.../auth/calendar.readonly
3. Create OAuth Credentials
Credentials → Create → OAuth 2.0 Client → Web application:
Authorized redirect URIs (exact, no trailing slash):
http://localhost:3000/api/integrations/gmail/callback
http://localhost:3000/api/integrations/drive/callback
http://localhost:3000/api/integrations/calendar/callback
For production:
https://yourdomain.com/api/integrations/gmail/callback4. Add to .env
GMAIL_CLIENT_ID="123.apps.googleusercontent.com"
GMAIL_CLIENT_SECRET="GOCSPX-..."
# Same credentials can be reused for Drive/Calendar or separate
GOOGLE_DRIVE_CLIENT_ID="..."
GOOGLE_DRIVE_CLIENT_SECRET="..."
GOOGLE_CALENDAR_CLIENT_ID="..."
GOOGLE_CALENDAR_CLIENT_SECRET="..."
SECRET_ENCRYPTION_KEY="already generated via node -e ..." # encrypts tokens
5. Connect in UI
/w/[workspace]/settings/integrations → Gmail → Connect → consent → redirect back with code → exchange for access_token + refresh_token.
Token Storage & Security
Flow:
1. OAuth callback: GET /api/integrations/gmail/callback?code=...
2. Exchange code → tokens via googleapis
3. Encrypt: vault = AES-256-GCM(JSON.stringify({ access_token, refresh_token }), SECRET_ENCRYPTION_KEY)
4. Store: vault_items { workspace_id, kind: "gmail", encrypted_blob }
5. Use: decrypt transiently per request, refresh if expired
Key file: lib/security/vault.ts
Table: vault_items (encrypted at rest, never logged)🔒 Production Must
Never commit
.env. Rotating SECRET_ENCRYPTION_KEY invalidates existing vault rows — re-encrypt via migration script.Ingestion & Classification
Current: webhook/poll hybrid.
Option A: Gmail push notification (watch)
POST https://gmail.googleapis.com/users/me/watch
→ Pub/Sub → POST /api/webhooks/inbox (verified via WEBHOOK_SECRET)
Option B: Poll (fallback, current worker every 5m)
Worker calls Gmail history.list → new messages → queue jobsBoth queue gmail_ingest jobs via Redis.
From Email to Task (Example)
Email from Sarah <[email protected]>:
Subject: Website pricing update
Body: "Please update pricing section on website by Thursday. See pricing.pdf attached. High priority."
Attachment: pricing.pdf (1 page)
Steps:
1. Fetch via Gmail API → store raw in emails { gmail_id, thread_id, subject, body, attachments }
2. Email Agent: classify task_request, confidence 0.94
3. Inbox Agent: extract client=Acme (from email domain), project=Website, task="Update pricing section",
deadline=Thursday 2025-12-04, attachment=pricing.pdf, priority=High
4. Propose → Review modal → Create task linked to client/project, attach file
5. Provenance: task.source_type=gmail, source_id=email_abc123, ai_confidence=0.94
6. Click Source in task → opens Gmail threadDatabase & Search
emails {
id, workspace_id, gmail_id, thread_id, subject, body_text, snippet,
from_email, to_email, date, has_attachments, classification, confidence
}
attachments { id, email_id, filename, mime, storage_path }
Search: lib/search includes emails content — query "pricing" finds this email + task + file.