Co-authored-by: Cameron Pfiffer <cameron@pfiffer.org> Co-authored-by: Caren Thomas <carenthomas@gmail.com> Co-authored-by: Charles Packer <packercharles@gmail.com> Co-authored-by: Sarah Wooders <sarahwooders@gmail.com>
7.9 KiB
Gmail Pub/Sub Integration
Receive email notifications and have your Letta agent process them automatically.
Overview
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ ┌─────────────┐
│ Gmail │───▶│ Google Cloud │───▶│ LettaBot │───▶│ Letta │
│ Inbox │ │ Pub/Sub │ │ Webhook Server │ │ Agent │
└─────────────┘ └──────────────┘ └─────────────────┘ └──────┬──────┘
│
▼
┌─────────────┐
│ Telegram │
└─────────────┘
When a new email arrives:
- Gmail sends a notification to Google Cloud Pub/Sub
- Pub/Sub pushes the notification to LettaBot's webhook
- LettaBot fetches the email details via Gmail API
- The email is sent to your Letta agent for processing
- The agent's response is delivered to you on Telegram
Prerequisites
- Google Cloud account with billing enabled
- Gmail account you want to monitor
- LettaBot running with a public URL (for Pub/Sub push)
gcloudCLI installed (install guide)
Setup Guide
Step 1: Create a Google Cloud Project
- Go to Google Cloud Console
- Create a new project or select an existing one
- Note your Project ID (you'll need it later)
Step 2: Enable Required APIs
# Set your project
gcloud config set project YOUR_PROJECT_ID
# Enable Gmail and Pub/Sub APIs
gcloud services enable gmail.googleapis.com pubsub.googleapis.com
Step 3: Create OAuth2 Credentials
- Go to APIs & Services > Credentials
- Click Create Credentials > OAuth client ID
- Select Desktop app as the application type
- Name it "LettaBot Gmail"
- Download the JSON file
- Note the Client ID and Client Secret
Step 4: Get a Refresh Token
You need to authorize LettaBot to access your Gmail. Use this script:
# Install the Google auth library
npm install googleapis
# Run this script to get a refresh token
node -e "
const { google } = require('googleapis');
const readline = require('readline');
const oauth2Client = new google.auth.OAuth2(
'YOUR_CLIENT_ID',
'YOUR_CLIENT_SECRET',
'urn:ietf:wg:oauth:2.0:oob'
);
const authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: ['https://www.googleapis.com/auth/gmail.readonly'],
});
console.log('Authorize this app by visiting:', authUrl);
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
rl.question('Enter the code: ', async (code) => {
const { tokens } = await oauth2Client.getToken(code);
console.log('Refresh Token:', tokens.refresh_token);
rl.close();
});
"
Save the Refresh Token - you'll need it for configuration.
Step 5: Create Pub/Sub Topic and Subscription
# Create a topic for Gmail notifications
gcloud pubsub topics create lettabot-gmail
# Grant Gmail permission to publish to the topic
gcloud pubsub topics add-iam-policy-binding lettabot-gmail \
--member="serviceAccount:gmail-api-push@system.gserviceaccount.com" \
--role="roles/pubsub.publisher"
Step 6: Configure LettaBot
Add these to your .env file:
# Enable Gmail integration
GMAIL_ENABLED=true
# Webhook server port (must be accessible from internet)
GMAIL_WEBHOOK_PORT=8788
# Shared secret for validating Pub/Sub requests (generate a random string)
GMAIL_WEBHOOK_TOKEN=your_random_secret_here
# OAuth2 credentials from Step 3
GMAIL_CLIENT_ID=your_client_id.apps.googleusercontent.com
GMAIL_CLIENT_SECRET=your_client_secret
# Refresh token from Step 4
GMAIL_REFRESH_TOKEN=your_refresh_token
# Your Telegram user ID (to receive email notifications)
# Find this by messaging @userinfobot on Telegram
GMAIL_TELEGRAM_USER=123456789
Step 7: Expose Webhook to Internet
LettaBot's webhook server needs to be accessible from the internet for Pub/Sub to push notifications.
Option A: Using Tailscale Funnel (recommended)
tailscale funnel 8788
Option B: Using ngrok
ngrok http 8788
Option C: Using Cloudflare Tunnel
cloudflared tunnel --url http://localhost:8788
Note your public URL (e.g., https://your-domain.ts.net or https://abc123.ngrok.io).
Step 8: Create Pub/Sub Push Subscription
# Replace with your actual public URL and token
gcloud pubsub subscriptions create lettabot-gmail-push \
--topic=lettabot-gmail \
--push-endpoint="https://YOUR_PUBLIC_URL/webhooks/gmail?token=YOUR_WEBHOOK_TOKEN" \
--ack-deadline=60
Step 9: Start Gmail Watch
You need to tell Gmail to send notifications to your Pub/Sub topic. This watch expires after 7 days and needs to be renewed.
Using the Gmail API directly:
curl -X POST \
'https://gmail.googleapis.com/gmail/v1/users/me/watch' \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H 'Content-Type: application/json' \
-d '{
"topicName": "projects/YOUR_PROJECT_ID/topics/lettabot-gmail",
"labelIds": ["INBOX"]
}'
Using a helper script:
// watch-gmail.js
const { google } = require('googleapis');
const oauth2Client = new google.auth.OAuth2(
process.env.GMAIL_CLIENT_ID,
process.env.GMAIL_CLIENT_SECRET
);
oauth2Client.setCredentials({ refresh_token: process.env.GMAIL_REFRESH_TOKEN });
const gmail = google.gmail({ version: 'v1', auth: oauth2Client });
async function startWatch() {
const res = await gmail.users.watch({
userId: 'me',
requestBody: {
topicName: 'projects/YOUR_PROJECT_ID/topics/lettabot-gmail',
labelIds: ['INBOX'],
},
});
console.log('Watch started:', res.data);
console.log('Expires:', new Date(parseInt(res.data.expiration)));
}
startWatch();
Step 10: Start LettaBot
cd /path/to/lettabot
npm run dev
You should see:
Starting LettaBot...
Bot started as @your_bot
Gmail webhook enabled on port 8788
Gmail webhook server listening on port 8788
Testing
- Send a test email to your Gmail account
- Check LettaBot logs for the notification
- You should receive a Telegram message with the agent's summary
Troubleshooting
"Invalid token" error
- Make sure
GMAIL_WEBHOOK_TOKENmatches the?token=in your Pub/Sub subscription
No notifications received
- Verify your public URL is accessible:
curl https://YOUR_URL/health - Check Pub/Sub subscription for errors in Google Cloud Console
- Make sure Gmail watch is active (re-run the watch command)
"User not authorized" error
- Ensure you granted
roles/pubsub.publishertogmail-api-push@system.gserviceaccount.com
Watch expired
- Gmail watches expire after 7 days
- Set up a cron job to renew:
0 0 * * * node watch-gmail.js
Cleanup
To disable Gmail integration:
# Stop the watch
curl -X POST \
'https://gmail.googleapis.com/gmail/v1/users/me/stop' \
-H "Authorization: Bearer $(gcloud auth print-access-token)"
# Delete Pub/Sub resources
gcloud pubsub subscriptions delete lettabot-gmail-push
gcloud pubsub topics delete lettabot-gmail
# Remove from .env
# GMAIL_ENABLED=false
Security Considerations
- Keep your
GMAIL_WEBHOOK_TOKENsecret - The refresh token grants read access to your Gmail - store it securely
- Consider using a service account for production deployments
- LettaBot only reads emails, it cannot send or modify them