Features

Everything Email Triage does — from LLM classification to calendar-aware meeting scheduling to the agent API.

Section 1

Intelligent Classification

Each incoming message is classified by a local LLM into one of your categories. The classifier sees the sender, subject, and body; you write the category descriptions and the system uses them as the classifier's instruction.

Default Categories

12 system categories ship out of the box:

CategoryDescription
to-respondEmails that need a reply from you
action-requiredTasks, requests, deadlines
fyiInformational. No action needed.
newslettersSubscriptions and recurring content
meetingsMeeting invites, agenda, scheduling
meeting-requestProse-only requests to schedule a meeting (no .ics)
grant-relatedGrant applications, reviews, funding communications
self-eventNote-to-self about a personal event. Auto-creates calendar entry.
… plus invoices, comments, notifications, sponsor

Custom Categories

Add as many as you want. Each category is defined by a short slug and a description; the description is what the LLM uses to decide whether a message belongs. Write it well, the classifier follows.

Categories admin page

/categories — system defaults + user-added categories with operator-editable descriptions

Classification Hints

Two types of fast-path rules bypass the LLM entirely:

Rules editor

/rules — mailing-list, sender, and regex rules with 30-day hit counts

Caching

Two-level Redis cache keyed by (sender, normalized_subject, body_hash). Repeat emails skip the LLM call. Cache is disabled by default for HIPAA-flagged accounts — re-classification cost buys audit posture.

Section 2

Automated Routing & Actions

After classification, messages route through configurable action chains. Actions are independent — any number can fire on a single message.

Available Actions

ActionWhat It Does
moveMove to a different folder. Per-category folder mapping.
labelApply the category as a label / Gmail label.
notifySlack, SMS-via-gateway, custom webhook.
draft_replyGenerate a contextual draft reply, save to Drafts folder.
suggest_meeting_timesCalendar-aware draft listing free slots. Auto-fires on meeting-request.
accept_invite / decline / tentativeCalendar invite handling with iMIP-threaded replies.
escalateForward to a configurable recipient. SMS-gateway pattern for phone delivery.
muteSuppress all classification + action firing.
Routes editor

/accounts/<id>/routes — per-category action table. Different accounts can route the same category differently.

Watch Rules

Independent of category. Match a sender, subject, body pattern → fire escalate / webhook / mute. Useful for "alert me immediately when X arrives" patterns that don't fit a category.

Section 3

Digests & Periodic Summaries

Two digest features that solve the two recurring inbox problems every professional has: "I subscribe to too many newsletters to read" and "I've handed my inbox to an AI and now have no idea what it's doing."

Newsletter Digest — for the high-subscription-volume professional

Target user: the knowledge-work professional whose job requires staying current on a high-volume subscription feed. The research clinician subscribed to journal alerts + NIH funding announcements + IRB digests + society newsletters + sponsor communications. The financial analyst tracking 12 industry briefings. The consultant signed up for every category-defining substack in their vertical.

For someone like this, newsletter overload is a 60-to-90-minute-per-day problem. Each newsletter has 2–4 articles worth a 30-second scan; only 5–10% of those articles deserve a real read. The current state-of-the-art is Cmd+A → Archive — losing the signal entirely.

The newsletter digest replaces that with one inbox entry at a scheduled time (typically 7 AM local, operator-configurable):

  1. The digest action pulls every newsletter-category message from the lookback window (default 24 hours)
  2. The local LLM reads each newsletter body and extracts per-article structure: headline, 1–2 sentence summary, "read more" link
  3. Articles are grouped by source so each newsletter's content clusters together
  4. An HTML digest renders with section headings per newsletter, article cards beneath, and "Read more" buttons linking back to the original article URLs
  5. The digest delivers to the account's own inbox (or sends, per operator policy)

Concrete outcome. A research clinician with 30 newsletter subscriptions averaging 3 articles each goes from "120 inbox entries the system can't help me with" to "one digest with 90 article cards I can scan in 5 minutes." The 5–10 articles worth a full read get a click-through; the other 80 are dismissed by closing the digest. Daily time saved: 60–90 minutes.

Configuration (per-account, on the Digests tab)

