Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Set up Email Inbound (receive email into Claude sessions)

Omniscio can receive email at a dedicated inbox, run each message through a safety classifier, and either start a new Claude session for it or continue an existing email thread — with the agent's replies going back out as email on the same thread. Setting it up needs an AgentMail inbox, an API key stored in your OS credential store, and the inbox address pasted into Omniscio.

What it is

Omniscio can receive email at a dedicated inbox, run each message through a safety classifier, and either spawn a new Claude session for it or continue an existing email-thread session. Replies the agent generates go back out as email on the same thread. This is the inbound side of email — distinct from Email Summarizer (forwarded-newsletter digests) and from Gmail integration (triaging your existing Gmail inbox inside Omniscio).

The inbox lives at AgentMail, a third-party email-for-agents service. Omniscio polls it every 30 seconds via the AgentMail HTTP API, so Omniscio must be running for new email to land in a session. The polling cadence, concurrency cap (max 10 simultaneous sessions), and new-thread rate limit (max 100 brand-new threads per rolling hour) are all hardcoded — no configuration needed. An email that arrives while the hourly limit is full is not dropped: it waits and is picked up on a later check once the hour frees up.

Three things have to be in place before email starts flowing:

  1. An AgentMail account + inbox (created at agentmail.to, outside Omniscio).
  2. An AgentMail API key stored in your OS credential store (Windows Credential Manager / macOS Keychain / AGENTMAIL_API_KEY env var on Linux). Omniscio has no UI for this — you provision it once with a one-line command.
  3. The inbox address pasted into Omniscio at Settings → Email Inbound, plus optional sender + token gating.

The split exists because the API key authenticates Omniscio to AgentMail (so Omniscio can poll your messages) while the inbox address tells Omniscio which mailbox to watch. Storing the key in the OS credential store keeps it DPAPI-encrypted at rest and out of config.json, so it never lands in a backup or sync target by accident.

Where to find it

How to use it

1. Create an AgentMail inbox

Sign up at agentmail.to and create an inbox. The address looks like your-name@agentmail.to. AgentMail has a free tier that's enough to evaluate the feature. Copy the API key from your AgentMail dashboard — it starts with am_us_.... You'll paste it into the OS credential store in the next step, never into Omniscio's settings.

2. Store the API key in your OS credential store

This is a one-time provisioning step and Omniscio has no UI for it. Pick the command for your platform:

Windows (PowerShell or Git Bash):

echo "am_us_..." | powershell.exe -NoProfile -File "$HOME/.claude/skills/agentmail/credential.ps1" store

The credential.ps1 script ships with the bundled agentmail skill at ~/.claude/skills/agentmail/credential.ps1. It writes to Windows Credential Manager via direct P/Invoke into advapi32.dll (CredWrite) — no PowerShell modules needed. The credential is DPAPI-encrypted by Windows and keyed to your login. Verify it landed with:

powershell.exe -NoProfile -File "$HOME/.claude/skills/agentmail/credential.ps1" exists

The exists subcommand prints yes or no and never the value. To rotate the key later, re-run store with the new value — it overwrites in place.

macOS:

security add-generic-password -s AgentMail -a api-key -w "am_us_..."

This stores the key in your login Keychain. Verify with security find-generic-password -s AgentMail -a api-key -w (prints the value).

Linux:

There's no credential-store fallback on Linux — set the AGENTMAIL_API_KEY environment variable in whatever environment Omniscio inherits (e.g. ~/.profile, your launcher's Exec= entry, a systemd Environment= directive). Omniscio reads it on startup.

3. Paste the inbox address into Omniscio

  1. Open Settings → Email Inbound (gear icon, or the Settings virtual project in the sidebar).
  2. Flip Email Inbound ON.
  3. Paste your AgentMail inbox address (e.g. your-name@agentmail.to) into the Inbox ID field.
  4. Click Test. Omniscio calls listMessages against AgentMail using your stored API key, which simultaneously proves the key works AND that the inbox exists. A green "Connected successfully" confirms both. A red "Could not reach AgentMail service" means the key is missing, invalid, or doesn't own that inbox — see Troubleshooting below.

