Automated property agent workflow: hourly Gmail monitoring, intelligent question detection, context-aware email drafting with RAG and conversation history
This skill orchestrates an automated property agent workflow that:
Deployment model: Runs as an OpenClaw cron job (hourly isolated agent turn).
┌─────────────────────────────────────────────────────────────┐
│ HOURLY CRON JOB (OpenClaw Gateway Scheduler) │
└─────────┬───────────────────────────────────────────────────┘
│
v
┌─────────────────────────────────────────────────────────────┐
│ 1. Gmail Check │
│ • List unread messages (last 1 hour) │
│ • Filter: from prospects/leads │
│ • Skip: autoresponders, newsletters │
└─────────┬───────────────────────────────────────────────────┘
│
v
┌─────────────────────────────────────────────────────────────┐
│ 2. Property Question Detection │
│ • LLM classifier: Is this a property-related question? │
│ • Examples: "good time to buy?", "hottest district?", │
│ "cooling measures affect my HDB flat?" │
│ • Skip: Non-property emails, invoices, spam │
└─────────┬───────────────────────────────────────────────────┘
│
v
┌─────────────────────────────────────────────────────────────┐
│ 3. Context Gathering (Parallel) │
│ ├─ Ragie.ai: Search property knowledge base │
│ │ (market reports, cooling measures, district data) │
│ └─ Supabase: Get client history │
│ (past conversations, preferences, meeting notes) │
└─────────┬───────────────────────────────────────────────────┘
│
v
┌─────────────────────────────────────────────────────────────┐
│ 4. Email Draft Generation │
│ • LLM generates structured reply (300-450 words) │
│ • Includes: headings, bullet points, source links │
│ • Tone: Professional, helpful, data-driven │
└─────────┬───────────────────────────────────────────────────┘
│
v
┌─────────────────────────────────────────────────────────────┐
│ 5. Delivery for Review (Safe by Default) │
│ • Post draft to Slack/WhatsApp/Telegram for approval │
│ • OR: Save as Gmail draft (no send) │
│ • Manual send after review │
└─────────────────────────────────────────────────────────────┘
Install and configure these skills first:
google-workspace - Gmail API access
/skills/google-workspace/SKILL.mdgmail.readonly, gmail.send, gmail.modifyragie - Property knowledge base RAG
/skills/ragie/SKILL.mdRAGIE_API_KEY in environmentsupabase - Conversation history database
/skills/supabase/SKILL.mdSUPABASE_URL and SUPABASE_SERVICE_KEY in environmentFor email classification and draft generation:
# Option 1: OpenAI
export OPENAI_API_KEY="sk-..."
# Option 2: Anthropic (Claude)
export ANTHROPIC_API_KEY="sk-ant-..."
This workflow runs as a cron job, so the Gateway must be running 24/7:
# Start Gateway (if not already running)
openclaw gateway run
Before orchestrating, verify each skill works:
# Test Gmail access
python3 ../google-workspace/scripts/gmail.py list --max-results 5
# Test Ragie search
python3 ../ragie/scripts/search.py \
--query "property cooling measures Singapore" \
--max-results 3
# Test Supabase connection
python3 ../supabase/scripts/test_connection.py
# Test contextgathering
python3 ../supabase/scripts/get_client_context.py \
--email [email protected]
Edit config/prospect_domains.txt to list email domains/addresses to monitor:
# Prospect email patterns (one per line)
*@gmail.com
*@hotmail.com
[email protected]
[email protected]
# Add your client/prospect domains here
Or use the default: monitor all emails (filter by subject/content instead).
# Add hourly cron job
openclaw cron add \
--name "Property Email Agent" \
--cron "0 * * * *" \
--tz "Asia/Singapore" \
--session isolated \
--message "Check Gmail for property questions and draft replies" \
--announce \
--channel slack \
--to "channel:C1234567890"
# Or webhook delivery:
openclaw cron add \
--name "Property Email Agent" \
--cron "0 * * * *" \
--tz "Asia/Singapore" \
--session isolated \
--message "Check Gmail for property questions and draft replies" \
--webhook \
--to "https://your-webhook.example.com/drafts"
Customization:
--cron "0 * * * *" to adjust frequency (hourly by default)--announce to post drafts to chat (Slack/WhatsApp/Telegram)--webhook to POST drafts to your backend--tz to your local timezone# List cron jobs
openclaw cron list
# See job runs
openclaw cron runs --id <job-id> --limit 10
# Manually trigger (for testing)
openclaw cron run <job-id> --force
Before deploying the cron job, test the workflow manually:
# Process a specific email
python3 scripts/process_email.py \
--email-id "msg_abc123xyz" \
--debug
# Dry run (no database writes, no email sends)
python3 scripts/process_email.py \
--email-id "msg_abc123xyz" \
--dry-run
# Check last hour of unread emails
python3 scripts/check_gmail.py \
--since "1h" \
--unread-only
# Check specific time range
python3 scripts/check_gmail.py \
--since "2026-03-03T10:00:00Z" \
--until "2026-03-03T11:00:00Z"
# Test classifier on an email
python3 scripts/classify_email.py \
--subject "Property inquiry" \
--body "Is it a good time to buy property in District 9?" \
--debug
# Full workflow: context gathering + draft generation
python3 scripts/draft_reply.py \
--client-email "[email protected]" \
--original-subject "Property inquiry" \
--original-body "Is it a good time to buy in District 9?" \
--query "District 9 Singapore property investment timing" \
--debug
check_gmail.py - Gmail MonitoringLists unread emails within a time range.
Parameters:
--since (default: "1h"): Time range start (duration or ISO timestamp)--until (optional): Time range end (ISO timestamp)--unread-only (flag): Only fetch unread messages--debug: Enable debug outputOutput (JSON):
{
"emails": [
{
"id": "msg_abc123",
"thread_id": "thread_xyz789",
"from": "[email protected]",
"subject": "Property inquiry",
"snippet": "Is it a good time to buy...",
"date": "2026-03-03T10:30:00Z"
}
],
"count": 1
}
classify_email.py - Property Question DetectionUses LLM to classify if email contains property-related questions.
Parameters:
--subject (required): Email subject--body (required): Email body text--from-email (optional): Sender email (for context)--debug: Enable debug outputOutput (JSON):
{
"is_property_question": true,
"confidence": 0.95,
"detected_topics": ["buying timing", "district analysis"],
"reasoning": "Email asks about property investment timing in a specific district"
}
draft_reply.py - Email Draft GenerationOrchestrates context gathering + draft generation.
Parameters:
--client-email (required): Prospect/client email address--original-subject (required): Original email subject--original-body (required): Original email body--query (required): Search query for Ragie (extracted from email)--thread-id (optional): Gmail thread ID (for threading)--dry-run: Don't save draft or write to database--debug: Enable debug outputOutput (JSON):
{
"draft": {
"subject": "Re: Property inquiry",
"body": "Dear John,\n\nThank you for your inquiry...",
"word_count": 387
},
"context_used": {
"ragie_chunks": 3,
"supabase_conversations": 2,
"supabase_memory": true
},
"saved_to": "gmail_draft"
}
process_email.py - End-to-End WorkflowFull workflow: classify → gather context → draft reply → save.
Parameters:
--email-id (required): Gmail message ID--dry-run: Don't save draft or write to database--debug: Enable debug outputGenerated email drafts follow this structure:
Subject: Re: [Original Subject]
Dear [Client Name],
Thank you for your inquiry about [topic]. Here's what you need to know:
## [Main Heading 1]
[2-3 paragraphs with data-driven insights from Ragie knowledge base]
Key points:
• [Bullet point 1]
• [Bullet point 2]
• [Bullet point 3]
## [Main Heading 2]
[Additional context based on client history from Supabase]
## Next Steps
[Clear call-to-action or offer for follow-up meeting]
Best regards,
[Your Name]
[Your Title]
---
📚 Sources:
• [Document 1 name] (via Ragie)
• [Meeting notes from [date]] (internal)
Constraints:
# Production: Hourly monitoring with Slack delivery
openclaw cron add \
--name "Property Email Agent" \
--cron "0 * * * *" \
--tz "Asia/Singapore" \
--session isolated \
--message "Run property email workflow (check Gmail, draft replies)" \
--announce \
--channel slack \
--to "channel:C_PROPERTY_DRAFTS"
# Run only during business hours (9 AM - 6 PM, Mon-Fri)
openclaw cron add \
--name "Property Email Agent (Business Hours)" \
--cron "0 9-18 * * 1-5" \
--tz "Asia/Singapore" \
--session isolated \
--message "Run property email workflow" \
--announce \
--channel WhatsApp \
--to "+6591234567"
# For high-volume agents: check every 15 minutes
openclaw cron add \
--name "Property Email Agent (15min)" \
--cron "*/15 * * * *" \
--tz "Asia/Singapore" \
--session isolated \
--message "Run property email workflow" \
--announce
Edit scripts/classify_email.py to customize the LLM prompt:
# Add domain-specific keywords
PROPERTY_KEYWORDS = [
"buy", "sell", "invest", "property", "condo", "HDB",
"district", "cooling measures", "ABSD", "resale", "BTO",
"rental yield", "capital appreciation", "mortgage",
]
# Adjust confidence threshold
CLASSIFICATION_THRESHOLD = 0.7 # Default: 0.7 (70% confidence)
Edit scripts/draft_reply.py to adjust the LLM prompt for email generation:
DRAFT_PROMPT_TEMPLATE = """
You are a professional Singapore property agent...
[Customize tone, structure, constraints here]
"""
The cron job can deliver to multiple channels:
# Multiple announcements (Slack + WhatsApp)
# (Not directly supported - use webhook + custom fanout)
# Webhook to your backend for custom processing
openclaw cron add \
--name "Property Email Agent" \
--cron "0 * * * *" \
--session isolated \
--message "Run workflow" \
--webhook \
--to "https://your-backend.com/api/email-drafts"
"Gmail API quota exceeded"
"Ragie API rate limit"
"Supabase connection timeout"
"LLM classification errors"
classify_email.py# Enable full debug logging
python3 scripts/check_gmail.py --debug
python3 scripts/classify_email.py --debug
python3 scripts/draft_reply.py --debug
Test workflow without side effects:
# No writes to database, no email sends, no drafts saved
python3 scripts/process_email.py \
--email-id "msg_abc123" \
--dry-run
⚠️ GDPR/PDPA Compliance:
~/.openclaw/credentials/ (chmod 600)All API keys must be in environment variables, never hardcoded:
# ✅ GOOD
export RAGIE_API_KEY="..."
export SUPABASE_SERVICE_KEY="..."
# ❌ BAD (never do this)
# API_KEY = "sk-12345" # in source code
See SECURITY.md for comprehensive threat model and incident response procedures.
Monthly cost for moderate usage (100 emails/day processed):
| Service | Usage | Cost |
|---|---|---|
| Gmail API | 100 reads/day | Free (within quota) |
| Ragie.ai | 3,000 searches/month | $29/month (Pro plan) |
| Supabase | Queries + storage | Free (within limits) |
| OpenAI GPT-4 | ~200K tokens/month | ~$6/month |
| Total | ~$35/month |
Cost optimization:
# View last 20 runs
openclaw cron runs --id <job-id> --limit 20
# Filter by status
openclaw cron runs --id <job-id> --status success
openclaw cron runs --id <job-id> --status error
# Count total conversations
python3 ../supabase/scripts/query.py \
--table conversations \
--select "count"
# Get daily email volume
python3 scripts/analytics/daily_volume.py --days 30
Track these in Supabase or your analytics platform:
# Check Gateway is running
openclaw gateway status
# Verify cron job is enabled
openclaw cron list
# Check job's last run
openclaw cron runs --id <job-id> --limit 1
# Manually trigger
openclaw cron run <job-id> --force
# Test Gmail access
python3 ../google-workspace/scripts/gmail.py list
# Check time range
python3 scripts/check_gmail.py --since "24h" --debug
# Verify OAuth scopes
python3 ../google-workspace/scripts/google_auth.py check-status
# Test classifier directly
python3 scripts/classify_email.py \
--subject "Test" \
--body "Is it a good time to buy property?" \
--debug
# Check LLM API key
env | grep -E "OPENAI|ANTHROPIC"
Future enhancements (not yet implemented):