Gmail OAuth scopes and why each is asked for
Each permission Connect asks Google for exists because one capability cannot work without it: reading mail, sending it, writing read state and labels back, and identifying which account was connected. Google's consent screen at the moment you connect is the authority on what was granted; the set Connect requests is held in google_scopes.py rather than assembled per workspace.
What each permission buys#
| What Google is asked for | The capability it serves | Withheld or withdrawn |
|---|---|---|
| Read the account's mail | Fetching messages into tenant_*, then canonical threads, messages and contacts | Nothing arrives. The mailbox is connected and permanently quiet — no triage, no drafts, no history |
| Send mail as the account | Every outbound message through outbound.py, whether written by Connect or by a person | Drafts are still written and still held; none can leave. The reply exists and the customer never sees it |
| Modify read state and labels | Writing back to Gmail after a reply | Replies still send. Your Gmail simply does not reflect what Connect did — messages stay unread and unlabelled |
| The account's address and basic profile | Naming the connected identity and enforcing one mailbox row per address | The connection cannot be identified or de-duplicated, which is why it is asked for even though it moves no mail |
The pattern is worth reading off that table rather than memorising the rows: each permission maps to exactly one thing that becomes impossible without it, and the failure is specific rather than general. A missing send permission does not break reading; a missing label permission does not break sending.
Withdrawing a permission later#
Google, not Connect, owns the grant. You can revoke it from the Google account's connected-apps settings at any moment, and nothing in Connect can prevent or delay that. What happens next is a health verdict rather than an error page: the adapter's next attempt fails, the mailbox reports what failed, and Needs You surfaces it.
- Existing threads, messages, contacts and history stay — they are canonical workspace records and were never Google's.
- New mail stops arriving from that account. There is no partial mode where some of it still comes through.
- Held drafts stay held. They are text in the workspace, not something in flight at the provider.
- Reconnecting through the same path resumes the mailbox rather than creating a second one, provided the row is visible to the uniqueness check.
What the permissions do not grant#
- Access to another Google service
- The mailbox grant is about mail. Nothing here reaches Drive, Calendar or Contacts.
- Access by another workspace
- Tokens live on one mailbox row inside one workspace, and every by-id action resolves through
mailbox_console.row_for. Isolation is enforced three times over — see Security and isolation. - A readable copy for anybody
- Credentials are sealed by
settings_storeon save and never echoed back to a screen, including to the person who granted them. - Training data
- Connect does not train a model on a workspace's mail. Mail is read to answer it.
Questions#
Why does Connect need to send as me rather than from its own address?
So that your customer receives a reply from the address they wrote to, on your domain, in your thread. A reply from a different address is a different conversation as far as the recipient's mail client is concerned.
Can I grant read access only, to watch what Connect would say?
Watching without sending is what draft_only autonomy is for, and it is the better tool: replies are written, nothing leaves, and everything is visible. Withholding the send permission achieves something similar by breaking the capability instead of configuring it.
Does withdrawing access delete what Connect already read?
No. Canonical threads, messages and contacts belong to the workspace. Withdrawal stops future access; Disconnecting a mailbox covers what a deliberate removal keeps and stops.