4. (Recommended) Restrict who can send

Two filters run in front of the safety classifier — neither is required, but you should configure at least one for any non-trivial use:

  • Approved Senders. Empty list = open intake (anyone who emails the inbox is processed). Add specific addresses to restrict; everyone else is silently dropped before the classifier ever runs.
  • Inbox Secret Token. Click the generate button to create a random token. Approved senders must include [MC:<token>] somewhere in the subject line — emails without it are silently dropped. Without a token, sender-address spoofing is your only barrier; with a token, an attacker has to know both the address you allow AND the token. The token applies to this AgentMail inbox only: mail to your Omniscio agent address is checked by that address's own sender rules instead, and the Gmail bug-report channel never looks at the token.

The setting card warns you in amber if no token is configured.

5. (Optional) Tune the safety classifier

Below the sender filters is a Prescreen Rules textarea — the safety-classifier prompt (run on OpenRouter’s google/gemini-2.5-flash-lite, a fixed model pin, auto-falling back to Claude Haiku 4.5) that decides safe: true / false on every email before any agent sees the body. The textarea is pre-filled with the default CORE rules (block prompt-injection, exfiltration, financial actions, RCE; approve bug fixes, code review, doc edits, etc.). Edit in place; saves on blur. The JSON output contract is appended at runtime and can't be edited away. Full details: email-inbound-prescreen.md.

6. (Optional) Route bug reports to a specific project

If you want testers to email bug reports straight into a project's session, use the slug-routing layer on top of email inbound:

  1. Click the pencil icon on the project row → Edit Project.
  2. Toggle "Auto-triage incoming bug reports" ON.
  3. Set a slug (e.g. amc).

Tester emails with subject [BUG: amc] ... or [FR: amc] ... now spawn a new Claude session directly in that project, with the body fenced as untrusted external data and the agent instructed to investigate read-only and wait for approval before any code change. Full details: bug-report-intake.md.

How it behaves

Attachments

When you forward an email that has attachments, Omniscio tries to deliver every attachment to Claude with the same fidelity as the chat composer — no setup required, no separate toggle. Images arrive as native vision content blocks, PDFs arrive as native document content blocks (also vision-rendered), and text/Office documents land in the session workdir with their paths prepended to the prompt. Anything that can't be delivered (oversized file, format the vision API doesn't accept, download failure) is silently dropped from the payload AND surfaced to Claude in a short "Note: N attachments could not be included:" line at the bottom of the email — so neither the user nor the agent silently loses information.

The three delivery routes match chat-attachments.md one-for-one — same partition logic, same Anthropic API content blocks, same workdir-save mechanics — so anything documented there about how Claude perceives a given file type holds for the email path too.

Attachment type What Claude sees Where the bytes go
Image (PNG / JPEG / GIF / WebP) image content block (base64 inline, vision) Inline only — not saved to workdir
PDF document content block (base64 inline, vision) Inline + saved to <workdir>/.claude/amc-attachments/<sessionId>/ so Claude can also Read / edit
Text doc (.md .txt .csv .json .html .xml .tsv .log .markdown) A line in the prompt prefix: "Please examine the following file(s): <path>" Saved to workdir; only the path is in the prompt
Office doc (.docx .xlsx .pptx + legacy .doc/.xls/.ppt, ODF .odt/.ods/.odp, .epub, .rtf) Markdown extracted in main via the anydoc reader, saved as <base>.md next to the original; that .md path is in the prompt The extracted .md lives in workdir; the original binary is not preserved (different from the chat path, which keeps both as chip metadata)

