inbox-jev
Every ten minutes it labels your new mail with Jev, archives what has nothing for you to do and unsubscribes you from pitches it can verify.
Only mail that needs you stays in the inbox
Each email gets one of eight labels. Personal mail, and automated mail that asks you to do something, stay in the inbox. Receipts, notifications, newsletters, promotions and sales pitches are archived. When Jev isn't sure enough, the email stays where it is with Jev/Review and its best guess.
See the categories$ inbox-jev run -a you@example.com --mode act 09:10:03 run started account=you@example.com mode=act lookback=2d 09:10:05 message processed category=marketing confidence=0.94 action=archive_unsubscribe previous_contact=never archive_group=false timings_ms={gmail:212,rule:3,sent:0,jev:1480,apply:160} 09:10:05 unsubscribe list=promo.shop.example method=one_click status=ok 09:10:07 message processed category=action_required confidence=0.93 action=keep needs_reply=0.85 09:10:09 message processed category=notification confidence=0.55 action=archive archive_group=true 09:10:10 message processed category=important decided_by=rule flags_only=true 09:10:12 message processed category=newsletter confidence=0.50 action=keep low_confidence=true 09:10:13 message processed category=transactional confidence=0.99 action=archive 09:10:14 message processed category=spam confidence=0.95 action=spam reply_without_thread=true 09:10:16 message processed category=cold_outreach confidence=0.96 action=archive_unsubscribe known_contact_kept=true 09:10:17 message processed category=notification confidence=0.91 action=archive 09:10:19 message processed category=action_required confidence=0.90 action=keep needs_reply=0.20 09:10:20 run finished processed=14 archived=9 unsubscribed=1 review=1 errors=0 09:10:20 next run in 10 min (launchd)
A pass runs every ten minutes on your own machine
A launchd or systemd timer starts it. Each email gets a category, a confidence and an action, and the decision goes into a SQLite database so you can look it up later or undo it.
Follow one email throughTry it on an email(live)
Paste an email, or take a sample, and the published package decides it here the way a pass would: the headers' signals, what Jev gets, Jev's answer and the policy's verdict, for an inbox with no rules and the default categories. What you paste goes to a function on this site and on to TypeSafe under the author's key, and is not stored.
Nothing decided yet. Take a sample or paste an email, then ask Jev.
How it decides(8)
Every email goes through the same steps. The eight in the figure are worked examples from the spec, and each one is also a test that runs the real code. Pick one and follow it to what Gmail shows afterwards.
Re: quick question about your pipeline · sam@growthagency.example
Archived with Jev/Outreach, unsubscribed with one click
Jev says cold outreach at 0.96, above the 0.90 bar. The sender passes DMARC in Gmail's own headers, the email has a List-Unsubscribe header, the sender isn't a group or anyone you have written to, and the account is in act mode, so one POST with List-Unsubscribe=One-Click (RFC 8058) ends the sequence.
Spec example EX-04. A test with the same id runs the real code.
Your rules decide first
An allowlisted sender, or a sender with a rule, is decided without asking Jev. Rules come from your corrections, from rules set, or appear on their own after Jev has answered the same category three times at 0.95 or more.
Jev reads the headers and the first lines
It also gets the domains the links point to, Gmail's own SPF, DKIM and DMARC verdicts, and whether you have ever written to the sender. It never gets a full URL, and the body is never stored. It answers with one category and how sure it is.
The confidence picks the action
If it reaches the category's bar, that category's action runs. If several archive categories add up to 0.90, the email is archived under the strongest of them. Anything else stays in the inbox with Jev/Review.
What it does to your inbox(6)
One Gmail label per category
Eight labels under the Jev/ prefix, plus Jev/Needs Reply, Jev/Invoices and Jev/Review. Rename them or add a category in categories.yaml.
Archives what has nothing for you to do
Receipts, notifications, newsletters, promotions and pitches leave the inbox once Jev is at least 0.80 sure. Below the bar they stay, with Jev/Review.
Unsubscribes only from senders it can verify
Marketing and cold outreach at 0.90 or more, from a sender that passes DMARC and offers a List-Unsubscribe header, never a group or anyone you wrote to.
Learns from your corrections
Swap a label or move an email back and the next pass writes a rule for that sender. Three answers at 0.95 or more make one on their own.
One Slack message every morning
At 08:00: what needs you, what it archived, which lists it unsubscribed you from and what it learned. Short enough for a phone.
Everything it does can be undone
Every decision is in SQLite. Watch it in dry_run, then label, before act moves anything. One command brings a day's mail back.
09:10:05 message processed category=transactional confidence=0.97 action=archive 09:10:07 message processed category=notification confidence=0.55 action=archive archive_group=true 09:10:08 message processed category=newsletter confidence=0.88 action=archive 09:10:09 message processed category=marketing confidence=0.94 action=archive_unsubscribe 09:10:11 message processed category=cold_outreach confidence=0.71 action=keep low_confidence=true 09:10:12 message processed category=spam confidence=0.95 action=spam 09:10:14 message processed category=transactional confidence=0.99 action=archive 09:10:15 message processed category=notification confidence=0.91 action=archive 09:10:17 message processed category=marketing confidence=0.73 action=archive archive_group=true 09:10:18 message processed category=newsletter confidence=0.96 action=archive 09:10:19 run finished processed=14 archived=9 unsubscribed=1 review=1 errors=0
3 emails need you
you@example.com · last 24h · mode: act
Needs reply (2)
- Signed contract by Friday? · Ana Diaz · 3h ago
- Can we move Thursday? · Marta Ruiz · 19h ago
What Jev did · last 24h
| Jev/Notifications | 31 |
| Jev/Marketing | 9 |
| Jev/Newsletters | 7 |
| Jev/Transactional | 6 |
| To review | 1 |
| Processed | 64 |
$ inbox-jev run --mode dry_run --limit 30 dry run: records decisions, touches nothing processed=30 would_archive=19 would_unsubscribe=3 review=2 $ inbox-jev run --mode label labels only: nothing leaves the inbox $ inbox-jev restore -a you@example.com --since 1d restored 12 emails to the inbox newsletter 7 · marketing 4 · notification 1 labels removed: Jev/Newsletters, Jev/Marketing, Jev/Notifications restored emails are never processed again
Categories(8)
These are the defaults the package ships with, and they are one person's preferences. You can change the criteria, the bars and the actions, or add a category, in your own copy of categories.yaml.
KeepJev/ImportantNo bar
Written by a real person specifically to you: clients, teammates, partners, investors, candidates, decisions and questions addressed to you. It is not mass mail that only greets you by name. Allowlisted senders and emails you pull back to the inbox land here.
KeepJev/ActionNo bar
Automated email that needs you to do something: approve a payment, update an expiring card, verify a domain, sign a document, fix something before a deadline. Also a lead from your own website's form, a meeting someone booked with you, and a calendar invitation waiting on your answer.
ArchiveJev/TransactionalBar 0.80
The record of something you did or bought, with nothing left to do: receipts, paid invoices, order and booking confirmations, shipping updates, verification codes, copies of documents you already signed.
ArchiveJev/NotificationsBar 0.80
FYI alerts and digests from the tools you use: GitHub, Slack, Calendar, Drive, Jira, Linear, analytics, monitoring, CI. Reminders of meetings already in your calendar and plain "Accepted" replies to invitations you sent. There is nothing for you to do with them.
ArchiveJev/NewslettersBar 0.80
Editorial content you subscribed to: newsletters, digests, blog updates, industry news. It informs rather than sells.
Archive + unsubJev/MarketingBar 0.90
Promotional email from a legitimate company: ads, sales, discounts, launches, webinars, event promotions, upsells. Unsubscribed only when the sender passes DMARC and offers a List-Unsubscribe header.
Archive + unsubJev/OutreachBar 0.90
An unsolicited pitch from someone you have no relationship with, written to look personal: agencies, lead-gen, recruiting, "quick question" sequences, podcast and sponsorship invitations.
SpamJev/SpamBar 0.90
Scams and phishing: a reply that answers no thread, links to domains unrelated to the sender, a conversation between other people pasted into a message to you, an authentication pass that proves a domain other than the one it claims. Below 0.90 it stays in the inbox with Jev/Review, already labeled, for you to confirm.
Before you let it touch your mail(6)
What does it never do?
It never deletes an email. It unsubscribes only when the sender passes DMARC and the email has a List-Unsubscribe header, and even then only when the sender is not a group or a discussion list, you have never written to them, Jev is at 0.90 or more and the account is in act mode. Starred mail, mail you moved back and mail whose labels you edited by hand are left alone. Below a category's bar it does nothing that would need undoing.
What does it send to Jev?
The headers that matter, the first characters of the body, the domains the links point to without the rest of the URL, Gmail's SPF, DKIM and DMARC verdicts, and whether you have ever written to the sender. The database and the log keep the sender, the subject and the classification, and never the body. The search of your sent mail reads only the To, Cc and Bcc fields.
How do I start without risk?
Run it in dry_run for a week. Every decision is recorded and the digest tells you what it would have done, but nothing in Gmail changes and no rule is learned. Switch to label when the categories look right, and to act when the labels do, for every category at once or one at a time with act_categories.
What if it gets one wrong?
Fix it in Gmail. Swap the label, remove Jev/Review or move the email back to the inbox, and the next pass records the correction and a rule for that sender. Tomorrow's digest lists what it learned. To undo a whole day, inbox-jev restore --since 1d moves everything archived or spammed back and removes its labels. An unsubscribe can't be undone, which is why it has the longest list of conditions.
Where does it run?
On your Mac or Linux machine, as a launchd or systemd user timer that inbox-jev schedule install sets up, with a pass every ten minutes and the digest at 08:00. It can also run on a server with Docker and systemd timers, with a watchdog for stale runs, nightly backups and Slack alerts. Run one copy per account.
Why trust the rules?
Every rule is written down twice. The functional spec has 50 worked examples, and each one is a test that runs the real code. The 53 architecture decision records say why each rule is the way it is and which alternatives were rejected. A change to how it decides is checked against real mail before it is merged.
Install it in two commands
It is free, MIT licensed, and runs on your own machine. You need a TypeSafe API key and a Google OAuth client of your own, and the setup wizard walks you through both. Slack is optional. Your email goes to Google and to TypeSafe, and to no other server.
Python 3.11 or newer, uv or pipx
The first command installs the tool. The second asks for your TypeSafe key, walks you through a Google OAuth client of your own, authorizes your Gmail and offers a first dry run. Then inbox-jev schedule install runs it every ten minutes.
uv tool install inbox-jev && inbox-jev initIf you run it from a checkout of the repo, it uses the checkout. Run it on a server with Docker
$ inbox-jev init [1/5] TypeSafe API key checked, saved to .env [2/5] Google OAuth client found in ~/Downloads, copied [3/5] Slack bot token skipped [4/5] Gmail account you@example.com authorized [5/5] Schedule every 10 min + daily 08:00 first dry run? [Y/n] y processed=30 would_archive=19 would_unsubscribe=3 review=2