If you already have three browser tabs open just to keep up with personal Gmail, corporate Outlook, and that random side-project inbox, this post shows exactly how to fold them into one OpenClaw agent and stay sane.

The problem: context switching, multiplied by three

Most tutorials stop at “connect Gmail.” Real life is uglier: the CTO wants you answering corp@domain.com, your consulting client pings you@freelance.io, and Mom still expects a Sunday reply to gmail.com. Copy-pasting between accounts wastes minutes that compound into lost hours. OpenClaw v1.9.4 (Node 22+) ships with Composio connectors for Gmail and Microsoft 365, so the agent can pull, summarise, and write email on your behalf—per account, with different tones, signatures, and routing rules.

Prerequisites: tokens, scopes, and a clean Node 22 install

You need:

  • Node 22.2+ and npm 10+
  • An OpenClaw daemon spun up locally or on ClawCloud. I’ll use the hosted flow because OAuth redirects are simpler.
  • Two OAuth apps—one in Google Cloud, one in Azure—that each grant https://mail.google.com/ or https://graph.microsoft.com/Mail.ReadWrite scopes.
  • A Composio key (free tier works)

Install the CLI if you haven’t already:

npm create openclaw@latest my-mail-claw && cd my-mail-claw && npm run dev

This boots the gateway on http://localhost:3301 and the daemon on port 3322.

Adding Gmail and Outlook accounts

1. Wire Gmail through Composio

  1. Navigate to the OpenClaw UI → Integrations → Email → Gmail.
  2. Click Connect via Composio. You’ll be redirected to Google’s consent screen. Approve mail.readonly and mail.modify.
  3. Back in OpenClaw, you’ll see a new gmail_personal credential. The JSON lives in ~/.openclaw/credentials.json.

2. Wire Outlook (Microsoft 365)

  1. Same screen, choose Outlook (Graph).
  2. Authorize Mail.ReadWrite and offline_access.
  3. The credential ID becomes outlook_work.

The daemon hot-reloads; check logs:

[mail] Registered gmail_personal [mail] Registered outlook_work

Creating a unified inbox digest

The trick is OpenClaw’s schedule block plus the email.digest action. Every morning at 07:00, we’ll pull unread threads from both accounts, deduplicate by Message-ID, group by sender, and shove the summary into Slack.

{ "schedules": [ { "cron": "0 7 * * *", "actions": [ { "use": "email.digest", "with": { "accounts": ["gmail_personal", "outlook_work"], "maxThreads": 50, "summaryStyle": "bullet", "output": { "type": "slack.post", "channel": "#daily-mail" } } } ] } ] }

A couple of notes:

  • Cron syntax is server-side. If you run on ClawCloud, the timezone is UTC unless you set CLAW_TZ.
  • maxThreads 50 keeps API quota reasonable. Gmail starts throttling after ~500 calls per user per minute.
  • output can be email back to yourself, Discord, or plain text. Slack is easiest to eyeball.

Per-account response styles with prompt templates

Writing an investor update from your work address demands a different tone than telling Dad you’ll visit next weekend. OpenClaw lets you mount multiple prompt templates and select them dynamically.

// prompts/email_work.md You are the Director of Engineering responding from a corporate Outlook account. Keep tone formal, avoid contractions, end with "Best, ". // prompts/email_personal.md You are writing from your personal Gmail. Keep it friendly, use emojis sparingly, sign off with "Cheers, ".

Add them to openclaw.config.json:

{ "prompts": [ { "id": "work_response", "path": "prompts/email_work.md" }, { "id": "personal_response", "path": "prompts/email_personal.md" } ] }

Binding prompts to accounts

Now create a responder workflow:

{ "workflows": [ { "id": "auto_reply", "on": { "email.received": { "accounts": ["gmail_personal", "outlook_work"], "filter": "is:unread newer_than:2d" } }, "actions": [ { "use": "email.reply_draft", "with": { "choosePrompt": "${ account == 'outlook_work' ? 'work_response' : 'personal_response' }", "dryRun": true } } ] } ] }

dryRun true means drafts are saved but not sent. When you trust the agent, flip to false. The ternary expression is JavaScript-ish; OpenClaw interprets via vm2 sandbox.

Routing rules: which account handles what

Sometimes you want to reply from work even if the message hit personal. Example: you forwarded vendor quotes to yourself and now need the purchasing alias to answer. We can add a router.js module:

// router.js module.exports = (msg) => { const { from, to, subject } = msg; // If the thread contains the word "invoice" use the work account if (/invoice|quote|purchase/i.test(subject)) return "outlook_work"; // Family group thread => personal if (to.includes("family@example.com")) return "gmail_personal"; // Default: reply from whichever account received the message return msg.account; };

Reference it in the workflow:

{ "actions": [ { "use": "email.reply_draft", "with": { "account": "${ require('./router')(msg) }", "choosePrompt": "${ account == 'outlook_work' ? 'work_response' : 'personal_response' }" } } ] }

Under the hood, OpenClaw passes msg (a normalised MIME 822 object) into the CommonJS require. Hot reload works, so tweak and save while the daemon keeps running.

Putting it all together in production

Local testing is fun until your laptop sleeps. Push the agent to ClawCloud:

  1. claw login – opens a browser, OAuth with GitHub.
  2. claw deploy – zips current directory, streams build logs.
  3. Set environment variables in ClawCloud dashboard:
COMPOSIO_KEY=ck_live_XXXXXXXXXXXXXXXXX GMAIL_CLIENT_ID=... GMAIL_CLIENT_SECRET=... OUTLOOK_CLIENT_ID=... OUTLOOK_CLIENT_SECRET=... CLAW_TZ=America/Los_Angeles

Deployment takes ~15 seconds. The unified digest will hit Slack the next time the cron fires. Draft replies appear in the respective Drafts folder of Gmail and Outlook, ready for human eyeballs.

Tip: enable shadow mode in Settings → Safety. The agent writes drafts and logs proposed sends to #claw-audit. Flip the toggle once you’re convinced.

Troubleshooting and community notes

Gmail quota errors: users on GitHub report sporadic Rate Limit Exceeded after 500 calls. Drop maxThreads or add throttle.ms": 200 under the with block.

Outlook "Invalid grant": Microsoft kills refresh tokens after 90 days unless you set allow public client flows in Azure Portal → Authentication.

Slack digest formatting: Use plain text if your workspace disables mrkdwn. Set format: 'plain' inside output.

Memory bloat: Earlier builds (<1.9.0) leaked listeners on imap-idle. Upgrade; the fix landed in PR #2133.

Develop on Windows: Node 22’s native fetch collides with corporate proxies that MITM TLS. Set NODE_TLS_REJECT_UNAUTHORIZED=0 or use WSL2.

Still stuck? Most answers land within an hour on the GitHub Discussions board. Worst case, hop into the #email channel on OpenClaw Discord; someone’s always debugging IMAP there.

Next step: let the agent send your first real reply

Keep drafts for a day or two, skim them, fix any odd phrasings, then flip dryRun off. The cost of a bad automated email is low; the cost of constant tab-switching is your focus. Once the agent proves itself, you can add more rules—for example, auto-file receipts into Notion via Composio, or schedule follow-ups if a prospect ghosts you. OpenClaw is happiest when it owns the busywork and you get back to engineering.