Caps and the per-attachment ceiling.

  • 30 MB per image / 32 MB per document — the same Anthropic vision API limits the chat composer enforces. Anything bigger is skipped with reason oversize_image or oversize_document.
  • 10 attachments per email — a hard cap to keep a single malformed email (most often an HTML newsletter with dozens of CID-embedded signature graphics, tracking pixels, and themed icons) from blowing up the prompt. When the inbound message exceeds the cap, Omniscio sorts by Content-Disposition first — explicit attachment parts win over inline parts, since the user almost certainly meant to forward the real payload rather than the email's chrome — then keeps the first 10. Everything past the cap is skipped with reason too_many_attachments.
  • Image format whitelist — the Anthropic vision API only accepts PNG, JPEG, GIF, and WebP. HEIC (iPhone default), SVG, BMP, TIFF, and AVIF are dropped at this layer with reason unsupported_image_format rather than rejected by the model after spawn. If you want Claude to look at a HEIC photo, convert it to JPEG or PNG before forwarding.
  • All office formats extracted — legacy binary Office (.doc / .xls / .ppt), OpenDocument (.odt / .ods / .odp), .epub, and .rtf now extract to Markdown via the on-device anydoc reader (the same engine + shared partition the chat path uses), not just the modern .docx / .xlsx / .pptx. A forwarded office file the reader can't parse surfaces as office_extract_failed (next bullet) rather than being dropped.
  • No-text-extracted Office files surface as office_extract_failed — the chip path on chat would keep the binary as a chip, but the email path has nowhere to surface a chip, so the skip note is the only signal.

What the agent sees when something is skipped. The session prompt ends with a literal note listing every skipped attachment, one per line, with the filename and a stable reason token:

Note: 3 attachments could not be included:
- big.png (oversize_image)
- old.doc (office_extract_failed)
- art.heic (unsupported_image_format)

So if a user forwards an email saying "look at the screenshot I'm attaching" and the screenshot was 50 MB, the agent will know to ask the user to re-send a smaller version rather than guess at what was missing.

The full set of skip reason tokens is: too_many_attachments, download_failed, not_carried_by_mail_pipeline, oversize_image, oversize_document, unsupported_image_format, unsupported_type, office_extract_failed, workdir_save_failed.

not_carried_by_mail_pipeline is the one token that is NOT about the sender. Every other reason means we could not use a file that reached us in full, or could not fetch it at all. This one means the file did arrive and its contents did not reach the session — either the pipeline dropped them, or it parked them somewhere this install could not retrieve them from. It is a distinct token, and it renders as a full sentence rather than the token, because an agent shown only download_failed reasonably concludes the sender forgot to attach the file and tells them so: on 2026-09-30 an agent told a customer her log zip "didn't come through" and asked her to send it again, when the pipeline had received it and discarded it. The note for this token says plainly that the sender DID attach it and instructs the agent not to ask for the same file again.

Large attachments are offloaded, not dropped. The parsed message reaches inboundEmail as one payload stored as one Firestore document, whose 1 MiB ceiling is far below a log bundle or a screenshot. The Cloudflare worker inlines only what comfortably fits and parks the rest in the amc-agent-email-attachments R2 bucket, which the desktop then fetches back over attachments.omnisciomail.com and feeds through the normal attachment pipeline — so a large file arrives, is saved to the session workdir or attached as a vision block, exactly like a small one. The object key is derived from data already on the queue row, so this needs no new field on the wire and an older install simply degrades to the token above. A not_carried_by_mail_pipeline skip on a large attachment therefore means the FETCH failed — the worker route was unreachable, the object had expired, or the install predates the feature. See the worker README.

One bad attachment never sinks the email. Each attachment is downloaded and processed independently — a 502 from AgentMail's CDN on attachment #3 surfaces as a single download_failed skip entry, and attachments #1, #2, #4… still flow through normally. The session still spawns; the email body still reaches Claude.

Troubleshooting

Symptom Cause Fix
Test button shows "Could not reach AgentMail service" API key missing or invalid Re-run the credential.ps1 store (or security add-generic-password) command in step 2. Confirm with the exists command.
Test button shows "401 / 403" Key is valid but doesn't own that inbox The key and inbox must be from the same AgentMail account. Re-copy the key from your AgentMail dashboard.
Email Inbound is on but nothing arrives Omniscio isn't running, or the polling task hasn't ticked yet Wait 30 seconds. Confirm Omniscio is open. Check the Omniscio log for [EmailInbound] lines.
Approved sender's email is dropped silently Subject doesn't include [MC:<token>] Either tell the sender to include it, or clear the Inbox Secret Token field to disable token gating.
forward_email automations fail with "AgentMail API key" error Same credential store; same key Same fix as the Test button — provision the key per step 2.
Key was rotated but Omniscio still uses the old one In-process cache holds it until the next 401/403 clears it Either restart Omniscio, or wait for the next AgentMail call to fail-then-retry with the new key.

