JEV FOR INBOX ROUTING

Jev Email Triage

Start with five unsorted emails. One Jev evaluation classifies each message, then application policy decides which ones can be routed automatically and which need review.

01 / LIVE INBOX

Turn five unsorted messages into work queues

Choose a sample inbox, edit any of the five emails, then let Jev route them to work queues.

No email account connected. Sample inbox only.
Inbox preset Presets replace the setup locally and never call Jev.
Routing setup · 6 queues

Names are presentation. Stable internal IDs map Jev choices back to your queues.

Queues: Billing · Technical · Sales · Account · General · Spam / Archive

Editable simulated emails
INBOX · 5 MESSAGES

One request · one Choice question per email

ROUTED WORKTypeSafe Jev

Your routed inbox will appear here.

Create a setup or use a preset, then run Jev.

05 / FROM INBOX TO QUEUES

Jev decides. Application software acts.

Incoming emailSimulated message
Jev chooses a queueBounded Choice
ProbabilityReturned distribution, when available
Application thresholdLocal demo policy
Auto Route or Manual ReviewApplication placement
JEV DECIDES

Bounded classification

Which category? How urgent? Does it need attention? Jev returns typed judgments and probabilities.

APPLICATION DECIDES

Mailbox and workflow actions

Move? Archive? Create a ticket? Notify someone? Ask a human? A Jev probability is not authorization to delete, archive, or send.

This Jev Playground demo performs no external action. Jevmail's current Gmail scope deliberately does not authorize Gmail write actions. A different production application may add them only through separate permissions and deterministic policy.

Advanced

06 / WHY PROBABILITY MATTERS

Uncertainty becomes visible work

A selected queue is useful, but an application also needs a policy for confidence. This demo auto-routes only when the returned selected probability meets the adjustable threshold. A lower value or missing distribution goes to Manual Review.

AUTO ROUTE

Selected probability meets policy

The email appears under its chosen operational queue. This is still a model estimate, not guaranteed correctness.

MANUAL REVIEW

Uncertain is a valid destination

The reviewer sees Jev's selected queue, its probability, and the runner-up when available. Review is deliberate workflow handling, not a failed request.

Implementation

02 / REAL GMAIL

Want to triage a real Gmail inbox?

Jev Playground deliberately does not request access to your inbox. If you want to run this pattern against real Gmail data, an open-source project called Jevmail implements a local, read-only Gmail workflow.

OPEN SOURCE · LOCAL · READ-ONLY GMAIL

Jevmail

Best for trying Jev classification on your own Gmail account without giving the app permission to send, archive, label, or delete messages.

  • Local Next.js application with Google OAuth and the Gmail API
  • The current project requests only gmail.readonly
  • Messages, classifications, and corrections use local SQLite
  • Corrections are stored separately from the original Jev answer
  • Jev classification runs through the configured provider
  • No Gmail write operations are authorized by the current scope

Demo here or real Gmail?

CapabilityJev Playground Email TriageJevmail
InboxSimulated / user-editedReal Gmail
OAuthNoneGoogle OAuth
Gmail permissionNonegmail.readonly
Jev callRealReal
Sends emailNoNo under current scope
Modifies GmailNoNo under current scope
Local mailbox cacheNoSQLite in local app
Best forDesigning routing policyTesting on a real Gmail inbox

03 / RUN JEVMAIL LOCALLY

Run the real Gmail version locally

These are Jevmail requirements, not requirements for using the Jev Playground demo: Node.js 22.18+, pnpm, a Google account, a Google Cloud project with a Web OAuth client, and Vercel AI Gateway access.

Source checked at f6f20af. That tracked tree does not contain .env.example, so the verified steps below create .env.local directly.

GOOGLE OAUTH SETUP

Configure a personal Testing project

  1. Create a Google Cloud project.
  2. Enable the Gmail API.
  3. Configure the OAuth consent screen as External → Testing.
  4. Add your Gmail address under Test users.
  5. Create a Web application OAuth client.
  6. Add the exact redirect URI below.
