Skip to main content
🦞

Automate Your Email with AI Agents

14 minTutorial

title: "Automate Your Email with AI Agents" date: "2026-02-24" description: "Learn how to build an AI-powered email automation system using OpenClaw. Automatically triage, draft replies, and manage your inbox with intelligent agents." category: "Tutorial" author: "OpenClaw Team" tags: ["email", "automation", "productivity"] readTime: "14 min"

Email is one of the highest-leverage places to apply AI automation. The average knowledge worker spends over two hours a day in their inbox — reading, triaging, drafting replies, and following up. With OpenClaw, you can delegate most of that to a set of cooperating agents that work around the clock.

This tutorial builds a complete email automation system from scratch: an inbox connection, a triage agent that categorizes incoming mail, an auto-reply agent that drafts responses, and a follow-up agent that nudges threads that have gone quiet.

What You'll Build

By the end of this tutorial, you'll have a working email automation pipeline with:

  • An IMAP listener that polls your inbox for new messages
  • A triage agent that classifies emails by urgency and category
  • A reply drafting agent that writes contextual responses for your review
  • A follow-up agent that flags threads needing a nudge
  • A filtering layer that applies AI-generated rules to route mail

Prerequisites

Before you begin, make sure you have:

  • OpenClaw installed and running (installation guide)
  • Claude API configured (setup guide)
  • An email account with IMAP/SMTP access enabled (Gmail, Outlook, or any standard mail server)
  • An app-specific password if your provider requires it (Gmail does)
  • Python 3.10+ for the custom agent scripts in this tutorial

Step 1: Connect Your Inbox via IMAP

OpenClaw includes a built-in email connector. Configure it with your IMAP credentials:

# openclaw.yaml
integrations:
  email:
    provider: imap
    host: "imap.gmail.com"
    port: 993
    tls: true
    username: "${EMAIL_ADDRESS}"
    password: "${EMAIL_APP_PASSWORD}"  # Use an app password, not your main password
    polling_interval: 60s             # Check for new mail every 60 seconds
    folders:
      - INBOX
      - "[Gmail]/Sent Mail"           # Monitor sent folder for follow-up tracking

For SMTP (sending replies and follow-ups), add a separate outbound block:

    smtp:
      host: "smtp.gmail.com"
      port: 587
      starttls: true
      username: "${EMAIL_ADDRESS}"
      password: "${EMAIL_APP_PASSWORD}"

Test the connection before building agents on top of it:

openclaw integration test email
# Expected output:
# IMAP connection: OK (inbox contains 247 messages)
# SMTP connection: OK (test message delivered)

Step 2: Build the Triage Agent

The triage agent is the front door of your automation. It reads each new email and assigns three properties: category, urgency, and required action.

Define the agent in your agents config:

# agents.yaml
agents:
  triage:
    role: "Email Triage Specialist"
    model: claude-3-5-haiku-20241022  # Fast and cheap — runs on every email
    instructions: |
      You are an expert email triage assistant. For each email, analyze:

      1. CATEGORY: Choose one of:
         - customer_inquiry (questions from customers or prospects)
         - internal (from colleagues, team members)
         - newsletter (marketing, subscriptions, digests)
         - notification (automated alerts, system messages)
         - personal (from friends, family)
         - spam (unsolicited, irrelevant)

      2. URGENCY: Choose one of:
         - urgent (requires response within 2 hours)
         - normal (respond within 24 hours)
         - low (respond within a week or not at all)

      3. ACTION: Choose one of:
         - reply_needed (must send a response)
         - draft_reply (draft a reply for human review)
         - archive (no action required, safe to archive)
         - unsubscribe (newsletter or marketing to unsubscribe from)
         - escalate (needs human attention immediately)

      Respond ONLY with valid JSON. No prose.

    output_schema:
      type: object
      required: [category, urgency, action, summary]
      properties:
        category: {type: string}
        urgency: {type: string}
        action: {type: string}
        summary: {type: string, description: "One sentence summary of the email"}

The triage agent uses claude-3-5-haiku-20241022 because it runs on every single email — including newsletters and spam. Using a cheaper, faster model here keeps costs minimal. The heavy models are reserved for drafting thoughtful replies.

Wire the triage agent into an email trigger:

