# Connecting Gmail

You connect Gmail by adding a mailbox and choosing Google, which sends you to Google's own consent screen. Google returns tokens, Connect seals them into the mailbox's `config`, and the adapter then reads and sends over the Gmail API. The Owner and a customer take the same path through the same console; the Owner's flow falls back to the platform's own Google client.

- **Status:** Available
- **Audience:** both
- **Channels:** email
- **In the app:** #/mailboxes
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/email/connect-gmail/

## Signing in is not connecting a mailbox

Both use Google, and they are different acts with different consequences. Signing in proves who you are to Connect — it is the only sign-in there is, because there is no password login. Connecting a mailbox grants Connect standing access to the mail in a particular Google account, on behalf of a workspace, until that grant is withdrawn.

You can therefore sign in as yourself and connect a mailbox that is not yours — a shared support address, for instance — provided you can complete Google's consent for it. Revoking the mailbox grant does not sign you out, and signing out does not disconnect the mailbox.

## The path

1. Open `#/mailboxes`, add a mailbox, and choose Google.
   - Result: Add-a-mailbox asks one question — which provider — and asks it identically for both audiences.
2. Complete Google's consent screen for the account whose mail you want Connect to work.
   - Result: Google shows exactly what is being asked for. That screen, at the moment you see it, is the authority on the permissions granted — see [Gmail OAuth scopes and why each is asked for](/docs/email/gmail-scopes/).
3. Google returns to Connect and `oauth.store_tokens` writes the result.
   - Result: The tokens land in the mailbox's `config`, sealed by `settings_store` on save. Nothing echoes a credential back to a screen afterwards, including to the person who granted it.
4. Give the new row a role, a signature and its autonomy, then let the first sync run.
   - Result: History is read in and the row begins reporting health. Until a first pass completes, 'connected' is all the screen can honestly say.

> **Note** `config` is a real setter, not a computed property, precisely because of this flow: `store_tokens` rebuilds the whole dictionary and assigns it back on every refresh. A read-only property would drop each refreshed access token, and the mailbox would keep working until the current one expired and then fail for no visible reason.

## The Owner could not do this, and now can

This is one of the three email defects worth documenting, because it is the exact shape the two-audience rule exists to catch. Every customer workspace could connect a Google account through the customer wizard. The Owner could not: the handler behind 'Connect a Google account' existed and worked, and nothing on any Owner screen called it. A capability that is reachable by one audience and unreachable by the other is a defect even when both share the implementation, and it is invisible in the code — the function is right there.

The fix was not a second flow. Add-a-mailbox now asks the same question the customer's wizard asks, and the Owner's Gmail path falls back to the platform's own Google client, so there is one route in and one place where policy lives. [One implementation, two audiences](/research/two-audiences-one-implementation/) is the longer argument for why the second implementation is never the cheaper option.

## What the connection can do afterwards

| Capability | Notes |
|---|---|
| Read mail | Fetched into `tenant_*` in Gmail's own shape, then bridged into canonical threads and messages |
| Resume after a gap | By history ID rather than by re-reading — [Gmail history sync](/docs/email/gmail-history-sync/) |
| Send | Through `outbound.py`, the one send boundary a person's own send also uses |
| Write back | Read state and labels — [Writing back to Gmail](/docs/email/gmail-writeback/) |

A write-back failure never blocks a reply. Marking a message read or applying a label is bookkeeping on Google's side; if it fails, the reply has still gone and the failure is reported as itself rather than as a send problem. That ordering is deliberate — the alternative is a workspace where a labelling permission quietly stops customers being answered.

## When it stops working

**The grant was revoked at Google** — Someone removed Connect from the Google account's connected apps. Reconnect through the same path; the existing threads and contacts are untouched.
**The password changed or a security event occurred** — Google may invalidate the refresh token. The verdict on the row is a health verdict, not a configuration error — [Mailbox health](/docs/email/mailbox-health/) names it.
**The mailbox authenticates and returns nothing** — That is a [quiet mailbox](/docs/email/quiet-mailbox/), which is a signal rather than a fault.
**The row vanished from the list** — Suspect an empty workspace stamp before reconnecting, because reconnecting is what turns one invisible row into two rows for one inbox.

## Questions

### Does Connect store my Google password?

No, and it never sees one. OAuth exchanges consent for tokens; the tokens live in the mailbox's `config`, sealed on save and never echoed back to any screen. Withdrawing access at Google ends it without anything changing here.

### Can I connect a Google Workspace address I do not personally own?

You need to be able to complete Google's consent for that account, which is a question for your Google administrator rather than for Connect. What Connect requires is only that consent was granted.

### Is the Owner's Gmail connection different from a customer's?

The path and the console are the same. The difference is which Google client the flow falls back to: the Owner's uses the platform's own. There is no capability one audience has here that the other does not.

## Related

- [Gmail OAuth scopes and why each is asked for](https://connectbyjbrh.com/docs/email/gmail-scopes/)
- [Gmail history sync](https://connectbyjbrh.com/docs/email/gmail-history-sync/)
- [Writing back to Gmail](https://connectbyjbrh.com/docs/email/gmail-writeback/)
- [Mailboxes](https://connectbyjbrh.com/docs/email/mailboxes/)
- [OAuth 2.0](https://connectbyjbrh.com/docs/technology/oauth/)
- [Gmail API](https://connectbyjbrh.com/docs/technology/gmail-api/)
- [One implementation, two audiences](https://connectbyjbrh.com/research/two-audiences-one-implementation/)

## What this page is based on

- Connect source pack — channels, §1: providers, the `config` setter, defect 2
- Connect source pack — security: Google OAuth is the only sign-in, sealed credentials (`docs-source/sources/GENERAL.md` §10)
- Connect capability registry (`docs-source/facts.py`) — `gmail_oauth`, `google_sign_in`
