Email integration
Email integration
Section titled “Email integration”Kazma’s native email-manager skill gives the agent full mailbox tools: list, get, send, delete, categorize, and analyze. There is no required /email slash command — use chat.
Quick start (sandbox — zero config)
Section titled “Quick start (sandbox — zero config)”Restart the server and ask:
- List my inbox
- Analyze the lottery / phishing email
- Draft a reply to boss@corp.com (HITL approve for send/draft mutators)
Without credentials, every response is prefixed with [sandbox mode]. Data lives in kazma-data/sandbox_emails.db.
| Tool | Purpose | HITL |
|---|---|---|
email_accounts | Every connected mailbox, its name, address and calendar | No |
email_list | List/search folder | No |
email_get | Full message body | No |
email_send | send / reply / forward / draft | Yes |
email_delete | trash or permanent | Yes |
email_categorize | read/star/labels/move | Yes |
email_analyze | summary, actions, phishing | No |
A listing gives each mail’s sender, date and subject (subjects up to 150 characters, senders up to 80), so the agent can answer “what came today” without opening each mail.
Common args: provider (auto|sandbox|gmail|microsoft|imap|pop), optional account (which mailbox: an account’s name or its address; see More than one account).
Provider resolution (auto)
Section titled “Provider resolution (auto)”- Explicit
provider/accounton the tool call EMAIL_DEFAULT_PROVIDER- First configured real account (Gmail → Microsoft → generic IMAP/POP → the other accounts)
- Sandbox
A provider or account named in steps 1–2 that is not connected is refused
(“Gmail is not connected. Settings → Email → …”), never answered from the
sandbox — the same rule Calendar follows. An unknown provider name or a
mistyped account alias is refused too. The sandbox answers only auto with
nothing connected, provider=sandbox, or an account whose TYPE is
sandbox.
Connect email (Settings UI)
Section titled “Connect email (Settings UI)”Open Settings → Email (/settings?tab=email).
Each of Gmail and Microsoft has a mode switcher: OAuth | IMAP | POP.
| Card | Modes |
|---|---|
| Sandbox | Always on — no setup |
| Gmail / Workspace | OAuth (recommended, Gmail API) · IMAP (imap.gmail.com + app password) · POP (pop.gmail.com + app password) |
| Microsoft 365 | OAuth (recommended, Graph) · IMAP (outlook.office365.com) · POP (outlook.office365.com:995) |
Status shows Active provider (auto) and the auth mode badge (OAUTH / IMAP / POP). Disconnect clears vault/env for that provider.
| Protocol | Hosts (preset) | Notes |
|---|---|---|
| Gmail IMAP | imap.gmail.com:993, smtp.gmail.com:587 | App password; enable IMAP in Gmail |
| Gmail POP | pop.gmail.com:995, smtp.gmail.com:587 | Inbox-only; no drafts/labels |
| MS IMAP | outlook.office365.com:993, smtp.office365.com:587 | Basic auth often disabled → use OAuth |
| MS POP | outlook.office365.com:995, smtp.office365.com:587 | Same auth caveat; limited features |
API:
POST /api/email/protocol/connect{"provider":"gmail"|"microsoft"|"generic","protocol":"imap"|"pop","address":"...","password":"..."}
POST /api/email/protocol/disconnect{"provider":"gmail"|"microsoft"|"generic"}
GET /api/email/presetsGmail OAuth setup (Workspace-friendly)
Section titled “Gmail OAuth setup (Workspace-friendly)”- Google Cloud Console → APIs & Services → enable Gmail API and Google Calendar API.
- OAuth consent screen → add scopes
gmail.modify,gmail.send,calendar,userinfo.email. - Credentials → OAuth client ID → type Web application.
- Authorized redirect URI (must match your host; Calendar uses the same callback):
http://127.0.0.1:9090/api/email/oauth/gmail/callbackhttps://your.domain/api/email/oauth/gmail/callback- Settings → Email → paste Client ID + secret → Save OAuth client → Connect with Google. That consent also requests Calendar. If Calendar was skipped or the Calendar API is off, use Connect Google Calendar on the calendar card of the same page (same client, same redirect URI).
The calendar card has one row per calendar, each with its own state, its own Connect and its own Disconnect:
- Connect Outlook Calendar signs in to Microsoft for the calendar only; Microsoft mail is left as it is. It uses the Azure application (client) ID saved on the Microsoft card and the same redirect URI. When Microsoft refuses the redirect (an app registered for the device code only), the card’s Connect with a code instead does the same with a code.
- Disconnect turns that calendar off until you connect it again from the card. The mail sign-in covers Calendar too, so deleting the calendar’s tokens alone would have left it reading through the mail grant; while a calendar is off, nothing reads or keeps its tokens. Mail stays connected.
- Disconnecting Gmail or Microsoft mail leaves the calendar on its own row connected; disconnect it there.
Gmail-only tokens cannot list Google Calendar events. The calendar skill used to fall back silently to an empty sandbox; it now fails closed with a connect hint when provider=google has no calendar grant.
Env alternative:
EMAIL_GMAIL_CLIENT_ID=...EMAIL_GMAIL_CLIENT_SECRET=...# After OAuth, tokens are stored automatically:# EMAIL_GMAIL_ACCESS_TOKEN / EMAIL_GMAIL_REFRESH_TOKEN (also vault)App passwords still work for personal Gmail if your admin allows them; OAuth is preferred for Workspace.
Error: “Kazma has not completed the Google verification process” / 403: access_denied
Section titled “Error: “Kazma has not completed the Google verification process” / 403: access_denied”Your OAuth app is in Testing. Google only allows test users until the app is verified (Gmail scopes are “sensitive/restricted”).
Fix for personal / single-user use:
- Google Cloud Console → APIs & Services → OAuth consent screen.
- Open Test users → Add users → add the Gmail you sign in with (e.g.
you@gmail.com). - Save, wait ~1 minute, try Connect with Google again.
- On the consent screen you will see “Google hasn’t verified this app” → Advanced → go to Kazma (unsafe) — expected for an unverified personal project.
Do not need full Google verification unless you ship Kazma as a multi-tenant product to arbitrary Gmail users.
Error: 401: invalid_client / “The OAuth client was not found”
Section titled “Error: 401: invalid_client / “The OAuth client was not found””Google cannot find a client with the ID you sent. This is a credentials problem, not a publishing or scopes one — the consent screen never loads.
Almost always the Client ID field holds the Client Secret. The two sit next to each other in the Cloud Console and look alike:
| Field | Always looks like |
|---|---|
| Client ID | 1234567890-abcdef.apps.googleusercontent.com |
| Client Secret | GOCSPX-… |
Fix: Google Cloud Console → APIs & Services → Credentials → your
OAuth 2.0 Client ID → copy the Client ID (the long one ending in
.apps.googleusercontent.com) → Settings → Email → OAuth → re-save both
fields → Connect with Google.
Kazma now rejects a mismatched pair when you save it, and again when you click Connect, naming the field. Both checks are format-only — a well-formed client that was deleted in the Console still fails at Google.
Testing vs In production — pick one deliberately
Section titled “Testing vs In production — pick one deliberately”The publishing status is not cosmetic; it decides how often you have to reconnect.
| Testing | In production (unverified) | |
|---|---|---|
| Who can connect | Only accounts on the Test users list (max 100) | Anyone, up to a 100-user cap while unverified |
| Refresh token lifetime | Expires after 7 days — you reconnect weekly | Does not expire on a timer |
| Consent screen | “Google hasn’t verified this app” warning | Same warning until verified |
| Verification needed | No | Only to lift the cap / drop the warning |
For a self-hosted single-operator install, In production is usually the better
choice: the scopes and the warning screen are identical, and the weekly
invalid_grant reconnect goes away. Publishing does not submit anything for
review — verification is a separate step you can decline.
:::note After switching to In production, reconnect once A refresh token issued while the app was in Testing carries its 7-day clock; publishing does not extend a token that already exists. Go to Settings → Email → Disconnect, then Connect with Google again to mint a durable one. Skipping this looks exactly like the fix not working, a week later. :::
Gmail’s gmail.modify / gmail.send are restricted scopes, so an
unverified app — Testing or published — always shows the warning screen and
is capped at 100 users. That cap is irrelevant for self-host and is the only
thing verification buys you.
Workspace: if the account is a company Google Workspace user, an admin may still block third-party OAuth; use a personal test user or admin-approved app.
Error: Request had insufficient authentication scopes / insufficientPermissions
Section titled “Error: Request had insufficient authentication scopes / insufficientPermissions”OAuth “succeeded” but the token only has profile/email (or no Gmail scopes). Gmail API then returns 403.
Fix (required — current token will never work until reconnected):
- Google Cloud → APIs & Services → enable Gmail API.
- OAuth consent screen → Data access / Scopes → Add or remove scopes → add:
https://www.googleapis.com/auth/gmail.modifyhttps://www.googleapis.com/auth/gmail.send
(optional:…/auth/userinfo.email)
- Save. Confirm you are a Test user.
- Kazma Settings → Email → Disconnect Gmail.
- Connect with Google again.
- On Google’s consent UI you must see Gmail / Read, compose, send, and permanently delete all your email (wording varies) — not only “See your email address”. Approve those.
- Retry in chat: List my inbox.
If consent only asked for email address, scopes were never added on the Cloud project — fix step 2, then disconnect/reconnect.
Microsoft Graph OAuth setup
Section titled “Microsoft Graph OAuth setup”- Azure app registration → Web redirect URI:
http://127.0.0.1:9090/api/email/oauth/microsoft/callback- Delegated permissions:
Mail.Read,Mail.ReadWrite,Mail.Send,offline_access. - Settings → Email → Client ID (+ secret if confidential client) → Connect with Microsoft.
Device code remains available under “Alternative: device code”.
EMAIL_MS_CLIENT_ID=...EMAIL_MS_CLIENT_SECRET=... # if requiredEMAIL_MS_TENANT_ID=commonEMAIL_MS_REDIRECT_URI=http://127.0.0.1:9090/api/email/oauth/microsoft/callback # optional overrideSet KAZMA_PUBLIC_URL=https://your.domain behind a reverse proxy so redirect URIs resolve correctly.
More than one account
Section titled “More than one account”The Gmail and Microsoft cards connect the main accounts. Any number of other mailboxes — three Gmail accounts, a second Outlook, a work IMAP server — are added on Settings → Email → Other accounts:
- Add Google account / Add Microsoft account: you give the account a
short name used in chat (
work,personal; letters a–z, digits and hyphens), then sign in. The sign-in asks which account to use, so a browser signed into several Google or Microsoft accounts picks the one you choose. The same Google or Microsoft app registration signs every account in, and its redirect URI is the one you already registered. When Microsoft refuses the redirect (an Azure app registered for the device code only), Add a Microsoft account with a code does the same by code. - Add with a password: an app password for Gmail, or an IMAP/POP server with its host. Kazma tries the login before it keeps the account, and refuses one that does not work with the server’s answer.
- Reconnect signs an account in again under its name; Remove forgets its sign-in (the mailbox itself is not touched). An address that is already connected — as the main account or another one — is refused.
In chat, name the account or its address: “check my work inbox”, “send
this from personal@gmail.com”, “what’s on the team calendar tomorrow”. The
agent’s email_accounts tool lists every account with its name, address,
whether it is signed in and whether its calendar is usable, and every email
and calendar tool takes account= (a name, an address, or gmail /
microsoft for a main one). With no account named, the main account of the
provider answers, as before. An approval card for sending shows the account.
A Google or Microsoft account’s calendar comes with its sign-in when the consent screen grants calendar access; a password account has no calendar.
Each account keeps its own tokens (the vault, under email.account.<name>.*)
and every refresh writes that account’s keys only. The list itself (names,
addresses, which have a calendar) is the setting email.accounts.
Accounts can still be written into .env; they are listed with a .env
badge and changed there, not in Settings:
EMAIL_ACCOUNTS=personal,work
EMAIL_ACCOUNT_PERSONAL_TYPE=gmailEMAIL_ACCOUNT_PERSONAL_ADDRESS=me@gmail.comEMAIL_ACCOUNT_PERSONAL_PASSWORD=app-passwordStatus:
GET /api/email/statusGET /api/email/accountsAnalyze
Section titled “Analyze”email_analyze uses the active LLM when available; otherwise a heuristic phishing/action detector. Always check security.risk_level on lottery/bank-looking mail.
Safety
Section titled “Safety”- Mutating tools require HITL approval (same gates as file/shell).
- Never paste app passwords into chat; use env or vault.
- Sandbox never sends real mail.
- Graph OAuth is the recommended M365 path; IMAP/POP basic auth only works when the tenant still allows it.
- POP is inbox-oriented (no folders/labels/drafts); prefer IMAP or OAuth when possible.
Related
Section titled “Related”- Plan:
docs/plans/EMAIL_INTEGRATION_FULL_PLAN.md - Environment variables
- Tools catalog

