--- paths: - backend/email_integration/** --- # Email capture & drafts (Gmail) Read this before editing Gmail capture or draft creation. ## What it does - `backend/email_integration/` captures Gmail via **domain-wide delegation** (`credentials.py`, `matcher.py`, `parser.py`, `db.py`, `sync.py`, `scheduler.py`, `routes.py`) and creates Tier-B in-thread drafts (`compose.py`). It has its own `migrations/`. - Captured email becomes CRM activity through a **propose → approve** flow — nothing lands on a contact record until a human approves the proposal. ## Hard rule - **Agents draft; humans send.** Never let an agent send email, post, or contact an LP autonomously. Tier-B `compose.py` only *creates* a Gmail draft for human review. ## Outbound mail — the daily digest (internal; exempt from "agents draft") The CRM sends an internal **daily activity digest** to the fund's own admins. This is the ONE automated send path, and it does **not** violate the hard rule above: that rule governs outward **LP/prospect** contact. An internal ops email to the team's own inboxes is a different category. **Never extend this path to send to LPs/prospects.** - **Transport selector: `backend/digest_mailer.py`** (top-level, not in this package) — `send_digest(conn, to_addrs, subject, body)` picks **Gmail-DWD (preferred) → SMTP (fallback)**. DWD-impersonation sender = `CRM_DIGEST_SENDER` env, else the first active admin. - **Gmail-DWD path: `gmail_send.py`** (this package) — reuses `credentials.py`'s `DWDCredentialProvider` with the **`gmail.compose`** scope to call `users.messages.send` (REST, mirrors `compose.py`; body is `{raw}` not the draft's `{message:{raw}}`). The deployment's DWD grant includes `gmail.compose` (which authorizes send) but **not** the narrow `gmail.send` — so request `gmail.compose`. Verified live 2026-06-15 (token mint + a real `messages.send`). - **SMTP fallback: `backend/smtp_send.py`** (top-level) — stdlib smtplib reading `SMTP_*` env, populated on the box by the **Configure Digest SMTP** Start9 action (writes `/data/secrets/smtp/*`; entrypoint exports `SMTP_*`). A dedicated per-package account, independent of any StartOS system-wide SMTP. - The admin **`POST /api/admin/digest/test-email`** restricts recipients to the active-admin set (not an open relay), and logs send failures rather than echoing them (an auth error can carry a token/credential). Digest *content* generation (Phase B) runs on **Spark, never Claude** — the digest is deliberately un-anonymized. ## Known gap - Tier-B drafts currently reply to the **LP only**; reply-all is the next change (see AGENTS.md → Current state). See also `docs/gmail-enablement-runbook.md`.