http://localhost:3000/api/auth/callback/google

Always compare these steps with the current upstream README because OAuth configuration can change.

1 / CLONE AND INSTALL

Install the checked source

git clone https://github.com/fazlerocks/jevmail
cd jevmail
git checkout f6f20af9c2805efd924c29cbea59547e72c558fc
pnpm install

Installation does not require Gmail access or a Jev call.

2 / CREATE AND FILL CONFIG

Create .env.local without overwriting it

Generate a local Auth.js secret, then copy its output into the AUTH_SECRET placeholder:

openssl rand -base64 32
if [ -e .env.local ]; then
  printf '%s\n' '.env.local already exists; edit it instead.'
else
  cat > .env.local <<'EOF'
AUTH_SECRET=<paste output from openssl rand -base64 32>
AUTH_GOOGLE_ID=<Google OAuth client ID>
AUTH_GOOGLE_SECRET=<Google OAuth client secret>
AI_GATEWAY_API_KEY=<Vercel AI Gateway API key>
EOF
fi

The command keeps an existing .env.local unchanged. Replace all four placeholders locally. Do not print, commit, or share the resulting credentials.

3 / START

Start only after configuration is complete

pnpm dev

Open http://localhost:3000. Reaching the login page confirms the local app started; it does not prove Google OAuth or real email classification has been completed.

Google's External Testing publishing status uses short-lived test refresh-token behavior; Google currently documents a seven-day expiry when broader scopes such as Gmail access are requested. Expect periodic re-authentication and check Google's current OAuth documentation before production use.

04 / PERMISSIONS & DATA FLOW

What can the Gmail connection actually do?

The current Jevmail project requests only https://www.googleapis.com/auth/gmail.readonly. This is an OAuth permission boundary enforced by Google for the granted token.

CAN READ

Data available under the scope

  • Email messages and settings covered by gmail.readonly
  • Message, thread, history, and profile data used by the app
  • Inbox content needed for local display and classification
CANNOT THROUGH THIS SCOPE

Gmail write actions

  • Send or compose mail as the user
  • Add, edit, or remove labels
  • Archive by changing labels
  • Mark messages read or unread through modify operations
  • Delete messages
GmailSource mailbox
↓ gmail.readonly
Local Jevmail appOAuth tokens, SQLite, corrections, app state
↓ selected classification text
Jev providerSender + subject + selected headers + trimmed body
↓ typed answers
Local SQLite / UITray + urgency + personalness + probabilities

Read-only Gmail does not mean the email content stays local. Classification requires sending the selected email text to the configured Jev provider. OAuth credentials and tokens, local SQLite data, corrections, and application state stay with the locally running app under the current architecture.

07 / EMAIL TRIAGE ARCHITECTURES

Batch a simulated inbox or classify real mail per message

JEV PLAYGROUND DEMO

Batch five decisions

multiple emails + shared queue definitions↓one Jev evaluation · one Choice per email↓local review threshold
JEVMAIL-STYLE APP

Classify each fetched message

Gmail fetch → one message state↓tray + urgency + personalness↓store probabilities and corrections

Neither structure is universally better. They illustrate different batching and application designs. The Jev overview explains the typed primitives behind both.

08 / BUILD THE CLASSIFICATION LAYER

One State, five Choice questions, local policy

This original TypeScript example mirrors the Playground workflow: assemble a bounded queue catalog and five user-edited emails, call typesafe-ai/jev once, validate every answer, then apply an application-owned threshold. It contains no Gmail or external mail API code.

email-triage.ts
import { createGateway, experimental_evaluate as evaluate } from 'ai';

