# Email

Symi connects to email through two integrations. Pick based on which account you have and what you want to do with it.

| Integration     | Best for                                                                                  | Setup effort                               |
| --------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------ |
| **Outlook 365** | Microsoft 365 work/school accounts, `outlook.com`, `hotmail.com`, `live.com`              | Two minutes — one chat command             |
| **Gmail**       | Google Workspace, personal Gmail, and you want incoming mail to auto-create Symi sessions | 15–30 minutes — Google Cloud Pub/Sub setup |

## Outlook 365

Ships bundled and auto-enabled. To connect:

1. Open any chat surface (Glass UI, Telegram, WhatsApp, etc.).
2. Type `/outlook login`.
3. Symi replies with a device-code URL and an 8-character code.
4. Open the URL in a browser, sign in with your Microsoft account, paste the code, approve access.
5. Credentials are stored at `~/.symi/credentials/outlook.json` (mode 0600) and the tools below are live immediately.

### Tools the agent gains

- `outlook_list` — list messages in a folder (`inbox`, `sentitems`, `drafts`, `archive`, `junkemail`). Optional unread filter.
- `outlook_read` — fetch the full body of a message by id (auto-marks read).
- `outlook_send` — new message with To, CC, subject, body.
- `outlook_reply` — reply or reply-all to an existing message id.
- `outlook_search` — keyword search across subject, body, sender.
- `outlook_folders` — list folders with unread and total counts.
- `outlook_move` — move a message to another folder.

### Commands

- `/outlook login` — connect (device-code flow).
- `/outlook status` — show current account, token state, last update timestamp.
- `/outlook logout` — disconnect and delete stored credentials.

### Limitations

- Plain-text body only. No HTML formatting, no attachments.
- **Pull-only.** Outlook does not create Symi sessions for incoming mail. You have to ask Symi ("any new mail?") and it calls `outlook_list` / `outlook_search`. If you want inbound-triggered sessions, use Gmail instead.
- No automatic retry on transient Graph 5xx errors — re-running a tool usually succeeds.

## Gmail

Gmail inbound uses Google Cloud Pub/Sub so new email triggers a webhook into Symi and creates a `hook:gmail:<id>` session.

### Prerequisites

- Access to a **Google Cloud project** — free at [console.cloud.google.com](https://console.cloud.google.com).
- `gcloud` CLI installed.
  - macOS: `brew install --cask google-cloud-sdk`
  - Linux: see [Google's install guide](https://cloud.google.com/sdk/docs/install).
- OAuth credentials for the Gmail API, managed via `gogcli`. See the `gog` skill for setup.
- Symi's webhook endpoint reachable by Google's push service — either a publicly reachable host or a Tailscale serve/funnel.

### Setup

1. **Authenticate gcloud**

   ```bash
   gcloud auth login
   gcloud config set project <project-id>
   ```

2. **Set up gogcli OAuth**

   ```bash
   gog auth credentials /path/to/client_secret.json
   gog auth add <your-email> --services gmail
   ```

3. **Create the Pub/Sub topic**

   ```bash
   gcloud pubsub topics create symi-gmail
   ```

4. **Grant Gmail permission to publish to the topic**

   ```bash
   gcloud pubsub topics add-iam-policy-binding symi-gmail \
     --member=serviceAccount:gmail-api-push@system.gserviceaccount.com \
     --role=roles/pubsub.publisher
   ```

5. **Create the push subscription** (points at Symi's webhook)

   ```bash
   gcloud pubsub subscriptions create symi-gmail-sub \
     --topic=symi-gmail \
     --push-endpoint=<SYMI_WEBHOOK_URL>/gmail-pubsub
   ```

6. **Register the Gmail watch** on the inbox label. The helpers in `src/hooks/gmail-setup-utils.ts` can do this from the gateway, or call the Gmail API's `users.watch` method directly with `curl`.

7. **Add `hooks.gmail` to `~/.symi/symi.json`**:

   ```json
   {
     "hooks": {
       "gmail": {
         "account": "user@example.com",
         "label": "INBOX",
         "topic": "projects/<project-id>/topics/symi-gmail",
         "subscription": "projects/<project-id>/subscriptions/symi-gmail-sub",
         "pushToken": "<random-shared-secret>",
         "hookUrl": "http://127.0.0.1:8788/gmail-pubsub",
         "includeBody": true,
         "maxBytes": 20480,
         "renewEveryMinutes": 720,
         "serve": { "bind": "127.0.0.1", "port": 8788, "path": "/gmail-pubsub" },
         "tailscale": { "mode": "off" }
       }
     }
   }
   ```

8. **Restart the gateway** to pick up the new hook config. `hooks.gmail` is not hot-reloaded.

9. **Verify** — send a test email. Google publishes to the topic → Pub/Sub pushes to Symi's endpoint → a `hook:gmail:<id>` session appears in the Glass UI feed.

### Config field reference

- `account` — the Gmail address being watched.
- `label` — Gmail label/folder to watch. Default `INBOX`.
- `topic` / `subscription` — fully-qualified Pub/Sub resource names.
- `pushToken` — shared secret Symi verifies on incoming push requests.
- `hookUrl` — URL the push subscription posts to. Must match `serve.bind` + `serve.port` + `serve.path` (or a Tailscale serve/funnel exposing them).
- `includeBody` — include the email body in the prompt. `maxBytes` truncates oversized emails.
- `renewEveryMinutes` — Gmail `watch` expires after ~7 days. Default 720 (12 hours) re-registers well ahead.
- `tailscale.mode` — `"off"` if Symi is publicly reachable, `"serve"` for a private tailnet webhook, `"funnel"` for a Tailscale-exposed public endpoint.
- `allowUnsafeExternalContent` — leave unset. Setting `true` disables the external-content safety wrapper on email content.

### Limitations

- Inbound only works while the Gmail watch is live. If `renewEveryMinutes` lapses, the Pub/Sub subscription fails, or the IAM binding is removed, mail stops arriving.
- Outgoing mail goes through the reply path of whichever session handles the inbound — there is no native Gmail send tool. For outgoing from scratch, Outlook is simpler.
- Very large messages are truncated at `maxBytes` so the prompt stays usable.

## Which one should you pick?

- **Send and read on demand from a Microsoft account** — Outlook.
- **Incoming mail auto-creates sessions** — Gmail (only option today).
- **You want both send and auto-receive in Outlook** — not available yet; push for Outlook is not implemented.
