Gmail keeps old automated mail forever unless something cleans it up.
It also ends up storing things that should not sit in email for years: temporary passwords, API keys, recovery codes, login details.
Gmail AutoPurge is a small Google Apps Script that moves old, labeled Gmail threads to Trash. Labels decide what kind of mail a thread is. The script applies the expiration dates.
It also scans recent mail once a day and labels obvious credential emails as Credentials.
| Label | Use it for | Default action |
|---|---|---|
Purge |
old automated mail | move to Trash after 1024 days |
Credentials |
possible passwords, API keys, recovery codes, login details | move to Trash after 16 days |
Keep |
records or mail history worth keeping | protects Purge threads |
Unlabeled mail is ignored.
Starred threads protect Purge threads.
Threads with a reply from you also protect Purge threads. When the cleanup sees one, it removes the Purge label instead of trashing the thread.
Credentials is the sharp label: Keep, stars, and your replies do not protect it. If a thread has Credentials for 16 days, it goes to Trash.
Actual Gmail searches:
label:Purge older_than:1024d -in:chats -in:sent -in:drafts -in:spam -in:trash -label:Keep -is:starred
label:Credentials older_than:16d -in:chats -in:sent -in:drafts -in:spam -in:trash
Trash, Spam, Sent, Drafts, and Chat threads are skipped.
Purge waits until at least 25 old threads pass safety checks, then moves up to 50 threads per run. Threads with your replies do not count toward that delete batch.
Credentials starts at 1 match and also moves up to 50 threads per run.
Oldest matches move first.
Use it for two jobs:
- give possible credentials a short fuse
- give old automated mail an expiration date
Good candidates:
- possible passwords, API keys, recovery codes, login details
- old login codes
- account confirmations
- delivery updates
- routine notifications
- ride receipts
- weekly digests
This is a small utility. Labels decide. The script cleans up on schedule.
Some services still email actual secrets.
The Credentials label is for messages that may contain plaintext passwords, temporary credentials, login details, API keys, recovery codes, backup codes, or similar secrets.
The script can auto-label threads that look like credential emails. It first uses a Gmail search prefilter, then reads the message text with strict classifier patterns before applying the label.
Current password-word prefilter languages: English, Japanese, Russian/Ukrainian, and Thai.
Auto-labeling is enabled by default in the daily cleanup run. The daily run checks the last 48 hours. It only applies the Credentials label and writes to Apps Script logs. It does not send a separate email report.
For a first pass over all matching mail, run:
labelAllCredentialCandidates()That is a live labeling run across matching mail.
For a normal recent scan, run:
labelRecentCredentialCandidates()If a message is not credentials, remove the label.
If it is credentials, move the secret to a password manager, rotate it if needed, and let the email age out.
Reports are on by default.
Normal Purge reports are compact.
Credentials reports are separate and loud:
REVIEW NOW: 3 possible CREDENTIALS moved to Trash
The report body is a compact thread list. Rows link back to Gmail Trash when possible.
Report emails get the Purge label, so they can expire later too.
To disable normal success reports:
const SEND_DELETE_REPORT = false;Credentials and error reports still send.
gmail-filters.xml is not a finished ruleset. It is three starter examples:
- one-time code / account confirmation style mail ->
Purge - a simple
login passwordheuristic ->Credentials - formal records like invoices, statements, contracts, transfers, tax, insurance ->
Keep
Review before importing. Delete what does not fit. Edit what almost fits. Add your own rules.
The included filters only apply labels. They do not delete, archive, mark as read, skip inbox, mark important, or forward mail.
Receipts are not kept by default. Some matter: tax, warranty, medical, business, expensive purchases. Plenty do not: coffee, parking, food delivery, tiny routine purchases. Add personal Keep filters for the ones that matter.
Codex Desktop can do the browser work. Google permission screens still need a human.
Copy this prompt:
Set up Gmail AutoPurge from this repository:
https://github.com/progbeat/gmail-autopurge
Use browser or computer control for Gmail and Apps Script setup.
Follow the README setup instructions. Do not invent a separate setup flow.
Pause at Google OAuth and permission screens so the user can approve them manually.
- Open script.google.com.
- Create a new Apps Script project.
- Paste the contents of all
.gsfiles. - Save the project.
- Select
installfrom the function dropdown. - Click Run.
- Approve the Google permissions.
- Open Gmail settings.
- Go to Filters and Blocked Addresses.
- Choose Import filters.
- Select
gmail-filters.xml. - Review every filter.
- Confirm each action is only Apply label.
- Create the filters.
install() creates or reuses Keep, Purge, and Credentials, then creates one daily trigger for runPurgeCleanup.
Run this before trusting the broom:
previewPurgeCleanup()Preview mode logs candidates and moves nothing.
To test report emails without moving anything:
testPurgeReports()This sends report emails for current candidates. It still does not move mail to Trash.
Edit these at the top of Code.gs:
const RETENTION_DAYS = 1024;
const CREDENTIALS_RETENTION_DAYS = 16;
const DRY_RUN = false;
const MAX_THREADS_PER_RUN = 50;
const MIN_THREADS_TO_DELETE = 25;
const SEND_DELETE_REPORT = true;
const AUTO_LABEL_CREDENTIALS = true;
const CREDENTIALS_AUTOLABEL_WINDOW_HOURS = 48;Useful changes:
DRY_RUN = truefor preview-only scheduled runsSEND_DELETE_REPORT = falseto disable normal success reportsRETENTION_DAYSif 1024 days is not your numberCREDENTIALS_RETENTION_DAYSif 16 days is too fastAUTO_LABEL_CREDENTIALS = falseif you want manualCredentialslabeling onlyCREDENTIALS_AUTOLABEL_WINDOW_HOURSif 48 hours is too short or too wide
If something moved by mistake:
- Open Gmail Trash.
- Move the thread out of Trash.
- Remove the wrong label.
- Fix the filter that added it.
Do step 4. Otherwise the same thing can happen again later.
Manual copy-paste is fine for first setup.
For updates, clasp is nicer:
./clasp-push.shThis expects .clasp.json to point at the Apps Script project. The file is ignored by Git because it is local setup, not repo content.
clasp-push.sh refuses to run if .clasp.json or ~/.clasprc.json is readable by other users. Fix that once:
chmod 600 .clasp.json ~/.clasprc.json| File | Purpose |
|---|---|
Code.gs |
cleanup, labels, triggers, reports |
CredentialClassifier.gs |
strict credential prefilter and body patterns |
gmail-filters.xml |
example Gmail filters |
appsscript.json |
Apps Script manifest |
clasp-push.sh |
local update helper |
This is not a classifier, secret scanner, or mailbox oracle.
The labels do the deciding. The script enforces the expiration dates.
Bad filters make bad cleanup. Good filters make Gmail a little less clogged.