What this does NOT cover

  • Existing-Gmail-inbox triage. That's a separate feature — see gmail-integration.md. Gmail integration uses your Google OAuth, not AgentMail.
  • Forwarded-newsletter digests. That's Email Summarizer, which uses the same AgentMail inbox but a different processing pipeline (Haiku summary on a thread, not session spawn).
  • Outbound forward_email automations. They use the same API key, but the rule structure is documented in forward-email-transport.md.
  • AgentMail account / billing. Sign-up, plan, quota, and inbox management all happen at agentmail.to. Omniscio has no UI for any of it.

For agents

How it works

API key lookup chain (agentmail-client.ts:357, getApiKey()). On first AgentMail call after Omniscio starts, the client checks process.env.AGENTMAIL_API_KEY first (used by dev/CI), then falls back to the OS credential store (credential.ps1 read on Windows, security find-generic-password on macOS, no fallback on Linux). The result is cached in-process. A 401/403 response clears the cache via clearApiKeyCache() so a rotated key gets picked up on the next call without an Omniscio restart.

Polling loop (email-inbound-service.ts). startEmailInboundService() schedules a periodic task at POLL_INTERVAL_MS = 30_000 (30 seconds). Each tick: listMessages since the last cutoff (with a 2-minute lookback overlap to catch indexing lag), dedup against email_inbound_processed, prescreen, and either continue an existing session by thread_id or spawn a new one. Concurrency is capped at MAX_CONCURRENT_SESSIONS = 10; brand-new thread IDs are capped at MAX_NEW_THREADS_PER_HOUR = 100 rolling-window via the NewThreadRateLimiter.

Settings keys (all in email-intake-settings.ts — one Zod schema that derives the type, defaults, and update shape, folded into AppSettings): emailInboundEnabled (false), emailInboundInboxId (''), emailInboundApprovedSenders ([]), emailInboundSecretToken (''), emailInboundPrescreenPrompt ('' — empty means use default CORE rules).

Pre-flight gate. The CLI control server's forward_email automation action also reads the AgentMail key via isApiKeyAvailable() — failing with a 409 response if the key isn't in the credential store. So the same provisioning step in this document also unlocks AgentMail-routed outbound automations. See forward-email-transport.md.

UI: Settings panel in /src/renderer/src/features/settings/EmailInboundSettings.tsx. Test-Connection IPC handler in /src/main/ipc/email-inbound-handlers.ts — it calls listMessages(inboxId) so a successful Test simultaneously proves the key works AND the inbox exists.

Attachments pipeline. processEmailAttachments() in /src/main/services/email/email-inbound-attachments.ts is the single entry point. It applies the disposition-priority sort + 10-cap before downloading, fans out the downloads in parallel via Promise.allSettled (so one 502 doesn't sink the rest), routes each downloaded attachment through the same partitionAttachments the chat path uses, validates against Anthropic's image format whitelist + size caps, runs extractOfficeText for modern Office files, saves PDFs/text-docs/extracted-Office to workdir via saveDocToWorkdir, and returns { images, documents, savedDocPaths, delivered, skipped }. createNewSession() in /src/main/services/email/email-inbound-service.ts calls it before spawn, threads images/documents into the launch call's positional args, prepends savedDocPaths to the prompt via buildPromptWithAttachments, and hands the skipped array to buildSessionPrompt so the "Note: N attachments could not be included:" line renders below the email body. AgentMail's get-attachment returns a small JSON envelope with a signed CDN URL (not the bytes) — downloadAttachment() in agentmail-client.ts handles the second-leg fetch with a proportional AbortController timeout.

Related

Last verified 2026-10-06