App integrations

# Email Inbox Plugin

Your inbox, with your agents alongside.

Read your mail, work through a reply with Hermes, and let your agents handle the routine. Cadu connects your existing mailboxes to a native iPhone inbox through the Himalaya-backed Mail plugin.

[Connect a mailbox](https://cadu.bot/docs#setup) [Explore the architecture](https://cadu.bot/docs#architecture)

Your existing email Native agent conversations Per-agent access

## [An inbox you can work from](https://cadu.bot/docs#inbox)

Choose an inbox from the header to switch between personal, studio, or work accounts. Each mailbox keeps its own folders, agent permissions, and automations.

**Read, search, and organize**

The app's search field filters loaded messages by sender, subject, or preview text. Ask your agent to search beyond loaded messages in a selected account and folder. You can also filter unread mail or thread status, and switch folders. Mark messages read or unread, archive them, or move them to another folder. Messages load newest first, in pages of 20.

**Follow the whole exchange**

Open an email to see related incoming and sent messages. Earlier replies collapse so the latest message is easy to read. Opening the conversation marks its messages read; browsing inbox previews does not.

**Open rich email and attachments**

Read HTML or plain text, expand individual messages, and open attachments from the reader. Remote images load only when you explicitly allow them for that message.

**Keep track of the work**

Give a thread a status: New, In progress, Needs input, or Done. These are shared workflow states, independent of the provider's read/unread flags.

New · In progress · Needs input · Done ·

## [Talk about the email, right there](https://cadu.bot/docs#discussions)

Open an email and tap **Chat** to work with the mailbox's owning agent. Ask for a summary, check an attachment, or prepare a reply. The conversation stays attached to that email thread, so you can come back to it later.

For example

“Read the pilot brief and draft a reply. Offer Tuesday at 10:00 or Thursday at 14:00, and ask which workflow they want to start with.”

### Bring in another agent

Use an **@mention** to ask an eligible agent for a second opinion. A helper with Read access can review the email and shared draft in its own email-specific session. Its response is returned to your main conversation, attributed to that agent.

Helpers keep their configured tools. A busy helper is reported as busy instead of being interrupted. The main discussion remains a one-to-one conversation with the owner.

### Email cards inside chat

When an agent lists, searches, or reads email, native mail cards can appear in the conversation. Tap one to open the email in the inbox. Cards are historical previews; opening them checks current mailbox access. **Rich email cards** in Inbox Settings controls cards for future tool results across the server.

**Opening a discussion does not start an agent turn**

The session receives a private reference to the email. The agent starts working when you send a message, or when a configured automation submits its prompt.

## [Get the reply right before it leaves](https://cadu.bot/docs#drafts)

Each thread has one shared reply draft. Ask an agent to prepare it, or use **Reply** in the email actions menu to write it yourself.

1.  **Prepare.** The agent can save a draft with Read access alone. It does not need permission to send.
2.  **Review.** Open Review Draft and check the To, Cc, subject, and message. Save changes for later or send the reviewed version.
3.  **Send.** Agent sends follow that agent's Send grant and approval setting. Your own composer sends directly.

Draft revisions prevent an agent or another device from silently overwriting a newer edit. Replies retain the email's threading headers.

### Ask before sending

Enable **Ask before sending** for an agent to queue its outgoing emails in **Send Approvals**. Review the agent, sender, recipients, subject, and body, then choose **Approve & Send** or **Reject**.

The server saves the exact message before requesting approval and rechecks permission when you approve. Pending requests survive restarts. A rejected request is never sent, and an uncertain delivery is not automatically retried.

**A send receipt is provider acceptance**

It is not proof that the recipient received or read the email. If delivery is uncertain, check the Sent folder before preparing another copy.

## [Give new arrivals a next step](https://cadu.bot/docs#automations)

Open **Automations** from the inbox toolbar. Create a named rule, optionally add conditions, and write the instructions your agent should follow.

1.  **Choose the cadence.** Each mailbox has a shared check interval, from 1 to 1,440 minutes.
2.  **Match the right emails.** Combine sender, subject, or body conditions with All or Any.
3.  **Describe the task.** Summarize a request, extract a deadline, or save a reply draft.

The mailbox owner runs every matching automation in the email's native discussion. Its normal Read, Send, and approval settings still apply.

### Predictable rules

Conditions use literal, case-insensitive matching: is, contains, is not, and does not contain. Body conditions inspect readable text, excluding attachments. No conditions means every new arrival. A mailbox supports up to 50 automations, with up to 20 conditions each.

### What happens when you close Cadu?

Hermes keeps checking on the server. Both the scheduler and dashboard must remain running; the iPhone does not perform background polling. Enabling or re-enabling a rule starts with future arrivals and skips existing mail.

Multiple matching rules run independently. Busy sessions defer work. Disabling a rule removes queued work, but does not interrupt a turn already submitted. An unconfirmed submission is never automatically submitted again.

## [Connect a mailbox](https://cadu.bot/docs#setup)

You need a connected Hermes instance, its dashboard API, and the Cadu Mail plugin. The server runs Himalaya; your iPhone connects to Hermes.

Hermes `≥ 0.21.3`Python `≥ 3.11`Himalaya `≥ 1.2`

1.  **Install Mail.** Open Mail in Cadu and follow the bundled-plugin setup. Install its declared Python and Himalaya dependencies in the Hermes runtime. Restart the dashboard to mount the API; gateways load the agent tool on their next start.
2.  **Choose an existing inbox or add one.** Existing Himalaya accounts are discovered automatically. To add another, open Inbox Settings, choose Add Inbox, then SMTP / IMAP.
3.  **Verify the connection.** Enter the account address and incoming/outgoing server settings. Setup checks incoming folders and SMTP authentication before publishing the account. TLS or STARTTLS is required.
4.  **Set agent access.** The owning agent starts with Read enabled and Send disabled for a newly managed inbox. Grant other agents access individually.

### Already using Himalaya?

Mail discovers the selected profile's `HIMALAYA_CONFIG`, including colon-separated TOML overlays, and profile-local Himalaya configuration. Only the default profile also reads the OS user's standard Himalaya configuration.

The Hermes email gateway account can also be adapted from its existing `EMAIL_ADDRESS`, `EMAIL_PASSWORD`, and IMAP/SMTP settings. Provider OAuth works when it is already configured in Himalaya.

### Change connection settings

Open Edit Inbox to adjust IMAP and SMTP settings. **Test** checks the draft configuration without saving; **Save** checks it and publishes a private connection override. Blank passwords preserve stored credentials or existing password helpers. Original provider configuration files are not rewritten.

**Credentials stay with the server runtime**

Managed passwords use Python keyring through Himalaya's auth.cmd mechanism. Configure a secure, unlocked keyring backend for the service user; the plugin does not select or enforce the backend. The plugin provides no plaintext fallback in managed configuration, and saved passwords are not returned to the app.

## [Decide what each agent can do](https://cadu.bot/docs#permissions)

In Inbox Settings, open **Access** and configure each agent for each mailbox. The server enforces these grants for agent tool calls. Authenticated dashboard operations act as you; your own mailbox access is separate from an agent's grants.

Mailbox permissions
| Setting | Allows |
| --- | --- |
| Read | List, search, and read mail, download attachments, mark and move messages, update thread status, participate in discussions, and prepare drafts. |
| Send | Submit outgoing email, including a saved reply draft. |
| Ask before sending | With Send enabled, require your approval of the exact outgoing message before transport. |

The owner keeps Read access. New agents start with no grants. Joining a discussion or asking a helper for a review does not grant Send permission. Revoking Read removes that agent from its email discussions; granting it again does not automatically rejoin them.

Boundary: these grants govern the Mail plugin. They do not revoke provider credentials or sandbox an agent's same-user shell access to account files. A permission change cannot recall email already submitted to the provider.

## [How sending restrictions are enforced](https://cadu.bot/docs#sending-restrictions)

These checks run in the server-side plugin for both direct agent sends and saved-draft sends. An instruction to send, a helper's review, or an automation prompt does not grant permission.

1.  **Resolve the acting agent.** The tool obtains its profile from the Hermes runtime. Model-supplied fields cannot choose the actor or enable trusted user access. Cross-profile grants are bound to the recipient profile's filesystem identity; stale or unbound grants do not authorize sending.
2.  **Check Send and the connection.** Message preparation requires the acting agent's Send grant and a configured sending backend. Replies that read a source email also require Read; sending a shared draft requires Read and Send. Read alone only permits preparing the draft.
3.  **Hold messages that need approval.** With Ask before sending enabled, the plugin persists the request and exact MIME snapshot and returns `awaiting_approval` before transport. A request field such as `approved` cannot bypass this check. Pending requests still need a decision if the setting is subsequently disabled.
4.  **Validate the user's decision.** Approval requires the trusted dashboard context. The server rechecks the agent's identity and current Send grant, validates the stored request, displayed fields, and MIME snapshot, then marks it as sending. Approval sends that snapshot, not replacement content supplied with the decision.
5.  **Recheck at transport.** Immediately before the durable outbox step, the plugin checks Send again and verifies either the matching approval or the current approval policy. It records intent before invoking Himalaya. A completed request is not sent twice; conflicting content or an uncertain previous attempt is rejected.

### Message limits

Outgoing messages are plain text, with 1–20 To recipients and up to 20 Cc recipients. The body must contain non-whitespace text and fit within 100,000 characters; subjects are limited to 998 characters and Cc input to 4,000. Recipient and subject headers cannot contain line breaks. The sender comes from the configured account. The tool does not expose Bcc, outgoing attachments, or arbitrary MIME. Request IDs must contain 16–64 letters, digits, or hyphens.

### Trusted user access and enforcement boundary

The dashboard constructs `user_access=True` for your mailbox operations, so your own composer can send without an agent grant or an additional agent approval. The plugin relies on the host dashboard to authenticate its routes. The agent tool exposes neither this context nor permission and approval controls.

These are Mail plugin checks, not a sandbox around other tools or provider credentials. A helper retains its configured tools, and a same-user shell may have access to the underlying account files. Revocation cannot recall a transport operation already authorized or a message already accepted by the provider.

## [One mailbox, two ways to work](https://cadu.bot/docs#architecture)

The native inbox and the agent's `cadu_mail` tool use the same server-side Mail implementation. Himalaya handles mailbox I/O; Hermes owns agent execution and session history.

Cadu iOS app connects through the authenticated Hermes dashboard to the Cadu Mail plugin. Hermes agents call the cadu_mail tool in that plugin. Both use Himalaya to access IMAP, SMTP, or Maildir.

### Reading a message

1.  The app requests a mailbox page using its authenticated dashboard connection, scoped by profile and account.
2.  The plugin checks access and asks Himalaya for envelopes. With progressive loading, rows arrive first and previews are enriched in batches of at most five.
3.  Opening a message can fetch the focused email first, followed by the conversation. RFC message references connect replies; matching subjects alone never merge threads.

### Discussions are native Hermes sessions

A durable binding connects mailbox, thread, and agent to a session created through the dashboard gateway. A hidden reference identifies the email without copying its body into the initial context. The binding follows session-compression descendants. Mail does not maintain a separate chat transcript.

### Sending is a durable operation

The plugin records send intent before invoking transport. A stable request ID makes an identical retry a no-op; reusing it with different content is rejected. Approval stores the exact MIME snapshot. Saved-draft sends use the reviewed draft revision, so content is not retyped by the model. Uncertain delivery is not replayed.

### Automation execution

One script-only Hermes cron job per mailbox detects arrivals and records queued work. The dashboard's dispatcher submits prompts through the native session gateway. Cron scripts do not carry dashboard credentials. Settings, baselines, and dispatch receipts live in Mail's activity database; receipts do not copy email content.

## [What lives where](https://cadu.bot/docs#storage)

Storage and retention
| Location | Contents and lifetime |
| --- | --- |
| Mail provider / Maildir | Source messages and folders. Himalaya reads and updates this mailbox. |
| Profile-local Mail storage | Profile-local settings, managed account configuration, grants, thread identity, statuses, drafts, approval state, and dispatch receipts. Draft bodies use ordinary JSON in SQLite; approval records can contain readable bodies and Base64 MIME snapshots. These stores are not encrypted by the plugin and have no automatic content-expiry policy. |
| Native Hermes sessions | Agent discussion and review history, including email content returned by tools. Mail stores bindings and request metadata, not a separate transcript copy. Mail cache cleanup does not delete Hermes session history. |
| Server preview cache | In-process message previews: 60-second lifetime, bounded to 16 MB / 1,000 entries. Bodies are not persisted by this cache. |
| iPhone cache | Catalogs, folders, previews, opened conversations, and prefetched recent message bodies in Application Support. File-protected and excluded from backups, with a 100 MiB pruning budget and seven-day cache-file expiry. |
| Server downloads | Attachment files under the downloading agent's profile. Himalaya operations clean downloads older than seven days from the mailbox owner's profile only. Helper downloads from a shared mailbox may remain indefinitely if no Mail operation cleans that helper's own profile. |

### Agent processing and retained copies

The Himalaya-backed inbox is readable by the Hermes server; it is not end-to-end encrypted against Hermes. When an agent reads mail, the returned content can enter its conversation history and be processed by its configured model provider. Automations can cause this without the app being open. Provider retention depends on your provider and its configuration.

Sending or discarding a shared draft changes its state but does not erase its stored body. Pending, rejected, and uncertain approvals retain their message content; successful approval delivery removes the approval's body, request, and full MIME snapshot while keeping receipt metadata. Base64 encoding is not encryption. These records have no automatic timed cleanup in Mail.

Revoking access or deleting a provider message does not erase previously returned tool results, model-provider records, downloaded attachments, or backups. The preview and iPhone cache lifetimes listed above are not a retention limit for all copies of an email.

### Offline reading

Previously cached mail can be read while disconnected. This is a bounded cache, not a full mailbox mirror. While connected, the app also prefetches the last seven days of mail from folders other than Trash, Junk, and Drafts on a best-effort basis. The app's search field filters loaded messages; agent search queries the selected mailbox folder through Himalaya and requires a server connection. Fetching uncached mail, sending, and agent work require the server connection. Cache keys separate server, profile, and account; observed Read revocation, authentication denial, or mailbox removal clears the affected cache.

### HTML rendering

The reader blocks scripts, frames, form submission, external stylesheets, and scripted network connections. Remote images are blocked by default; a per-message choice permits HTTPS images. Attachment downloads return a private local path, MIME type, size, and SHA-256 to agent file tools.

## [Dashboard API](https://cadu.bot/docs#reference)

Dashboard routes are mounted under `/api/plugins/cadu-mail` and require dashboard authentication. Requests are scoped by `profile` and, for mailbox operations, `account`. Use IDs returned by the account catalog.

```
GET /api/plugins/cadu-mail/accounts?profile=default
GET /api/plugins/cadu-mail/messages?profile=default&account=<account-id>&folder=INBOX&page=1&lightweight=true
```

The account response advertises capabilities. Feature-detect `progressive_mail`, `mail_collaboration`, `thread_drafts`, `send_approvals`, and `mail_automation_rules` instead of assuming every server is current.

Dashboard route reference
| Method | Route | Purpose |
| --- | --- | --- |
| GET / POST | `/accounts` | Discover or create accounts |
| GET / POST | `/appearance` | Inbox name and icon |
| GET / POST | `/connection` | Read safe settings or save overrides |
| POST | `/connection/test` | Check settings without saving |
| GET / POST | `/access` | Read or update agent grants |
| GET | `/folders · /messages` | Folders and paginated envelopes |
| POST | `/messages/previews` | Enrich 1–5 messages on a live page |
| GET | `/conversation · /attachment` | Read a thread or export an attachment |
| POST | `/flags · /move` | Update flags or move messages |
| GET / POST | `/activity` | Read or change thread status |
| POST | `/discussion/open` | Open the native agent session |
| GET / POST | `/draft` | Load or revision-check a draft |
| POST | `/draft/send · /send` | Submit a reviewed draft or new mail |
| GET | `/send-approvals` | List pending and past requests |
| POST | `/send-approvals/{id}` | Approve or reject a request |
| GET | `/send-approvals/{id}/message` | Locate the source email or sent copy in the mailbox |
| GET / POST | `/automations` | Read or update automation rules |

The approval-message endpoint returns a mailbox envelope, not the saved approval snapshot. It resolves the source email for a pending or rejected reply, or the sent copy after delivery, and can return 404 if no matching mailbox message exists. Use the approvals listing for the stored review fields, including the body of a pending request.

Automation writes use the collection `revision` and schema version 2. See [Agent tools](https://cadu.bot/docs#agent-tools) for the actions available to Hermes through the plugin.

## [What your agent can do](https://cadu.bot/docs#agent-tools)

Mail registers one tool, `cadu_mail`, in the `cadu_mail` toolset. The agent chooses one of 17 actions through its `action` argument. Together, they let it read email, work with attachments, organize a mailbox, prepare replies, consult another agent, and send when authorized.

The acting profile comes from the Hermes runtime, not a model-supplied profile argument. Other profiles' mailboxes use `mailbox_profile` and still require explicit grants.

### Discover and read

Reading messages and folders requires Read access.

**`accounts`**

Discover the agent's own accounts, shared accounts with grants, and mailboxes available for discussions. Start here outside an email session.

**`folders`**

List the selected account's folders, including valid destinations for moves.

**`list`**

Read a page of message envelopes from a folder. Results include message IDs and thread activity for follow-up actions.

**`search`**

Search beyond loaded messages in one account and folder using a Himalaya query. Requires Read access and returns matching envelopes, previews, and thread activity in pages of 20 without marking messages read.

**`read`**

Read a conversation using a folder and message ID, including message content and attachment metadata.

**`read_thread`**

Read the email referenced by an opened discussion. Checks current Read access, recorded participation, and the message's identity.

**`download_attachment`**

Save an attachment on the server using its folder, message ID, and attachment ID. Returns a local path, MIME type, size, and SHA-256 for file tools.

### Organize the work

These actions require Read access. Thread status is separate from read/unread flags.

**`get_status`**

Inspect thread activity and its current revision. Also returns available\_agents for consultations.

**`set_status`**

Set new, in\_progress, needs\_input, or done using the latest revision. A conflict requires refreshing before another update.

**`mark`**

Mark a message unread or read with the unread boolean.

**`move`**

Move a message to an existing destination folder, for example Archive.

### Prepare and send replies

Drafting requires Read. Sending requires Send and follows Ask before sending when enabled.

**`get_draft`**

Load the thread's shared reply draft and its revision before editing or sending.

**`save_draft`**

Create or update the persistent reply draft with a stable request\_id. Updating an existing draft requires draft\_revision; saving does not send.

**`send_draft`**

Send the exact saved draft\_revision without retyping its content. That revision identifies the delivery request and any approval snapshot.

**`send`**

Prepare a new message or reply with recipients, subject, body, and a stable request\_id. Replies can reference a thread or reply\_id and reply\_folder.

### Consult another agent

The requester must participate in the discussion; the helper needs mailbox Read access. Consultation does not grant Send.

**`ask_agent`**

Send a self-contained review task with target\_profile, body, and a stable request\_id. Opens or reuses the helper's email session. A busy helper receives no new request.

**`get_review`**

Retrieve the response for the same request\_id from the helper's native history. Waits up to 15 seconds by default; wait\_seconds accepts 0–20. Relay completed replies with attribution.

### Search with your agent

Ask Hermes to find an email, for example an unread invoice from a particular sender. The agent uses `search` with a required `query`, an account, and a folder. The folder defaults to `INBOX`; use `folders` to discover other folders and search each separately. Shared mailboxes use `mailbox_profile` and require the acting agent's Read grant.

```
{
  "action": "search",
  "account": "<account-id>",
  "folder": "INBOX",
  "query": "from maya@example.com and subject invoice and not flag seen",
  "page": 1
}
```

Queries use Himalaya's filter syntax, not bare search text: `from`, `to`, `subject`, and `body` take a pattern; `date`, `before`, and `after` take a `YYYY-MM-DD` date; `flag` takes a flag. Combine conditions with `and`, `or`, `not`, and parentheses. For unread mail, use `not flag seen`. Prefer single-word patterns; for multiple subject words, use `subject project and subject update`.

Results default to newest first. An explicit sort such as `order by date asc` overrides that order; Himalaya also supports sorting by sender, recipient, or subject. When `has_more` is true, increment `page` with the same query. Pages contain up to 20 results and page numbers range from 1 to 1,000. Queries must contain 1–4,096 characters, include non-whitespace text, and contain no control characters. Use a returned message ID and folder with `read` to open the conversation.

### A typical agent workflow

1.  **Get the context.** Use the email session's Mail reference with `read_thread`, or discover an account and use `list` or `search` followed by `read`.
2.  **Prepare the response.** Inspect attachments as needed, call `get_draft`, then `save_draft`. Optionally request a second opinion with `ask_agent` and retrieve it with `get_review`.
3.  **Finish deliberately.** Leave the draft for your review, or use `send_draft` when authorized. If the result is `awaiting_approval`, the message has not been sent; review it in Send Approvals.

```
{
  "action": "save_draft",
  "account": "<account-id>",
  "thread": "<thread-id>",
  "draft_revision": "<revision-from-get_draft>",
  "request_id": "<stable-unique-request-id>",
  "body": "Hi Leah, Tuesday at 10:00 works for me."
}
```

Call `get_draft` first. Omit `draft_revision` only when no draft exists. Omitted fields preserve saved values; an explicit empty string clears a field. A stale revision requires a refresh. `send_draft` sends the exact saved revision and obeys approval policy.

Status writes use the revision from `get_status`. Consultation requests use stable request IDs; `get_review` reads the response for that request, waiting up to 15 seconds by default.

**Account configuration stays in your hands**

The agent tool exposes no actions to add accounts, change credentials, grant permissions, approve its own sends, or configure automation rules. Those controls live in the authenticated dashboard API and Cadu's settings.

## [When something needs attention](https://cadu.bot/docs#troubleshooting)

### The mailbox does not appear

Check that Himalaya is available on PATH or in the selected profile's bin directory. Verify HIMALAYA\_CONFIG and the profile-local TOML paths. Non-default profiles do not inherit the OS user's standard configuration. Refresh the account catalog after fixing discovery.

### Mail asks for an update or dashboard restart

The app reads the running plugin's capabilities. Use Update Mail, then Restart Dashboard where supported. On a manually managed instance, restart the dashboard process and check setup again. Uploading files alone does not mount the new API.

### Saving a password fails

Confirm the Hermes service user has a secure, unlocked keyring backend and that the plugin's Python keyring dependency is installed in the same interpreter. The plugin relies on the configured backend and provides no plaintext fallback in managed configuration. Keep a blank password field when preserving an existing password command.

### An agent cannot send

Check Send for that specific agent and inbox. With Ask before sending enabled, inspect Send Approvals. Read permission and discussion participation do not authorize sending.

### An automation did not run

Check that the rule is enabled, the email arrived after its baseline, and its conditions match readable text. Confirm the Hermes scheduler and dashboard are running. Review the automation sheet and Hermes cron history for provider failures. Busy sessions defer; unconfirmed dispatch is not replayed automatically.

### A draft or status update conflicts

Reload the current state and review the newer revision before saving again. This prevents changes from another agent or device being overwritten.

### Delivery is unconfirmed

Check the provider's Sent folder before sending another copy. The outbox deliberately avoids automatic resend when provider acceptance is uncertain. A frozen draft can be discarded by the user after checking Sent; the original receipt is retained.
