Connection
Folders
iPick the folders to scan. Gmail uses special names like [Gmail]/All Mail, [Gmail]/Trash (may be localized). Connect first to load the list.
Not connected - folders will appear here. Defaults to INBOX.
Add to scan just includes a folder in the list above (creates nothing). Create on server actually creates a new folder - a label on Gmail - on your mailbox (e.g. to use as a move destination).
⚠️ Every action - Run, List senders, Empty folder, Move - applies to the selected folders above. On a folder row, 🗑 deletes it on the server (system folders like Trash/Sent are protected and have no 🗑), while × only removes it from this list.
Manual Cleanup
Choose which messages to act on: paste senders / domains as a Target list, or build a Rule with the query builder. This selects the messages for Count, Run and Export - and, with AI Cleanup, the senders the AI analyzes.
✓ Works with AI Cleanup too: the Target list / Rule selection and the Load from file / Save to file / Clear all buttons and the Load saved Spam addresses flag still apply - AI takes them into account in its search. Leave it empty to have AI analyze the whole selected folder(s).
•
spam@example.com - exact sender address•
*@newsletter.com - that domain exactly, never subdomains•
annoying.com - that domain, plus subdomains if “Include subdomains” is on•
mail.annoying.com - that specific subdomain• lines starting with
# are commentsIn "full" mode,
*@paypal.com = paypal.com without Include-subdomains. The point: with Include-subdomains on, *@paypal.com stays exact while bare domains (e.g. newsletter.com) also catch their subdomains - so you can mix per-entry. ("search" mode is always a substring match.)
one entry per line · reuse across runs
Rules match server-side (substring), so a domain also matches its subdomains and partials - and *@ is not special here. For an exact domain with no subdomains, use a Target list with *@domain.com and scan mode "full".
Options iThese shape a Target list / Rule run. AI Cleanup decides what to delete on its own and ignores all of them (it always scans server-side, never moves/empties, and handles Gmail Trash automatically). Expunge (in the Run section below) is the exception - it applies to AI Cleanup too.
Below options apply to a Target list / Rule run only - AI Cleanup ignores them.
AI Cleanup and Rule matching both always use a fast server-side SEARCH (subdomains already included), so Scan mode is disabled - it applies only to Target lists.
⚠️ On Gmail, deleting without “move to Trash” does not delete: it only removes the INBOX label, so the message is archived to All Mail (still there, recoverable). To actually delete, tick “Gmail: move to Trash” and run; then, after the run (a separate step), select [Gmail]/Trash and run Empty folder.
Pick an existing folder, or choose ➕ Create new… to make one (a label on Gmail). Tip: leave the Target list / Rule empty to move every message in the selected folders.
⚠️ Will delete all messages in the selected folders (the ones ticked under Folders), not just matches.
Counts how many emails match the current filter in the selected folders - makes no changes.
These also follow your filter: Export downloads the matching messages - or the whole selected folder(s) if no filter is set; Import appends into the single selected folder (one only, not more).
.mbox file - same matching as Count (targets/rule, "search" or "full"), or the whole folder when no filter is set. Read-only: it does not mark anything as read. Import appends the messages from a .mbox (or single .eml) file into the one folder you have selected.
✨ AI Cleanup iScores every sender with a heuristic spam score (List-Unsubscribe, unread ratio, send frequency, sender pattern, bulk), then - with a configured model - asks an LLM to judge the senders above your threshold. Press Run to act: with Report only ticked it just builds the report (deletes nothing); without it, Run deletes the senders the LLM confirms. It honors your Target list / Rule as a filter, or scans the whole folder when empty.
pip install "imap-cleanup-tool[ai]"
then restart the web UI.
No LLM model configured. You can still run a heuristic-only report - tick Report only + Skip LLM below, then Run. Deleting for real (Report only off) needs a model. Tip: add a model in the LLM tab for a more precise, LLM-verified analysis.
Advanced: scoring weights (calibrated)
These weights drive the heuristic spam score (the local analysis that runs before any LLM). You can change them, but they were calibrated through testing - it is recommended to leave the default values unless you know what you are tuning.
Press Run (below) to launch: with Report only it just builds the report; without it, it deletes the confirmed senders. Saved reports live in the Report tab (download / delete there).
Log
⚠️ Disabled on Gmail (no effect here): Gmail auto-expunges deleted messages itself. With “Gmail: move to Trash” they go to the Trash - then, after the run, select [Gmail]/Trash and use Empty folder to remove them for good.
Removes the flagged messages permanently. Without it they are only flagged \\Deleted - not every server drops those on its own, so they can linger until expunged. Applies to manual runs and AI Cleanup.
How many messages each IMAP request handles. Used at full size by: Count / Run / Export matching in "full" mode (header fetch), Export (message bodies), the Import duplicate-check (Message-IDs), and Delete / Expunge / Move / Empty. Capped for a few: AI Cleanup and List senders fetch headers at max 50/request, and on Gmail "move to Trash" is auto-capped at 200 (so these ignore a larger batch size). The "search" mode does one server-side SEARCH, so batch size doesn't apply there. The import itself is one message at a time (IMAP APPEND), so it isn't batched.
Runs the action chosen above on the selected folders: Manual Cleanup (delete / move / empty folder for the matching messages) or AI Cleanup (heuristic + LLM, then delete the confirmed senders). With dry-run on, “Run” only previews - nothing is changed.
ℹ️ On Gmail, some folders (e.g. [Gmail]/Drafts) may need more than one pass for deleting messages - including Empty folder - because of how Gmail handles them. If some messages remain, just run it again. Or prefer the standard flow: Run with “Gmail: move to Trash”, then after the run select [Gmail]/Trash and tick Empty folder.
Shows the senders in the selected folders, following the selected filters (Target list / Rule) when one is set, ranked by how many emails each sent - use it to decide what to target.
Downloads the last “List senders” result to your browser’s download folder. Edit the file name if you like; with “ask where to save” enabled, your browser also lets you choose the folder.
New scheduled job
A job runs the profile you're connected with: connect via a saved (non-encrypted) profile in the Cleanup tab, then here you set the SMTP profile and when it runs. Jobs are scoped to that profile - the Saved jobs list shows the connected profile's jobs. Configure what the job does (AI or Manual, match, options, folders) in the Cleanup tab.
Everything this job does - AI or Manual, the match, the exclude list, the options and the folders - is taken from the Cleanup tab. Change the behaviour there; the Job summary below updates live. The connection account is the profile you're connected with; here you set the SMTP profile and the schedule.
⚠️ The optional [ai] extra isn't installed where this app runs, so an AI job can't run here. Install it where the app (and the scheduled task) runs: pip install "imap-cleanup-tool[ai]", then restart. A Manual job still works.
Saved jobs
Runs via the system scheduler (Task Scheduler / cron) - even when this app is closed.AI Cleanup reports iEvery AI Cleanup report or run auto-saves a timestamped CSV, per account. This table lists the connected mailbox's reports - newest first. Download or delete one, or select several (this page, or all pages) and download as a zip or delete in bulk. Deleting only removes the CSV file, never any email.
Connect in the Cleanup tab to see this account's saved reports.
No saved reports for this account yet. Run an AI Cleanup (or Report only) from the Cleanup tab and it will appear here.
| Report file | Date | Time | Actions |
|---|
pip install "imap-cleanup-tool[ai]" then restart the web UI.
Add / edit a model
The model used by AI Cleanup. Local-first & BYOA (Bring Your Own API key): run a free local model (Ollama) so nothing leaves your machine, or bring your own key for any cloud model (OpenAI, OpenRouter, ...). Powered by litellm, so both use the same form. The key is stored in a local SQLite DB and can be encrypted. ⚠️ Remote models send only sender subjects + stats (never the message body) to that provider - for full privacy prefer a local model.
🔒 This model's key is encrypted. Leave the key blank to keep it (and its passphrase); type a new one to change it.
- ○ at least 8 characters
- ○ a lowercase letter
- ○ an uppercase letter
- ○ a number
- ○ a special character
- ○ both passphrases match
This passphrase is never stored - you'll need it to use the model (encrypted models can't run scheduled jobs).
Configured models
No models yet.
Add / edit an SMTP profile
Outgoing mail server used to send notification emails. The password is stored locally (SQLite) and can be encrypted (an encrypted profile can't run in scheduled jobs). Supports any provider - Gmail, Amazon SES, Outlook, SendGrid, etc.
🔒 This profile is encrypted. Leave the password blank to keep it (and its passphrase); type a new one to change it.
- ○ at least 8 characters
- ○ a lowercase letter
- ○ an uppercase letter
- ○ a number
- ○ a special character
- ○ both passphrases match
This passphrase is never stored - you'll need it to use the profile (encrypted profiles can't run scheduled jobs).
Notifications
Get an email when a cleanup finishes. One profile is active. By default emails are sent for scheduled jobs; you can also enable them for interactive runs. For Gmail accounts the email reminds you to empty the Trash.
Saved SMTP profiles
No profiles yet.
Spam addresses
List-Unsubscribe header. done = already unsubscribed (✓ done). After a bulk Unsubscribe this jumps to manual so you can finish the leftovers by hand.
Senders flagged by AI Cleanup (report or run) for the connected mailbox, with their heuristic spam score and the LLM verdict. Click a column header to sort the whole list. Select rows to remove them from the list, or flag senders as spam - a popup lets you either move one message to the Junk/Spam folder and delete the rest (like Run), or just move all their mail to Spam. It scans the folders selected in the Cleanup tab (just like a run); the popup shows which ones. Moving mail to Spam is the standard "report spam" signal that trains the server to route their future mail to spam. Each connection has its own list. You can also add a sender manually below, and load matching addresses into a Target list from the Cleanup tab. To unsubscribe from newsletters, select senders and click Unsubscribe: it is automatic where possible; the rest only offer a confirmation page (a link ↗ in the Unsub column) - use the Unsub filter (top right) to list just those and open each by hand.
SenderiThe sender's email address (the local-part + domain extracted from the From header). Click to sort. |
ScoreiHeuristic spam score, 0-10. A weighted blend of the signals below, normalized to 10: List-Unsubscribe present, unread ratio, send frequency, Precedence: bulk, and a bulk-looking sender name (noreply@, newsletter@…). Higher = more spam-like. Click to sort. |
MsgsiTotal messages seen from this sender in the scanned folder(s). Click to sort. | UnreadiUnread ratio = messages without the \Seen flag ÷ total. A high ratio means you rarely open their mail. Click to sort. |
Msgs/weekiSend frequency: messages per week, estimated from the span between the oldest and newest Date headers. Frequent senders score higher. Click to sort. |
SignalsiBoolean flags that feed the score: unsub = has a List-Unsubscribe header; bulk = Precedence: bulk; pattern = a bulk-looking sender name (noreply@, newsletter@, no-reply@…). Sorts by how many signals are set. |
LLM verdictiThe model's judgement (only if AI Cleanup ran with a model): delete or keep, with its reason and confidence. Sorts by verdict then confidence. Empty if heuristic-only. | UnsubiHow each sender can be unsubscribed (from its List-Unsubscribe): ✓ done = already unsubscribed (hover for the method, date and result); auto ✉ = automatic, an email sent from your active SMTP profile; auto = automatic, a one-click HTTPS request (no SMTP needed); link ↗ = a confirmation page you open by hand; rescan = header seen but no link stored yet (run a fresh AI report; if it persists, clear the cache and retry); none = no List-Unsubscribe, so it can't be unsubscribed from here. Use the Unsubscribe button on selected rows, or the Unsub filter above to narrow the list. |
|
|---|---|---|---|---|---|---|---|---|
| No spam addresses yet - run an AI Cleanup report first. | ||||||||