Each schedule slot has an independent enable/disable toggle and a "Run now" button for previewing without waiting 24 hours.

Digest schedule editor

/accounts/<id>/edit on the Digests tab — Daily Activity, custom digests (Morning Newsletter, Weekly Wrap), per-slot Preview and Send-test-now buttons

What a rendered newsletter digest looks like

The HTML body of a generated newsletter digest as it arrives in your inbox — section headings per source, article cards with headlines, 1–2 sentence summaries, and "Read more" links back to the original article URLs. The visual "30 emails → 1 digest" outcome:

Rendered newsletter digest

A daily 7 AM newsletter digest — 12 articles across 5 sources (NIH funding, NEJM, AHA, STAT News, JAMA), each with a headline + summary + "Read more" link

Triage-Activity Digest — for the "I want to know what the AI is doing" user

The "set it and forget it" anti-pattern is real: an AI quietly classifies + drafts + moves mail for weeks, the operator's confidence drifts toward "I have no idea what it's doing," trust erodes, eventually the operator pulls the plug. The triage-activity digest is the antidote — every day, the operator gets a complete record of what email-triage did with their mail.

Daily summary delivered to the account's own mailbox at the scheduled time (typically end-of-day, 6 PM local — operator-configurable):

The system never feels like it's acting on your behalf without your knowledge. Every classification, every action, every draft is in the digest. For HIPAA-regulated environments this doubles as a lightweight audit surface — the compliance officer reviews the operator's last 5 daily digests instead of querying the access log.

Compliance posture, locked in code

Delivery mechanics

Section 4

Smart Reply Drafting

The draft_reply action generates a contextual response using your local language model. The draft lands in your Drafts folder. Nothing is ever sent automatically.

Three Signals Per Draft

The Incoming Message

Sender, subject, body — the obvious context.

Your Writing Style

Distilled from your sent mail. Tone, greeting style, signoff conventions, common phrases, average reply length. Default window: last 50 messages, operator-adjustable up to 250+.

Recent Similar Replies

Per-account vector index of past sent mail. Few-shot examples shown to the LLM. Edits you make to drafts are weighted 1.3× over ordinary sent mail — the system learns your corrections.

Drafted reply

A drafted reply in the email client's Drafts folder — threaded into the original conversation, ready for review

Distilled Writing-Style Profile

The system extracts measurable signals from your sent mail and shows you what it learned. Greeting patterns, signoff conventions, average length, common phrases — all visible and verifiable, not magic.

Writing-style profile

/profile/style-data — the distilled writing-style profile. Edits you make to drafts feed back into this index.

Multi-Alias Awareness

If your account sends from multiple addresses (work, personal, project-specific), the system maintains a separate writing-style profile per address. A draft to a message addressed to alias-a@ reflects how you write as alias-a.

Captured Feedback Loop

When you edit a draft before sending, the final version is captured as a "high-signal example." Future drafts learn the way you actually want it to write — not the way it guessed.

Privacy posture. All drafting runs on the local LLM. The incoming message, the style examples, and the generated draft never leave your network. HIPAA-flagged accounts apply the same scrubbing rules to drafted body as to classification reasoning.

Section 5

Calendar-Aware Meeting Scheduling

The highest-leverage feature for anyone whose calendar is the bottleneck. When mail is classified as meeting-request, the system reads your calendar, picks slots, and drafts a calendar-aware reply.

Meeting preferences page

/profile?tab=meeting — slot configuration, working-hours grid, OOO override

