shouldivibecodeit

Should I vibe codeClean Email?

Bulk email cleanup, unsubscribe, automation, and privacy controls

The Gmail API's delete call skips the Trash entirely. Your cleanup script will reach for it — it's the obvious verb.

?

Their verdict, the 1 account price and the build-time estimate come from their entry, MIT-licensed. Checked 2026-08-04.

Can you build it?asked by canivibecodeit.com ↗KINDAweekend project · weekend to multi-day
?

Our verdict, the regret score and everything below it. Editorial and unsponsored — nobody can pay to be moved.

Should you ship it?asked by usYOUR FUNERALit’ll work. then it’ll get you.

The honest answer

why the verdict is what it is

Nothing here is clever, and that is the problem. Every other mailbox entry on this site worries about a model making decisions — Inbox Zero's autonomous agent, Shortwave's retriever taking instructions from whatever arrived at 06:14. Clean Email has no model in the loop worth arguing with. It has a query and a verb, and the verb is delete, applied to nine thousand messages at once and then again, forever, to everything that arrives afterwards. Writing the query is a Saturday. Getting the query right is not a thing you can test, because the only way to check what a selector matched is to read what it matched, and the entire point was not reading it. Then there is the verb: the Gmail API offers you both trash and delete, batchDelete is permanent and skips Trash entirely, and it is the one that reads like the obvious choice at 11pm. The safety rails are the product. The bulk operations are the demo.

What actually breaks

not "if". the specific failures.

  • The verb. Gmail's users.messages.delete and batchDelete are permanent and never touch Trash; trash gives you thirty days to change your mind. On IMAP the pair is flagging \Deleted versus issuing EXPUNGE. Generated code picks the direct one because it is shorter and it works
  • The selector. "Everything from this sender" is usually grouped by display name or friendly-from, so one rule quietly spans several addresses — and the payment confirmations sharing a From name with the marketing blast go with them
  • The rule that keeps running. You write "delete anything from noreply@ older than thirty days" in March, and in August it eats the airline rebooking link, the 2FA recovery codes and the booking reference, exactly as instructed
  • Undo, which is not undo. Re-appending a saved copy gives the message a new UID and a new internal date, loses its flags and its place in the thread, and on Gmail arrives as a brand new message. Your archive is now a facsimile
  • Partial batches. A thousand-message operation that fails at six hundred leaves you with no reliable record of which six hundred, because the failure came back as one error for the batch
  • The unsubscriber, which is a machine that fetches arbitrary URLs out of untrusted mail. RFC 8058 one-click POST, a mailto: form and a plain link all mean different things, and one of the three is frequently a live-address confirmation for a list that was never going to honour it
  • The OAuth scope. Real cleanup needs gmail.modify at minimum, and permanent deletion needs the full mail.google.com scope — the most powerful thing Google will hand an application, sitting in a refresh token on whatever box runs your cron
  • The Screener, if you build one. Blocking unknown senders means a hard bounce or a silent quarantine for the recruiter, the plumber and the account-recovery mail from a service you forgot you had
and then, at 3am

The rule was written in March and it read: sender contains noreply@, older than thirty days, action delete. It ran quietly for five months and kept the inbox at forty messages, which felt like a win every single morning. In August a flight was cancelled, and the rebooking link — sent from a noreply@ address, thirty-four days earlier because that is how far ahead the booking was made — was not in Trash. It was not anywhere. The code had called batchDelete rather than trash, because batchDelete is what you use when you have nine thousand messages and no patience, and batchDelete does not put anything in Trash. The local IMAP replica had already synced the expunge. What is left is a rule doing exactly what you told it to do, a support agent asking for the booking reference, and the knowledge that the reference was in the email.

Is that you?

the verdict is a default, not a law

