Connect your inbox, let AI triage it, and act on what matters.
The Email Intelligence Plugin connects Hrida AI Studio to your IMAP/SMTP inbox. Once configured, AI reads your incoming mail, scores each message for urgency, tags it by topic, flags spam, suggests a draft reply, and links meeting invites to your CalDAV calendar — all without leaving Hrida AI Studio.
The Email workspace is only visible when the admin has enabled the Email Intelligence Plugin under Admin > Settings > Email > Install for All Users. See Admin Setup below.
What the plugin does
| Capability | Description |
|---|---|
| Urgency scoring | Each email is rated 1–5 for how urgently it needs attention |
| Auto-tagging | Topics like billing, legal, internal, urgent are detected and attached as tags |
| Spam detection | A spam_score (0.0–1.0) is assigned; emails above the admin-configured threshold are automatically moved to Spam |
| Draft replies | AI generates a ready-to-send reply draft for each triaged message |
| CalDAV awareness | Meeting invites found in email are cross-referenced with your calendar to surface scheduling conflicts |
| Chat assistant | The Email Triage Assistant pipe lets you converse with your inbox in natural language |
Admin Setup
1. Install the plugins
Go to Admin > Settings > Email and click Install for All Users.
This single action registers two plugins server-side:
| Plugin | Type | ID |
|---|---|---|
| Email Manager | Tool | email_manager |
| Email Triage Assistant | Pipe (function) | email_triage_assistant |
The install is idempotent — clicking again after a server update reinstalls both plugins with the latest source.
2. Tool and pipe valves are set automatically
The Hrida API URL and API Key fields you fill in on the same Admin > Settings > Email screen before clicking Install for All Users are written straight into both plugins' HRIDA_API_URL / HRIDA_API_KEY valves server-side — there's no separate manual valve-configuration step. HRIDA_API_URL defaults to the container's own internal address (override via the EMAIL_PLUGIN_SELF_URL env var if yours differs; do not point it at the browser-facing port, e.g. not 3000). HRIDA_API_KEY is optional for the pipe — it falls back to the signed-in user's own session token, so leave it blank unless you need headless/API access.
If you want to double-check or hand-edit them afterward, they're visible under Admin > Workspace > Tools > Email Manager and Admin > Workspace > Functions > Email Triage Assistant (gear icon on each).
3. Email Triage Assistant pipe settings (optional)
The pipe also exposes a few tunables (gear icon on Email Triage Assistant under Admin > Workspace > Functions) — all have sane defaults, so this step is optional:
| Valve | Default | Notes |
|---|---|---|
MODEL_ID | (empty) | Model that writes the replies. Empty uses the Task Model, then the first default model, then the first available model |
TRIAGE_SYSTEM_PROMPT | Built-in email-assistant system prompt | Customize the assistant's persona/instructions |
AUTO_TRIAGE_ON_LOAD | true | Include the email digest in the context of every reply |
AUTO_TRIAGE_LIMIT | 50 | Max cached triage entries pulled into the digest |
Per-user, the pipe only has one setting — User Valves > account_id — covered in Linking your account below.
4. Set admin-level email settings
Go to Admin > Settings > Email:
| Setting | Default | Description |
|---|---|---|
| Max Emails per Triage Run | 50 | Prevents runaway IMAP polling. Max: 500 |
| Spam Auto-flag Threshold | 0.75 | Emails with spam_score ≥ this are moved to Spam |
| Triage Model Override | (blank) | Model ID for the background sync triage. Leave blank to use the instance default. |
Docker Compose environment variables
# docker-compose.yaml → hrida-ai-studio service
# Background inbox auto-sync
- EMAIL_SYNC_INTERVAL=900 # seconds between automatic syncs (default: 15 min)
- EMAIL_RETENTION_DAYS=90 # how long triage results are kept
# Internal URL for the email pipe (only needed if your internal port differs from 8080)
# - EMAIL_PLUGIN_SELF_URL=http://localhost:8080SMTP_HOST / SMTP_USER / SMTP_PASSWORD (if set on your instance) configure the app's own outgoing system email — password resets, demo request notifications, etc. They have nothing to do with this feature. Each user's own inbox IMAP/SMTP credentials are entered per-account in Dashboard > Email (next section) and stored encrypted in the database, not as environment variables.
Setting up your email account
Navigate to Dashboard > Email and click + Add Account.
| Field | Description |
|---|---|
| Label | Friendly name for this account (e.g. "Work Gmail") |
| IMAP host | IMAP server address (e.g. imap.gmail.com) |
| IMAP port | Default: 993 (SSL) |
| SMTP host | SMTP server address (e.g. smtp.gmail.com) |
| SMTP port | Default: 587 (STARTTLS). Use 465 for SMTP over SSL |
| Username | Your email address |
| Password | App password or account password |
| Use SSL | Enable for IMAP port 993; leave off for STARTTLS configurations |
| CalDAV URL | (Optional) CalDAV server URL for calendar-aware scheduling |
| CalDAV username / password | (Optional) CalDAV credentials (stored separately from email credentials) |
Credentials are encrypted at rest using Fernet symmetric encryption keyed to the instance HRIDAAI_SECRET_KEY. Passwords are never returned in API responses or logs.
After saving, click Test Connection to verify IMAP and SMTP connectivity before running a triage.
Gmail requires an App Password (not your account password) when IMAP access is enabled. Generate one at Google Account > Security > 2-Step Verification > App passwords.
Linking your account to the Email Triage Assistant
The pipe needs to know which email account belongs to you. After adding your account:
- Go to Dashboard > Email, click your account, and copy the Account ID.
- Open a new chat and select Email Triage Assistant as the model.
- Open its Chat Controls panel (the sliders/adjustments icon) → Valves, and paste your Account ID into the
account_idfield under User Valves.
Using the Email Triage Assistant
With your account ID set, a digest of recently triaged emails (from the local triage cache) is injected into the assistant's context on every reply (controlled by the AUTO_TRIAGE_ON_LOAD / AUTO_TRIAGE_LIMIT valves above). From there you can ask things like:
- "Which emails are most urgent today?"
- "Is there anything from the finance team I should respond to?"
- "Triage my inbox" — explicitly re-runs triage mid-conversation
The injected digest looks like:
📬 **Email Digest** — 2026-08-14 09:30 UTC
Account: Work <you@example.com>
🔴 **CRITICAL** (1 email)
• **Budget approval needed** — finance@company.com [billing, urgent]
🟠 **URGENT** (2 emails)
• **Server outage — action required** — ops@company.com [infra]
• **Contract expires Friday** — legal@company.com [legal]
🚫 **Auto-filtered spam**: 3 emails
The model then answers based on this real-time inbox snapshot.
How triage works
Triage is triggered manually from the Email workspace, or automatically via the background scheduler:
- Fetch — the plugin connects via IMAP and fetches up to N recent messages (admin-configurable; default: 50).
- Analyse — each message is sent to the configured triage model. The model returns a structured triage result:
urgency(1–5)tags(list of topic strings)spam_score(0.0–1.0)summary(one-paragraph summary)draft_reply(ready-to-send response)
- Cache — results are stored in the triage cache (
email_triage_cachetable). Re-triaging the same message UID updates the cached result. - Spam filter — messages with
spam_score ≥ threshold(admin default:0.75) are moved to the Spam folder via IMAP.
Browsing the triage results
The Email workspace shows your triaged messages. Use the filter controls to narrow by:
| Filter | Values |
|---|---|
| Urgency | Minimum urgency level (1–5) |
| Tag | Filter to messages with a specific tag |
Each triaged message card shows:
- Subject, sender, received timestamp
- Urgency badge (color-coded: 1=grey, 3=yellow, 5=red)
- Tags
- AI summary
- Draft reply (expandable)
- Spam score
Click a message to open the full detail view where you can copy or edit the draft reply before sending.
Scheduled triage (background sync)
You don't need to set anything up for this — every active email account is synced and re-triaged automatically in the background on a fixed interval, no Automation needed:
| Env var | Default | Description |
|---|---|---|
EMAIL_SYNC_INTERVAL | 900 (15 min) | Seconds between automatic background syncs |
EMAIL_RETENTION_DAYS | 90 | How long triage results are kept before being pruned |
These are plain environment variables (not admin-UI editable) — changing them requires a container restart. Use the manual ↻ Sync button in Dashboard > Email, or POST /api/v1/email/accounts/{id}/sync, to trigger a triage run immediately instead of waiting for the next scheduled interval.
Security
- Fernet encryption: All email and CalDAV credentials are encrypted using a key derived from
HRIDAAI_SECRET_KEY. If the secret key changes, existing credentials cannot be decrypted. - Owner-scoped access: Each user can only see and manage their own email accounts. Admins can access any account.
- Passwords never returned: The account API never includes plaintext passwords in responses. A separate internal endpoint decrypts on the fly for plugin use only and is only called server-side.
- Session token precedence: The Email Triage Pipe uses your signed-in session token by default. The
HRIDA_API_KEYvalve is only a fallback for headless API usage. - Spam scores are advisory: The plugin moves messages to the Spam IMAP folder but does not delete them. You can always recover messages from Spam manually.
Troubleshooting
"Pipe load failed: unterminated string literal"
The pipe source was stored with a corrupt escape sequence. Re-run the install endpoint:
Admin > Settings > Email > Install for All Users
This re-installs the latest pipe source and clears the cached module.
"Configuration error: Could not auto-detect model"
The pipe's HRIDA_API_URL is pointing to the wrong address. Common causes:
| Symptom | Cause | Fix |
|---|---|---|
Max retries exceeded ... port=3000 | Valve set to browser-facing URL | Set HRIDA_API_URL to http://localhost:8080 |
Read timed out | Sync HTTP call deadlocking event loop | Upgrade to the latest server image — the fix is in functions.py (asyncio.to_thread) |
Connection refused | Container port mismatch | Check the PORT env var; default is 8080. Set EMAIL_PLUGIN_SELF_URL if needed |
The HRIDA_API_URL must be the container's own internal port, not the port your browser uses. If you publish port 3000:8080 in Docker, the internal URL is still http://localhost:8080.
Empty response (blank chat bubble)
This happens when the pipe returns raw JSON instead of text. Cause: the pipe was returning a dict (pass-through body). The fix is in the server — make sure you are running version 1.5.0+ of the pipe (visible in Admin > Workspace > Functions > Email Triage Assistant). Re-install if needed.
"LLM error 400: Bad Request"
The pipe was sending framework-internal fields (metadata, tool_ids, session_id) that your backend rejected. Fixed in pipe version 1.5.0 — the pipe now builds a clean OpenAI-compatible body before forwarding. Re-install the plugin.
No emails appearing after sync
- Confirm the email account credentials are correct (Dashboard > Email > Test Connection).
- Run a manual sync: click Sync on your account, or call
POST /api/v1/email/accounts/{id}/sync. - Check that the Email Triage Assistant has your
account_idset in User Valves. - Ensure the triage model is available: go to Workspace > Models and confirm at least one model is loaded.
Email account returns "Failed to decrypt credentials"
The HRIDAAI_SECRET_KEY environment variable changed between the time the account was saved and now. Credentials are encrypted with this key — changing it makes existing accounts unreadable. Re-add the account after restoring the original key, or keep the key stable across restarts (set it explicitly rather than letting it auto-generate).
API reference
All email endpoints are under /api/v1/email.
# Account management
GET /api/v1/email/accounts
POST /api/v1/email/accounts
GET /api/v1/email/accounts/{id}
PATCH /api/v1/email/accounts/{id}
DELETE /api/v1/email/accounts/{id}
# Connection test
POST /api/v1/email/accounts/{id}/test
# Returns: {"imap": true, "smtp": true, "errors": {}}
# Manual IMAP sync + triage
POST /api/v1/email/accounts/{id}/sync
# Body: {"limit": 50, "folder": "INBOX", "only_unseen": true, "mark_as_read": true}
# Returns: {"synced": N, "fetched": N}
# Triage cache
GET /api/v1/email/accounts/{id}/triage ?urgency_min=1&tag=&skip=0&limit=50
POST /api/v1/email/accounts/{id}/triage # upsert a triage result (plugin use)
DELETE /api/v1/email/accounts/{id}/triage/{message_uid}
DELETE /api/v1/email/accounts/{id}/triage # clear all cached results for account
# Thread grouping
GET /api/v1/email/accounts/{id}/threads ?limit=200
# Draft management
GET /api/v1/email/drafts
POST /api/v1/email/drafts
PATCH /api/v1/email/drafts/{draft_id}
DELETE /api/v1/email/drafts/{draft_id}
# Plugin install (admin only)
POST /api/v1/email/plugin/install
# Body: {"hrida_api_key": "<service-account-key>"}
# Returns: {"tool": "created|updated", "function": "created|updated"}