The Intercept

  1. Reads your calendar over the next 14 days
  2. Filters to your configured working hours (per-weekday, with lunch-break carve-outs)
  3. Skips events marked "free" (birthdays, reminders don't blank your day)
  4. Picks N suggestions spread across M distinct days
  5. Drafts a reply listing slots in your local time zone with DST-correct labels
  6. Saves to Drafts for one-click edit-and-send
Meeting suggestion draft

A generated meeting-suggestion draft — N slots across M days with proper EST/EDT labels

Invite Acceptance

When a .ics-attached invite arrives, the accept_invite / decline_invite / tentative_invite actions respond on your calendar and draft an iMIP-formatted reply, properly threaded back to the organizer.

Calendar Surrogates (IMAP)

Pure-IMAP servers don't expose a calendar API. The system supports routing calendar operations for an IMAP account through a sibling Gmail / O365 account. Operator sets the surrogate via dropdown.

Section 6

Email Provider Support

ProviderPushLabels/FoldersCalendar API
GmailCloud Pub/Sub (seconds)Native labelsGoogle Calendar API
Office 365Graph subscription (seconds)Native foldersMicrosoft Graph
IMAPIDLE (<2 sec, multi-folder)SPECIAL-USE detectedvia surrogate Gmail/O365

Supports Dovecot, Cyrus, Exchange-via-IMAP, Fastmail, Proton Bridge. TLS by default (port 993). Multi-folder watch per account with per-folder route overrides.

Multi-account list

/accounts — multi-account view with real-time push status and per-account health indicators

Per-Account Configuration

Each account has its own tabbed configuration page covering provider credentials, push/poll cadence, watched folders, calendar role assignment, delegates, and integration endpoints. Every knob is in the UI — no config-file editing required.

Account edit page

/accounts/<id>/edit — provider credentials, aliases, time zone, push/poll, watch folders

Section 7

Multi-User & Multi-Account

One install supports any number of users, each with their own accounts, categories, style profile, and meeting preferences. Three role tiers: Admin (install-wide config), User (owns own accounts), Delegate (granted view/triage/draft on another user's account).

Per-user isolation is enforced at the data layer. Delegate actions are audited with both actor and account-owner stamped. The HIPAA §164.312(b) audit gate distinguishes owner self-access from delegate access — the former is a §164.502(a) self-disclosure carve-out and isn't audited as PHI access; the latter writes an audit row every time.

Users and delegate admin

/users — user management, role assignment, and delegate grants visible on the same surface

Section 8

Operations & Configuration

Every operational signal is exposed through the admin UI — classifier latency, cache hit ratio, embedding metrics, supervised-task state, push watcher health, ingestion rollups. JSON for machine consumption (Nagios, Datadog, Prometheus); HTML for the admin stats page.

Admin operational stats

/admin/stats — operational signals dashboard showing the system is healthy at a glance

Install-Wide Configuration

Tabbed configuration page covers ingestion, classifier model, integrations (Redis cache, webhooks), AI backends, TLS / security, and BAA acknowledgments. Operator-controlled, audit-logged, no config-file editing.

Install-wide config

/config — install-wide configuration tabs

Container Image & Lazy Embedding Stack

The runtime container image is ~250 MB compressed, Debian-13-slim with Python 3.12, non-root user, signed with cosign and SLSA-3 provenance. CPU-only PyTorch wheels (the GPU is reserved for the Ollama chat-model host). Fast pulls, fast CI, fits under GitHub Releases' 2 GiB asset cap for air-gap tarballs.

The embedding backend (PyTorch CPU runtime, sentence-transformers, all-MiniLM-L6-v2 model — ~600 MB total) is not baked into the image. On first admin setup, the operator clicks [Install now] on the AI Backends config page; the installer fetches each manifest-listed file over HTTPS, verifies SHA-256 against a pinned manifest baked into the image, and pip installs into the persistent volume at /app/data/runtime-deps/.

Operators who choose Ollama for embedding (rather than the in-process CPU backend) skip the local install entirely; their embed calls go to the same Ollama host as the chat model.

Air-Gap Installation

For organizations on disconnected networks — classified environments, OT segments, regulated research enclaves — air-gap installs use scripts/download-embedding-bits.sh on a connected machine to produce a hash-pinned tarball + SHA-256 sidecar. Transfer the tarball to the air-gap host and sideload through the admin UI. Sideload runs the same hash verification as the auto-download path — operator-staged bytes are not trusted; a bit-corrupted tarball or a tampered air-gap drop trips the same refusal as a poisoned PyPI mirror.

Image and source. Container: ghcr.io/unlimited-data-works-llc/email-triage. Source: github.com/Unlimited-Data-Works-LLC/Email-Triage (Apache 2.0). Verification recipe in the repo's docs/install.md.

Want this in your stack?

A 30-minute call to scope deployment, integration, and tier fit.

Schedule a Demo