Gmail Filter Tooling — Change Log
What changed, why, and how to use it going forward.
ZALORA promotional emails kept landing in your inbox despite having filters for them. Investigation found ~10 separate ZALORA filters, nearly all matching the exact address noreply@em.zalora.com.my — several of them redundant or conflicting (some trashed, some archived, some labelled). Exact full-address matching is fragile: senders rotate subdomains and addresses, so mail slips through.
1. Immediate fix (your mailbox)
Done directly via the Gmail API and verified live:
- Created one robust domain-level filter —
from:zalora.com.my→ skip Inbox + mark read. Thefrom:operator matches the whole domain including all subdomains (em., marketing, transactional), so it can't leak the way the exact-address rules did. - Archived the 2 ZALORA promos sitting in the inbox at the time.
- Verified:
from:zalora.com.my label:INBOXnow returns 0.
Also set up earlier in the session — Time internet bill
Created a Bills label (Label_51) and a filter on time.com.my → label Bills + skip Inbox. The current Time bill (RM145.20, due 09 Jun) was filed under Bills and removed from the inbox.
2. CLI updated — google-workspace v1.1.0
The CLI previously had no way to read or write Gmail filters — that work required hand-written curl calls through the broker proxy. I added five new commands so it's first-class going forward. File: ~/.hermes/tools/google-workspace.
| Command | What it does | Tested |
|---|---|---|
gmail-filters-list <email> | List all filters with their IDs, criteria & actions | ✅ returned 142 |
gmail-labels-list <email> | List labels with their Label_ IDs | ✅ found Bills |
gmail-filter-create <email> | Create a filter — --from/--to/--subject/--query + --add/--remove | ✅ created |
gmail-filter-delete <email> <id> | Delete a filter by ID | ✅ deleted |
gmail-modify <email> --ids … | Archive/label existing messages (batchModify) | ✅ via flow |
Create & delete were tested as a full round-trip on a throwaway filter (create → capture ID → delete → confirm). Syntax validated with bun build.
Usage examples
# See every filter (with IDs you can delete by) google-workspace gmail-filters-list leeweisern@gmail.com # The robust fix pattern: domain-level, not exact-address google-workspace gmail-filter-create leeweisern@gmail.com \ --from zalora.com.my --remove INBOX,UNREAD # Route a bill to a label and out of the inbox google-workspace gmail-filter-create leeweisern@gmail.com \ --from noreply@time.com.my --add Label_51 --remove INBOX # Archive/label messages already sitting in the inbox google-workspace gmail-modify leeweisern@gmail.com \ --ids 19e8...,19e7... --remove INBOX,UNREAD # Remove a filter google-workspace gmail-filter-delete leeweisern@gmail.com <filterId>
Label semantics: INBOX = inbox · UNREAD = unread · TRASH = trash · SPAM = spam. Archive = remove INBOX. Mark read = remove UNREAD.
3. Skill updated — gmail
Added a Filters and labels section to the gmail skill documenting the new commands, plus two durable lessons so this knowledge survives:
- Pitfall captured: exact-address filters leak — prefer domain-level
--from <domain.tld>. - Workflow captured: the exact "fix a leaking filter" recipe (list → consolidate to one domain rule → retro-apply to inbox → verify → prune redundant rules).
Next time you ask to manage Gmail filters, I run a single CLI command instead of hand-crafting API calls — faster, less error-prone, and the approach is documented so it stays consistent.
Still open — your call
- Delete the ~10 redundant ZALORA filters? They're now harmless (the domain rule supersedes them) but they're clutter. I can clear them cleanly with the new
gmail-filter-delete. - Distribution caveat: this CLI lives in
~/.hermes/tools/, which is fleet-managed. The edits work now and are local to this host — but a future registry update could overwrite them. To make it permanent across the fleet, it would need to go upstream to the operator. Say the word and I'll flag it.
Generated by Hermes · changes applied and verified against the live account before this page was written.