Jev Events

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.

An email it would read
Dana Reyes <dana@acme.com>
Q3 deck
Could you look over the Q3 deck before Friday? Mostly the pricing slide.

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

monitor.ts
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. 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. 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. 3

    Put your TypeSafe API key in .env.

    echo "TYPESAFE_API_KEY=<your key>" >> .env
  4. 4

    Save the code as monitor.ts, and run it.

    npx tsx --env-file=.env monitor.ts
  5. 5

    Native actions log a [dry-run] would … line instead of running. When those look right, set dryRun: 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 gmail

By 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.

OptionDefaultWhat it does
backfill0Also emits this many of the latest emails on the first check
label"INBOX"Watches another label instead, by its ID
protectColleagues and people you've emailedWho native actions never touch. false protects no one

Actions

ActionWhat 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:

FieldTypeNotes
subjectstring
bodystringThe new text, with quoted replies cut
from, to, cc{ name?, address }
labelsstring[]Gmail label IDs, such as INBOX or IMPORTANT
categorystringGmail's inbox tab: personal, social, promotions, updates or forums
mailingListbooleanSent through a list or bulk sender
attachments{ filename, mimeType, size }[]
protectedBecausestringWhy 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 with protect: false. A skipped action says why, for example colleague 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 as google.app({ scopes: ["gmail"] }).
  • A server with one account of your own. google.fromEnv() builds the connection from GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET and GOOGLE_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.

On this page