const gateway = createGateway({ apiKey: process.env.AI_GATEWAY_API_KEY });
const QUEUES = {
  billing: 'Payments, invoices, refunds, and subscription billing.',
  technical: 'Product faults, errors, broken features, and technical failures.',
  sales: 'Pre-purchase pricing, demos, procurement, and commercial evaluation.',
  account: 'Identity, ownership, access administration, and account recovery.',
  general: 'Legitimate operational mail that fits no other queue.',
  spam_archive: 'Scams, unsolicited promotions, bulk mail, or no-action newsletters.',
};

export async function triageInbox(emails: Array<{
  id: string; from: string; subject: string; body: string;
}>) {
  if (emails.length !== 5) throw new Error('This workflow requires five emails');

  const state = [
    'TASK',
    'Route each email to exactly one operational queue.',
    '',
    'QUEUE DEFINITIONS',
    ...Object.entries(QUEUES).map(([id, definition]) => `${id}: ${definition}`),
    '',
    'INCOMING EMAILS',
    ...emails.flatMap((email) => [
      '', `EMAIL ${email.id}`, `FROM: ${email.from}`,
      `SUBJECT: ${email.subject}`, 'BODY:', email.body,
    ]),
  ].join('\n');

  if (state.length > 8_000) throw new Error('Inbox is too large');

  const questions = Object.fromEntries(emails.map((email, index) => [
    `email_${index + 1}`,
    {
      type: 'choice' as const,
      instructions: `Which work queue should email ${email.id} be routed to?`,
      criteria: QUEUES,
    },
  ]));

  const result = await evaluate({
    model: gateway.evaluationModel('typesafe-ai/jev'),
    state,
    questions,
    maxRetries: 0,
    abortSignal: AbortSignal.timeout(30_000),
    providerOptions: { gateway: { zeroDataRetention: true } },
  });

  const AUTO_ROUTE_THRESHOLD = 0.8; // Application policy.
  return emails.map((email, index) => {
    const answer = result.answers[`email_${index + 1}`];
    if (!answer || answer.type !== 'choice' || !(answer.choice in QUEUES)) {
      throw new Error(`Invalid routing answer for ${email.id}`);
    }
    const probability = answer.probabilities?.[answer.choice];
    return {
      email,
      queue: answer.choice,
      probabilities: answer.probabilities,
      status: probability !== undefined && probability >= AUTO_ROUTE_THRESHOLD
        ? 'AUTO_ROUTE'
        : 'MANUAL_REVIEW',
    };
  });
}

For server-side credentials, public errors, and provider setup, continue to the Jev API guide.

10 / OTHER IMPLEMENTATION PATTERNS

Other ways to build email triage

MOCK / WORKFLOW ORCHESTRATION

typesafe-jev-workflow

Useful for studying Choice routing and workflow handlers without a real mailbox integration.

Inspect the repository ↗
GENERIC BATCH CLASSIFICATION

jev-mcp · jev_classify

Useful when emails already exist in application code and only need bounded classification against a shared catalog.

Inspect jev-mcp ↗

This demo uses editable sample mail and never connects to Gmail or changes a real mailbox.

Limits & privacy

09 / PRIVACY & PRODUCTION BOUNDARIES

Before connecting a real inbox

SENSITIVE DATA

Read-only is still powerful

Mailbox access exposes private correspondence. Request the least privilege needed and send only the minimum fields required for classification.

LOCAL SECRETS

Keep credentials server-side

OAuth client secrets, refresh tokens, and Jev or Gateway keys belong outside client bundles, logs, and source control.

RESTRICTED SCOPE

Personal testing and public products differ

Google classifies gmail.readonly as Restricted. A public product can require OAuth verification and, when restricted-scope data is stored on or transmitted through servers, a security assessment.

CONSEQUENTIAL ROUTING

Probability is not permission

Do not let a high probability trigger irreversible external actions without tested deterministic policy, authorization checks, and appropriate review.

Jevmail is an independent side project. It is not affiliated with Jev Playground, TypeSafe AI, Vercel, Google, or their products. Follow its source and Google's current requirements before using real mailbox data.

Sources

11 / SOURCES

Primary references for Gmail access and Jev classification

Verified on .