Email Skill
Gmail integration for reading, sending, and managing emails through your agent. Built on top of the google-oauth skill for secure authentication.
Features
- Read & List Emails: Browse your inbox with pagination
- Search: Full Gmail query syntax support
- Send & Reply: Compose and reply to emails
- Thread View: See entire conversation threads
- Labels: View and filter by Gmail labels
- Attachments: Download email attachments
- Actions: Star, archive, trash, mark read/unread
- SQLite Cache: Local metadata caching for performance
Installation
npm install
npm run build
Prerequisites
The email skill requires Gmail authorization through the google-oauth skill:
# Connect your Google account with Gmail scope
node ../google-oauth/dist/cli.js connect default gmail
CLI Usage
Check Status
# Check connection status
node dist/cli.js status
# Check specific profile
node dist/cli.js status work
Health Check
node dist/cli.js health
List Emails
# List 20 most recent emails
node dist/cli.js list
# List specific number
node dist/cli.js list default 10
Search Emails
Use Gmail's powerful search syntax:
# Search by sender
node dist/cli.js search "from:boss@company.com"
# Unread emails from sender
node dist/cli.js search "from:client@example.com is:unread"
# Emails with attachments
node dist/cli.js search "has:attachment filename:pdf"
# Recent important emails
node dist/cli.js search "is:important after:2024/01/01"
Read Email
node dist/cli.js read <message-id>
View Thread
node dist/cli.js thread <thread-id>
Send Email
node dist/cli.js send "recipient@example.com" "Subject" "Email body text"
Reply to Email
node dist/cli.js reply <message-id> "Your reply text"
Manage Emails
# Mark as read/unread
node dist/cli.js mark-read <message-id>
node dist/cli.js mark-unread <message-id>
# Star/unstar
node dist/cli.js star <message-id>
node dist/cli.js unstar <message-id>
# Archive (remove from inbox)
node dist/cli.js archive <message-id>
# Move to trash
node dist/cli.js trash <message-id>
# Delete permanently (careful!)
node dist/cli.js delete <message-id>
List Labels
node dist/cli.js labels
JavaScript/TypeScript API
Initialize
import { EmailSkill } from '@openclaw/email';
// Create skill for default profile
const email = new EmailSkill();
// Or for specific profile
const workEmail = EmailSkill.forProfile('work');
Check Status
const status = await email.getStatus();
console.log('Connected:', status.connected);
console.log('Email:', status.email);
console.log('Has Gmail:', status.hasGmailScope);
List Emails
const result = await email.list({ maxResults: 10 });
for (const msg of result.emails) {
console.log(`${msg.id}: ${msg.subject}`);
console.log(` From: ${msg.from}`);
console.log(` Unread: ${msg.isUnread}`);
}
// Pagination
const nextPage = await email.list({
maxResults: 10,
pageToken: result.nextPageToken
});
Search Emails
const result = await email.search('from:boss@company.com is:unread');
console.log(`Found ${result.resultSizeEstimate} emails`);
Read Full Email
const fullEmail = await email.read('message-id');
console.log('Subject:', fullEmail.subject);
console.log('From:', fullEmail.from);
console.log('Body:', fullEmail.bodyText);
console.log('HTML:', fullEmail.bodyHtml);
// Attachments
for (const att of fullEmail.attachments) {
console.log(`Attachment: ${att.filename} (${att.size} bytes)`);
// Download
const content = await email.getAttachment(fullEmail.id, att.id);
fs.writeFileSync(att.filename, content);
}
View Thread
const thread = await email.getThread('thread-id');
for (const msg of thread.messages) {
console.log(`${msg.from}: ${msg.bodyText.substring(0, 100)}...`);
}
Send Email
// Simple text email
await email.send({
to: 'recipient@example.com',
subject: 'Hello',
bodyText: 'This is the email body'
});
// With CC and HTML
await email.send({
to: ['user1@example.com', 'user2@example.com'],
cc: 'manager@example.com',
subject: 'Meeting Notes',
bodyText: 'Plain text version',
bodyHtml: '<h1>Meeting Notes</h1><p>Details...</p>'
});
// With attachments
await email.send({
to: 'client@example.com',
subject: 'Proposal',
bodyText: 'Please find attached the proposal.',
attachments: [
{
filename: 'proposal.pdf',
content: fs.readFileSync('proposal.pdf'),
mimeType: 'application/pdf'
}
]
});
Reply to Email
await email.reply('message-id', {
bodyText: 'Thanks for your email!'
});
Manage Emails
// Mark as read/unread
await email.markAsRead('message-id', true);
await email.markAsRead('message-id', false);
// Star/unstar
await email.star('message-id', true);
await email.star('message-id', false);
// Archive
await email.archive('message-id');
// Trash
await email.trash('message-id');
// Delete permanently
await email.delete('message-id');
List Labels
const labels = await email.listLabels();
for (const label of labels) {
console.log(`${label.id}: ${label.name} (${label.type})`);
}
Health Check
const health = await email.healthCheck();
if (health.status === 'healthy') {
console.log('Gmail API is accessible');
} else {
console.error('Issue:', health.message);
}
Gmail Query Syntax
The search function supports Gmail's full query syntax:
| Query | Description |
|---|---|
from:sender@example.com |
Emails from specific sender |
to:recipient@example.com |
Emails to specific recipient |
cc:someone@example.com |
CC'd emails |
subject:meeting |
Subject contains "meeting" |
has:attachment |
Has any attachment |
filename:pdf |
Has PDF attachment |
is:unread |
Unread emails |
is:read |
Read emails |
is:starred |
Starred emails |
is:important |
Important emails |
in:inbox |
In inbox |
in:sent |
Sent emails |
in:trash |
In trash |
in:spam |
In spam |
label:work |
With specific label |
after:2024/01/01 |
After date |
before:2024/12/31 |
Before date |
older_than:1d |
Older than 1 day |
newer_than:1w |
Newer than 1 week |
Combine with operators:
from:boss@company.com is:unread- Unread from bosshas:attachment (filename:pdf OR filename:doc)- PDF or DOC attachmentssubject:meeting -from:calendar@google.com- Meeting emails not from calendar
Storage
Cached email metadata is stored in:
~/.openclaw/skills/email/cache.db
Tables:
email_metadata- Cached email headers and metadatasync_history- Sync state for incremental updates
Multi-Profile Support
Manage multiple Gmail accounts:
import { EmailSkill } from '@openclaw/email';
// Work account
const work = EmailSkill.forProfile('work');
// Personal account
const personal = EmailSkill.forProfile('personal');
// Use independently
const workEmails = await work.list({ maxResults: 10 });
const personalEmails = await personal.list({ maxResults: 10 });
Each profile needs separate authentication:
node ../google-oauth/dist/cli.js connect work gmail
node ../google-oauth/dist/cli.js connect personal gmail
Error Handling
try {
const emails = await email.list();
} catch (error) {
if (error.message.includes('Not connected')) {
console.log('Please authenticate first');
} else if (error.message.includes('Gmail scope')) {
console.log('Re-authenticate with Gmail permissions');
} else {
console.error('Error:', error.message);
}
}
Testing
# Type checking
npm run typecheck
# Build
npm run build
# Check status
npm run status
# List recent emails
npm run cli -- list default 5
# Search
npm run cli -- search "is:unread"
Troubleshooting
"Not connected" error
Authenticate with google-oauth first:
node ../google-oauth/dist/cli.js connect default gmail
"Gmail scope not authorized"
Your Google account is connected but without Gmail permissions. Reconnect:
node ../google-oauth/dist/cli.js disconnect default
node ../google-oauth/dist/cli.js connect default gmail
API errors
Check health status:
node dist/cli.js health
Dependencies
@openclaw/google-oauth: For Gmail authentication@openclaw/auth-provider: Base authentication (via google-oauth)sqlite3: Local caching
Security Notes
- OAuth tokens stored encrypted by auth-provider
- Cache database has 0600 permissions (user read/write only)
- No email content stored locally (only metadata cached)
- Uses Gmail API with least-privilege scope
More from ticruz38/skills
binance-auth
Binance API authentication and key management for trading skills. Securely stores API keys, validates permissions, supports testnet vs production environments, and provides health checks with balance queries.
45reminders
Local reminder system with natural language scheduling
28meetings
Coordinate meetings using calendar and email integration. Find mutual availability, send invites, create calendar events, and manage meeting agendas with prep documents.
6auth-provider
Centralized authentication provider for OAuth 2.0 and API key management. Supports Google, Binance, QuickBooks, and Slack. Provides encrypted SQLite storage, auto-refresh, PKCE flow, and health checks.
6slack
Send messages and notifications to Slack channels. Built on top of slack-auth for authentication, supports messaging, file uploads, channel management, DM sending, and notification templates with variable substitution.
3calendar
Google Calendar integration for events and availability. Built on top of google-oauth for authentication, with SQLite caching, free/busy queries, and full Calendar API capabilities.
3