You have too much email. You already knew that. What you might not know is that OpenClaw can act as a 24×7 sorting hat that surfaces only what matters and quietly takes care of the rest.
Below is a practical, from-scratch tutorial on building an OpenClaw email triage system prioritizing important messages – urgent (ping me right now), important (include in my daily briefing), routine (auto-handle) and junk (archive forever).
Why OpenClaw for email triage?
Traditional filters are brittle: one Regex miss and the CFO’s message lands in spam. OpenClaw combines LLM reasoning, tool integrations and persistent memory to make the rules less hard-coded and more adaptive. Thanks to Composio, you get Gmail, Outlook, Fastmail and IMAP out of the box. Everything below works on OpenClaw v0.47.2 (Node 22+) locally or on ClawCloud; I’ll show both paths.
Prerequisites and fast setup
Local install (dev box or server):
# Node 22 is mandatory for the worker threads rewrite
brew install node@22 # or asdf install nodejs 22.5.0
npm install -g openclaw@0.47.2
openclaw daemon start # boots gateway on http://localhost:3033
ClawCloud (hosted, less yak-shaving):
- Sign in → New Agent → Name it mail-triage.
- Choose the Gmail or IMAP integration.
- Click Deploy. 60 s later you have a public gateway URL.
The rest of the guide is identical; just swap localhost for your ClawCloud URL when you hit APIs.
Connect the email inbox
Using the Gmail adapter
OpenClaw uses OAuth2 under the hood. In the gateway UI go to Integrations → Gmail → Connect, grant scopes gmail.readonly and gmail.modify. Behind the curtain Claw stores the token in its encrypted secrets store (~/.claw/secrets.json locally, KMS on Cloud).
IMAP fallback
If your company forbids Gmail OAuth, use plain IMAP:
{
"type": "imap",
"host": "imap.acme.com",
"port": 993,
"secure": true,
"auth": {
"user": "me@acme.com",
"pass": "CHANGE_ME"
}
}
Add the JSON above in Settings → Tool Config → Email IMAP.
Designing the four-tier priority model
Borrowing from airline boarding but for email:
- Tier 0 – Urgent: incidents, production alerts, messages from the CEO. Notify immediately via Slack/Signal.
- Tier 1 – Important: stuff you want in your daily briefing at 8 AM.
- Tier 2 – Routine: receipts, GitHub notifications, newsletters. Auto-archive after action.
- Tier 3 – Junk: marketing blasts, no-reply anything. Archive + mark read.
We encode this with a priority classifier skill in OpenClaw.
Implementing the priority classifier
1. Create a new skill
# file: skills/emailPriority.js
module.exports = async function classifyEmail({ subject, sender, snippet, body }, ctx) {
const vipSenders = ctx.memory.get("vipSenders") || [];
const urgentKeywords = [
/production down/i,
/security breach/i,
/payment failed/i,
];
if (vipSenders.includes(sender)) return "urgent";
if (urgentKeywords.some(rx => rx.test(subject) || rx.test(body))) return "urgent";
if (/invoice|receipt|statement/i.test(subject)) return "routine";
if (/unsubscribe|promo|sale/i.test(body)) return "junk";
return "important"; // default fall-through
};
The logic mixes:
- A memory-based
vipSenderslist (mutable without redeploy). - Regexes for hard triggers (fast, deterministic).
- Fallback to LLM when ambiguous (see next step).
2. Let the LLM break ties
# file: skills/emailPriorityLLM.js
const { openai } = require("openclaw/toolkit");
module.exports = async (email) => {
const prompt = `Classify the email into one of: urgent, important, routine, junk.\nSubject: ${email.subject}\nBody: ${email.snippet}`;
const res = await openai.chat({ model: "gpt-4o", prompt });
return res.choices[0].message.content.trim().toLowerCase();
};
Wire both skills in agent.json:
{
"skills": [
"./skills/emailPriority.js",
{
"when": "confidence < 0.7",
"use": "./skills/emailPriorityLLM.js"
}
]
}
Training OpenClaw on your rules
Populate VIP list
POST /memory/vipSenders \
-H "Authorization: Bearer $CLAWS" \
-d '["ceo@acme.com", "pagerduty@alerts.io"]'
The list lives in vector storage; edits are O(1), no container restart needed.
Keyword triggers via UI
Navigate to Skills → EmailPriority → Parameters. There you can add or remove Regexes without touching the repo – the gateway rewrites the skill file under the hood.
Automating actions per tier
Routing graph
OpenClaw’s Router DSL is terse. Create routes/email.json:
{
"on": "email.received",
"steps": [
{ "run": "classifyEmail", "as": "prio" },
{
"switch": "{{ prio }}",
"cases": {
"urgent": ["notifySlack", "forwardPhone"],
"important": ["appendBriefing"],
"routine": ["autoReply", "archive"],
"junk": ["archive"]
}
}
]
}
Each step is just another skill; two examples:
# skills/notifySlack.js
const { slack } = require("openclaw/toolkit");
module.exports = async ({ subject, sender, link }) => {
await slack.post("#alerts", `🚨 <${link}|${subject}> from ${sender}`);
};
# skills/appendBriefing.js
const fs = require("node:fs/promises");
module.exports = async (email) => {
await fs.appendFile("/tmp/briefing.md", `* ${email.subject} – ${email.link}\n`);
};
Auto-replies that don’t sound like a bot
# skills/autoReply.js
const { gmail } = require("openclaw/toolkit");
module.exports = async ({ threadId, sender }) => {
const draft = `Hi,\n\nThanks for the update – I\'ll review when I\'m back online.\n\nCheers,\n--\nSent via OpenClaw`;
await gmail.reply(threadId, draft);
};
You can A/B the draft via the UI; the skill hot-reloads.
Escalation paths and fail-safes
LLMs hallucinate; production incidents can’t. Add guardrails:
- Confidence score threshold: Skills can return
{ label, confidence }. If<0.4→ send to important bucket instead of junk. - PagerDuty hook: For any urgent email with subject matching
/SEV[1-2]/, callpagerduty.trigger(). - Audit log: Pipe every decision to Postgres. This saved me when the model update in OpenAI gpt-4o-mini silently changed tone detection.
Testing: unit, integration, chaos
Unit test the classifier
# test/emailPriority.test.js
const classify = require("../skills/emailPriority");
test("CEO sender -> urgent", async () => {
const tier = await classify({ sender: "ceo@acme.com", subject: "hi", body: "ping" }, { memory: { get: () => ["ceo@acme.com"] } });
expect(tier).toBe("urgent");
});
Replay 1,000 historical emails
openclaw replay --source mbox://2023.mbox --route email.received
The --dry-run flag prints routing decisions without acting.
Chaos testing escalation
openclaw chaos email --inject "production down" --expect urgent
The agent aborts if the expect block fails.
Running in production
- Scaling workers:
openclaw daemon scale 4adds three more Node workers on the same box; ClawCloud users resize in the web UI. - Rate limits: Gmail caps
users.messages.sendto 100/day by default. Move routine replies to scheduled batches (gmail.quota()) or use Outlook integration. - Monitoring: Ship
~/.claw/logs/*.logto Grafana Loki; set alert if urgent volume > N per hour. - Backups:
openclaw export memory --to s3://claw-backup/mail-$(date +%F).json
Hardening: security & privacy
Email is PII. Minimal checklist:
- Rotate OAuth tokens weekly (
openclaw oauth rotate --all). - Set
OPENCLAW_ENCRYPTION_KEYin environment; by default Claw falls back to a generated key on disk – bad for laptops. - Disable browser control skill for this agent; no reason for an email bot to control Chrome.
- Use
memory.scopesto restrict skills: classifier can read sender but not full body for junk tier.
What about multiple inboxes?
Add another Gmail integration and tag its events with the account ID. In the classifier prepend the account to the VIP lookup key (sales:vipSenders) so sales@ mail doesn’t inherit engineering alerts.
Cost snapshot
- ClawCloud Pro: $19/month per agent
- OpenAI gpt-4o calls: ≈$0.0005 / email (assuming 1K tokens round-trip)
- Composio Gmail API: free tier 10k calls / month
My midsize startup sees 4,500 emails/day. Total monthly cost: ~$9 in LLM, $19 ClawCloud, negligible bandwidth. Cheaper than one hour of me pre-coffee.
Next step: ship it
Push the repo to GitHub, point your CI at openclaw test, and enable the router. Day one you’ll still check the junk folder; day three you’ll forget it exists. When the CEO emails on Saturday and your phone buzzes, you’ll know the system works.