# workflows/email-triage.yaml
name: email_triage
trigger:
  type: email_received
  folder: INBOX

steps:
  - name: triage
    agent: triage
    input:
      from: "{{email.from}}"
      subject: "{{email.subject}}"
      body: "{{email.body_text | truncate: 2000}}"  # Limit context for speed
      date: "{{email.date}}"
    output: triage_result

  - name: apply_labels
    type: email_action
    action: label
    email_id: "{{email.id}}"
    labels:
      - "{{triage_result.category}}"
      - "{{triage_result.urgency}}"

  - name: route
    type: conditional
    conditions:
      - if: "{{triage_result.action == 'draft_reply'}}"
        then: draft_reply_workflow
      - if: "{{triage_result.action == 'archive'}}"
        then: archive_email
      - if: "{{triage_result.urgency == 'urgent'}}"
        then: send_urgent_notification

Step 3: Build the Reply Drafting Agent

For emails that need a response, the drafting agent generates a contextual reply based on the original message and your writing style.

First, create a style profile that the agent will use as a reference. You can generate this by feeding it examples of your past emails:

openclaw agent train reply_drafter \
  --samples "./email-samples/*.txt" \
  --output "./profiles/my-writing-style.yaml"

Then configure the drafting agent:

# agents.yaml (continued)
  reply_drafter:
    role: "Email Reply Specialist"
    model: claude-3-5-sonnet-20241022  # Better writing quality
    context_files:
      - "./profiles/my-writing-style.yaml"
      - "./data/company-faq.txt"         # Common answers to inject automatically
    instructions: |
      You draft email replies on behalf of the user. Follow their writing style
      exactly as described in the style profile.

      Guidelines:
      - Match the formality level of the incoming email
      - Be concise — aim for the shortest response that fully addresses the question
      - Never invent facts. If you don't know the answer, say so and offer to find out.
      - Always end with a clear next step or call to action
      - Do not include a subject line — only the body text
      - Leave [PLACEHOLDER] for any specific detail you cannot confidently fill in

    output_schema:
      type: object
      required: [draft, confidence, notes]
      properties:
        draft: {type: string, description: "The full reply body text"}
        confidence: {type: number, minimum: 0, maximum: 1}
        notes: {type: string, description: "Any flags or uncertainties for the human reviewer"}

The confidence score is important. Low-confidence drafts get flagged for mandatory human review before sending:

# workflows/draft-reply.yaml
name: draft_reply_workflow
steps:
  - name: draft
    agent: reply_drafter
    input:
      original_email: "{{email.full_content}}"
      triage_summary: "{{triage_result.summary}}"
      category: "{{triage_result.category}}"
    output: draft_result

  - name: save_draft
    type: email_action
    action: save_draft
    to: "{{email.from}}"
    subject: "Re: {{email.subject}}"
    body: "{{draft_result.draft}}"

  - name: notify_if_low_confidence
    type: conditional
    conditions:
      - if: "{{draft_result.confidence < 0.7}}"
        then:
          action: send_notification
          channel: slack
          message: |
            Low-confidence draft saved for {{email.from}}.
            Subject: {{email.subject}}
            Notes: {{draft_result.notes}}
            Review: {{draft_result.review_url}}

Drafts are saved to your drafts folder for review, never sent automatically. You review, edit if needed, and hit send. Over time you can raise the auto-send threshold for high-confidence replies in low-stakes categories.

Step 4: Build the Follow-Up Agent

Threads that need follow-up are tracked in OpenClaw's internal store. A scheduled job runs daily to surface emails that have gone quiet:

# agents/followup_agent.py
from openclaw import Agent, EmailStore, ScheduledTask
from datetime import datetime, timedelta