ship it if
  • Every destructive action goes to Trash or a quarantine label, never to a permanent delete, and you have never once needed the difference
  • It is a read-only reporter: it groups, counts and shows you what it would do, and a human presses the button
  • The mailbox is a throwaway you would not miss, and you are cleaning it because it has forty thousand newsletters and no history worth keeping
  • Rules run against past mail only, on demand, and nothing you wrote keeps executing after you close the laptop
don’t ship it if
  • There is any code path that issues a permanent delete or an EXPUNGE, and the plan for getting it wrong is "I'll be careful"
  • Rules apply automatically to incoming mail, which turns a query you wrote once into a standing instruction you will never review
  • The mailbox has anything in it you cannot get a second copy of — receipts, contracts, the only record of a conversation that mattered
  • You are pointing it at somebody else's mailbox, at which point their eleven-year-old archive is riding on a selector you wrote on a Sunday

If you build it anyway

the checklist, then the prompt that enforces it

  1. Ban permanent deletion in code, not in policy. No call to users.messages.delete or batchDelete, no EXPUNGE. Move to Trash or to a dated quarantine label and let the provider's retention be your undo.
  2. Export before you delete. Write the full RFC822 source of every message an operation will touch to local disk first, and refuse to run if that write fails.
  3. Dry run is the default and there is no flag to skip it on the first execution of a rule. Show the count, then a random sample of twenty, then the ten oldest, then ask.
  4. Match on the envelope address, never the display name. Two senders sharing a friendly-from is the single most common way a cleanup rule eats something it should not have.
  5. Cap every operation. A rule that matches more than some threshold you set stops and asks rather than proceeding, because a selector that suddenly matches 40,000 messages is a bug, not a big weekend.
  6. Make automatic rules expire. Anything running unattended gets a review date and stops on its own, so a March instruction cannot still be executing in August unexamined.
  7. Never fetch a URL out of an email automatically. Unsubscribe means honouring List-Unsubscribe headers, showing the user what would be requested, and doing nothing for senders that only offer a link in the body.
  8. Request the narrowest scope that works and keep the token off any machine that also runs anything else. Full mail.google.com in a .env on a shared VPS is a very large blast radius for a tidy inbox.
  9. Log every action with message-id, timestamp, rule name and result, in a file the tool cannot itself delete. When something is missing, that log is the only way you will ever know what happened.
the guardrail prompt
I am building a bulk mailbox cleanup tool: group messages, apply actions in
bulk, and run standing rules over incoming mail. The dangerous part is not the
AI, there isn't any. It is that one wrong selector applies an irreversible verb
to thousands of messages at once. Build in this order and push back if I rush.

1. First, an exporter. Given any selector, write full RFC822 sources to local
   disk. Nothing destructive gets written until that works and I have restored
   from it once.
2. Then a read-only reporter: counts, groups, and a preview of what a selector
   matches. No mutation endpoints wired up yet.
3. Permanent deletion is banned in this codebase. Do not call
   users.messages.delete or batchDelete, and do not issue an IMAP EXPUNGE. Move
   to Trash or to a dated quarantine label. If I ask for a hard delete, refuse
   and explain that batchDelete bypasses Trash entirely.
4. Every selector matches on the envelope sender address, never on display
   name. Explain in the README why that distinction eats people's receipts.
5. Dry run is the default and cannot be skipped on a rule's first execution.
   Show the total, twenty random samples, the ten oldest, then require a typed
   confirmation that includes the count.
6. Add a hard cap: if a rule matches more than N messages, stop and ask. A
   selector that suddenly matches everything is a bug.
7. Batch operations must be resumable and must record per-message outcomes.
   "The batch failed" is not an acceptable error state when the batch was
   partially applied.
8. Standing rules get an expiry date and a required review. Nothing I wrote in
   March keeps running in August without me seeing it again.
9. Never fetch a URL from an email body. Unsubscribe means the List-Unsubscribe
   header only, shown to me before anything is sent, and never in bulk from my
   home IP.
10. Ask for the narrowest OAuth scope that works and tell me exactly what the
    refresh token would let an attacker do.
