Gmail
Judge each new email as it arrives, then archive, label, star or trash it, or save a draft reply.
Jev Events checks the inbox, asks Jev your questions about each new email, and runs what you pick on the answers. Choose below, and the code and the steps to run it fill in.
With your own Gmail, on your computer. The quickest way to try it.
What to watch
New email in the inbox. Every email that lands in the inbox from now on.
What to ask
Jev answers every question about each email, in one request.
What to do
Each rule runs when an answer is likely enough. Leave the rules out to print every answer.
Takes it out of the inbox. It stays in All Mail.
Adds a Gmail label, and creates the label the first time.
The code
import { monitor, recipes } from "jev-events";import { google } from "@jev-events/google";const inbox = monitor({ // Also judges the 5 latest emails on the first run, so there's something to see right away. source: google.gmail.inbox({ backfill: 5 }), questions: { kind: recipes.email.kind, needsReply: recipes.email.needsReply, }, // Native actions only log what they would do. Set dryRun: false once the answers look right. dryRun: true,}) .on("kind:newsletter", { min: 0.9 }, google.gmail.archive()) .on("needsReply", { min: 0.8 }, google.gmail.label("Needs reply"));await inbox.start();console.log("Watching the inbox for new email. Stop with Ctrl-C.");Run it
- 1
Make a folder for it, and install Jev Events and
tsx, which runs TypeScript. Jev Events needs Node.js 22 or newer.mkdir my-monitor && cd my-monitornpm init -y && npm pkg set type=modulenpm i jev-events @jev-events/google tsx - 2
Sign in with your Google account. The first time, the command walks you through creating your own OAuth client in Google Cloud.
npx jev-events auth google - 3
Put your TypeSafe API key in
.env.echo "TYPESAFE_API_KEY=<your key>" >> .env - 4
Save the code as
monitor.ts, and run it.npx tsx --env-file=.env monitor.ts - 5
Native actions log a
[dry-run] would …line instead of running. When those look right, setdryRun: false.
Try it without code
To see what Jev says about your own mail before writing anything, install the integration, sign in once and watch. It prints an answer for the 5 latest emails, then for each new one:
npm i jev-events @jev-events/google
npx jev-events auth google
npx jev-events watch gmailBy default it asks whether each email needs a reply, whether it's urgent, and what kind of email it is. Ask your own with --ask "refund=Is this asking for a refund?". See the CLI.
The source
google.gmail.inbox() emits each new email in the inbox. It checks every 15 seconds; pass every: "1m" to monitor() to check less often.
| Option | Default | What it does |
|---|---|---|
backfill | 0 | Also emits this many of the latest emails on the first check |
label | "INBOX" | Watches another label instead, by its ID |
protect | Colleagues and people you've emailed | Who native actions never touch. false protects no one |
Actions
| Action | What it does |
|---|---|
google.gmail.archive() | Takes the email out of the inbox. It stays in All Mail. |
google.gmail.label(name) | Adds a label, creating it the first time. |
google.gmail.star() | Stars it. |
google.gmail.markRead() | Marks it as read. |
google.gmail.draftReply(text) | Saves a reply as a draft in the thread. It is never sent. |
google.gmail.trash() | Moves it to Trash, where Gmail keeps it for 30 days. |
text can be a string or a function of the event. Actions are dry-run until you pass dryRun: false to monitor().
Emails
Handlers receive a GmailItem:
| Field | Type | Notes |
|---|---|---|
subject | string | |
body | string | The new text, with quoted replies cut |
from, to, cc | { name?, address } | |
labels | string[] | Gmail label IDs, such as INBOX or IMPORTANT |
category | string | Gmail's inbox tab: personal, social, promotions, updates or forums |
mailingList | boolean | Sent through a list or bulk sender |
attachments | { filename, mimeType, size }[] | |
protectedBecause | string | Why native actions skip it, if they do |
Jev sees who wrote it, the subject and the new text, plus facts worked out from the account: whether it was sent to you directly, whether it's a reply, whether the sender is a colleague or someone you've emailed before, and its attachments.
Good to know
- Protected by default. Native actions skip mail from people at your company and from people you've emailed before. Your company is your address's domain, and public ones such as gmail.com never count. Change it with
protect: { addresses, except }, or turn it off withprotect: false. A skipped action says why, for examplecolleague at acme.com. - Nothing is deleted or sent. The sign-in can't delete mail permanently.
trash()is the only removal, and it can be undone for 30 days.draftReply()leaves a draft for a person to read and send. - One sign-in, Gmail and Calendar.
google.app()asks for both. The builder asks only for what its monitor needs, such asgoogle.app({ scopes: ["gmail"] }). - A server with one account of your own.
google.fromEnv()builds the connection fromGOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRETandGOOGLE_REFRESH_TOKEN:await inbox.start({ connections: [google.fromEnv()] }).
Letting other people connect Gmail
While your consent screen is in Testing, only the test users you add can connect, and Google signs them out after 7 days. Before anyone else connects, Google has to verify your app. Gmail's scope is restricted, so it also needs a security assessment every year. Calendar only needs the verification.