class FollowUpAgent(Agent):
    name = "follow_up"
    model = "claude-3-5-haiku-20241022"

    def run(self):
        store = EmailStore()

        # Find sent emails with no reply after 3 days
        pending = store.query(
            folder="sent",
            no_reply_since=datetime.now() - timedelta(days=3),
            exclude_categories=["newsletter", "notification"]
        )

        for thread in pending:
            assessment = self.assess_followup_need(thread)

            if assessment["needs_followup"]:
                draft = self.draft_followup(thread)
                store.save_draft(
                    thread_id=thread.id,
                    body=draft,
                    label="follow-up-needed"
                )

    def assess_followup_need(self, thread) -> dict:
        return self.complete(
            prompt=f"""
            I sent this email {thread.days_ago} days ago and haven't heard back.
            Should I follow up?

            Original email:
            {thread.sent_body}

            Respond with JSON: {{"needs_followup": bool, "reason": str}}
            """
        )

    def draft_followup(self, thread) -> str:
        return self.complete(
            prompt=f"""
            Write a brief, friendly follow-up to this email thread.
            Keep it to 2-3 sentences. Don't be pushy.

            Original subject: {thread.subject}
            Original email: {thread.sent_body}
            """
        )

Schedule it to run once a day:

# schedules.yaml
schedules:
  daily_followup:
    agent: follow_up
    cron: "0 9 * * 1-5"  # 9am Monday through Friday
    timezone: "America/New_York"

See the scheduling AI tasks guide for more patterns around cron-based agent triggers.

Step 5: Apply AI-Generated Filtering Rules

Beyond the triage agent, you can have OpenClaw generate Gmail-style filter rules from your email history. This creates a deterministic pre-filter that catches obvious cases before the AI even runs — saving tokens and latency:

# generate_rules.py
from openclaw import RuleGenerator, EmailStore

store = EmailStore()

# Sample the last 30 days of mail
sample = store.sample(days=30, max_messages=500)

generator = RuleGenerator(model="claude-3-5-sonnet-20241022")
rules = generator.generate(
    email_sample=sample,
    objective="""
    Generate filtering rules to:
    1. Auto-archive newsletters and marketing emails
    2. Label emails from my team with 'internal'
    3. Flag anything with words like 'urgent', 'ASAP', 'deadline' with high priority
    4. Move automated notifications to a 'Notifications' folder
    """
)

# Write rules to OpenClaw config
rules.save("./config/email-rules.yaml")
print(f"Generated {len(rules)} rules")

The generated rules file looks like this:

# config/email-rules.yaml
rules:
  - name: "Auto-archive newsletters"
    conditions:
      - field: from
        operator: contains_any
        values: ["newsletter", "no-reply", "noreply", "digest", "updates@"]
      - field: list_unsubscribe
        operator: exists
    actions:
      - label: newsletter
      - archive: true
    skip_ai_triage: true  # Don't waste tokens on these

  - name: "Flag urgent keywords"
    conditions:
      - field: subject
        operator: matches_regex
        value: "(?i)(urgent|asap|deadline|time.sensitive)"
    actions:
      - label: urgent
      - notify: slack

Step 6: Test the Full Pipeline

Run the end-to-end test to validate the complete pipeline before going live:

# Replay the last 10 emails through the pipeline (dry-run, no actions taken)
openclaw workflow test email_triage \
  --source inbox \
  --last 10 \
  --dry-run

# Output:
# Email 1 (support request): triaged=customer_inquiry, urgency=normal, action=draft_reply ✓
# Email 2 (newsletter):      triaged=newsletter, urgency=low, action=archive ✓
# Email 3 (urgent client):   triaged=customer_inquiry, urgency=urgent, action=escalate ✓
# ...
# 9/10 correctly classified. 1 misclassified (false positive on urgency).

Review misclassifications and adjust the triage agent's instructions accordingly. A few rounds of refinement typically gets accuracy above 95%.

Common Issues and Fixes

Gmail blocking IMAP access — Enable IMAP in Gmail settings (Settings > See all settings > Forwarding and POP/IMAP) and generate an App Password at myaccount.google.com/apppasswords.

Triage agent misclassifying a specific sender — Add an explicit rule in email-rules.yaml for that sender. Deterministic rules run before AI and take priority.

Reply drafts are too long — Add "Aim for under 150 words" to the drafting agent's instructions and set max_tokens: 400 in the generation config.

Too many low-confidence flags — The agent needs more context. Add your FAQ file to context_files or increase the number of writing style samples used during training.

Next Steps

With your email automation running, explore what else you can build:

Email automation is one of those things where the upfront investment pays dividends every single day. Set it up once, refine it over a few weeks, and reclaim hours you were spending on inbox management.

Cookie Preferences

We use essential cookies and analytics to operate and improve the site. Advertising cookies are loaded only after you consent. Privacy Policy