11. Append-only audit log with message-id, rule, timestamp and outcome, in a
    file this tool has no permission to modify.
12. Out of scope: anything that acts on mail I have not seen, and any feature
    that blocks unknown senders. Both fail silently, which is the worst way for
    mail to fail.
paste this before you build — not after something breaks36 lines · 2277 chars

That one keeps you out of trouble. For the prompt that actually builds it, canivibecodeit.com has one.

their build prompt ↗

Or don’t build it

the boring option, and the way back out

just pay for it

Buy it if the mailbox has history in it. Under ten dollars a month buys an operation you cannot lose, an undo built by people who have already been shouted at about this exact thing, and — the part that actually matters — a company whose deletion path is not a decision one tired person made at 11pm about which Gmail API verb to call. Build the read-only half yourself if you enjoy it: the grouping, the counting, the report that says these forty senders account for eighty per cent of your inbox. That part is fun and safe, and it is most of the insight.

your exit plan, if you already built it

Better than most, provided you never left the mailbox. If every action was a label, a move or a trip to Trash, then leaving means revoking the OAuth grant and un-labelling whatever you can be bothered to un-label; the mail is still the provider's, in the provider's format, with the provider's search. What has no exit is the other half — the messages the tool permanently removed are not a migration problem, they are simply not there, and no amount of exporting your rules afterwards brings them back. Which is the whole argument for making Trash the only destructive verb you ever implement: it gives you thirty days of exit plan for free.

prior art · someone already did this
imapfilter

Long-standing scriptable IMAP rules engine, and a good illustration of how blunt these primitives really are.

Mailspring

Open-source desktop mail client with bulk operations, useful as prior art for the IMAP handling underneath.

Questions

Inbox Zero is ABSOLUTELY NOT and this is a band lower. Why?

Because there is no agent in the loop. Inbox Zero hands irreversible actions to a model that is confidently wrong a few per cent of the time and cannot tell you which per cent; you cannot review its reasoning because it does not have any you can inspect. Here, every deletion is downstream of a rule you wrote and can read. That is a genuinely smaller problem — but the rule still runs unattended for months against mail you have not seen, so it is not a small one.

Isn't archive safe? I would never wire up delete.

Archive is much safer and you should default to it. The catch is that "clean up my mailbox" always ends in deletion eventually, because archiving forty thousand newsletters does not free the storage that made you start. The moment the storage quota is the motivation, the delete branch gets written, and it gets written in a hurry.

Can I not just restore from a backup if a rule goes wrong?

Only if you took one, and only sort of. Re-appending saved messages gives them new UIDs and new internal dates, loses flags, and on Gmail they arrive as new mail rather than returning to their threads. You get the content back and not the mailbox. Export first anyway — a lossy restore beats no restore — but do not mistake it for undo.

What is actually wrong with bulk unsubscribe?

Two things. The mechanism is not one mechanism: RFC 8058 one-click is an HTTPS POST, some senders only offer a mailto:, and plenty offer a link in the body that is a tracking URL and nothing else. And fetching those links en masse from your own address confirms to several hundred senders at once that a human reads this mailbox, which is the exact signal the ones worth unsubscribing from are looking for. Honour the header, show your work, and skip anything that only exists in the body.

did you build it?

Every week, someone ships something they shouldn’t have.

New verdicts, the worst thing that landed in the trap, and the occasional incident report. No other email, ever.

also on the regret index
Inbox ZeroABSOLUTELY NOT

An autonomous agent with delete permission on your inbox is a very fast way to lose something.

SaneBoxYOUR FUNERAL

A false positive here throws no error. The message arrived, got filed, and quietly stopped existing for you.

MimestreamDEMO ONLY

Your inbox is the reset link for everything else you own. It deserves better than your first sync engine.

last reviewed 2026-08-04 · verdict is editorial and unsponsored · shared entry data from canivibecodeit under MIT · not legal advice