A Google Sheets control panel for auto-labeling and auto-filtering Gmail by vendor/sender, driven by plain-English descriptions instead of hand-written search queries.
Each row in the sheet is one "group" (e.g. a vendor, a bank, a service): you write a plain-English description, an LLM (Gemini, free tier) turns it into a Gmail label name and search query, and toggling Active/Inactive labels the matching mail retroactively and installs/removes a real Gmail filter so future mail keeps getting labeled automatically — all from the spreadsheet, no per-email manual labeling.
- Description → Label + Search Query, via AI. Write something like
subgroup all bilt under bank and cc and label biltand it produces a nested label (Bank & CCs/BILT) and a real Gmail search query, reusing existing labels/parents where they already exist. - Grounded, not guessed. Before calling the LLM, the script pulls real sender addresses from mail already under the row's label (or a first-pass search on the description itself for brand-new vendors), so the generated query is based on actual senders in your mailbox, not a guess.
- Active/Inactive toggle drives real Gmail state.
Activeretroactively labels matching mail and creates a Gmail filter (via the Gmail API — labels stay in the inbox, nothing gets archived).Inactiveremoves the filter and strips the label from previously-labeled mail. - Nested labels actually nest. Gmail's sidebar only renders a
collapsible parent group when the parent segment of a
Parent/Childlabel name exists as its own label object — it does not infer the tree purely from/in sibling names. The script creates any missing parent segments automatically, soBank & CCs/BILTshows up nested under a realBank & CCsgroup, not as a flat label. - Auto-colored labels. New labels are colored once, at creation, by sampling a plausible brand color out of real matching HTML email and snapping it to Gmail's fixed label-color palette (the Gmail API only accepts ~89 specific colors, not arbitrary hex). A menu action can also (re)color existing labels on demand.
- Handles multi-row edits/pastes, retries transient LLM API errors with model fallback, skips redundant filter rebuilds when a query hasn't changed, and has an hourly backstop trigger to finish very large first-time label backfills that exceed one execution's time limit.
- A Debug: List Labels Matching... menu item dumps the raw Gmail label object (id, type, visibility, color) for any substring match, for diagnosing label/filter weirdness directly instead of guessing at it.
- Create a Google Sheet. Rename its first tab to
Groups. - Add a header row (order matters):
Description | Active | Label | Search Query | Filter ID | Last Synced | Status Extensions > Apps Script, delete the boilerplate, paste inCode.gsfrom this repo. Save.- In the Apps Script editor:
Services > +→ add the Gmail API (advanced service). (appsscript.jsonin this repo shows the scopes/ service the project ends up needing, for reference — Apps Script manages this file for you once the advanced service is added via the UI.) - Reload the spreadsheet tab so the Gmail Groups menu appears.
- Menu → Set Gemini API Key → paste a free key from Google AI Studio.
- Menu → Add Active/Inactive Dropdown (turns the Active column into a proper dropdown).
- Menu → Enable Auto-Sync → approve the Gmail OAuth prompt (one-time).
- Optional: Add Status Colors, Enable Hourly Backstop.
Fill in a Description cell for a row (e.g. all emails from idaho power about my electric bill) and either wait for auto-sync to fill in Label
and Search Query, or run Regenerate Label & Query from Description
from the menu. Set Active to Active to sync it to Gmail; set it back to
Inactive to unwind it (removes the filter, strips the label from mail).
Label and Search Query can also be edited by hand — that skips the AI
call and just re-syncs with whatever's in the cell.
- Renaming
Labelon an already-Activerow doesn't clean up the old label automatically — toggleInactivethenActiveagain for a clean switch. - Gmail label colors are snapped to the nearest of Gmail's fixed palette, not the vendor's exact brand hex, and many emails have no solid color block to sample from (left default-colored in that case).
- Very large first-time backfills may need more than one execution due to Apps Script's 6-minute run limit; the Enable Hourly Backstop trigger handles this automatically, or re-run Sync All Rows Now manually.
MIT — see LICENSE.