Gmail#
Gmail is an OAuth-backed, metadata-only provider. It exposes SYNC, sync-coupled
USER_IMPORT, and authenticated Cloud Pub/Sub WEBHOOK processing. It never requests
message bodies, snippets, attachments, drafts, Bcc, spam, or trash.
Capabilities#
| Capability | Behavior |
|---|---|
SYNC |
Reads a bounded inbox snapshot and creates a reviewable import session. |
USER_IMPORT |
Collects the connected mailbox and valid From, To, Cc, and Reply-To actors during sync. |
WEBHOOK |
Verifies Google-signed Pub/Sub pushes, durably queues them, and reconciles Gmail history in ARQ. |
CSV_IMPORT |
Unsupported: Gmail has no native task CSV export. |
| API-key credentials | Unsupported: private mailbox data requires delegated user authorization. |
Required configuration#
OAuth uses exactly https://www.googleapis.com/auth/gmail.metadata with offline access:
GMAIL_CLIENT_ID=<google-oauth-client-id>
GMAIL_CLIENT_SECRET=<google-oauth-client-secret>
GMAIL_REDIRECT_URI=https://<host>/api/v1/providers/gmail/oauth/callback
GMAIL_SCOPES=https://www.googleapis.com/auth/gmail.metadata
Pub/Sub subscriptions additionally require:
GMAIL_PUBSUB_TOPIC=projects/<project-id>/topics/teamboost-gmail-events
GMAIL_PUBSUB_SUBSCRIPTION=projects/<project-id>/subscriptions/teamboost-gmail-push
GMAIL_PUBSUB_AUDIENCE=https://<host>/api/v1/providers/gmail/webhooks
GMAIL_PUBSUB_PUSH_SERVICE_ACCOUNT=<service-account>@<project-id>.iam.gserviceaccount.com
WEBHOOK_ACCOUNT_LOOKUP_KEY=<at-least-32-byte-secret>
The Google Cloud topic, push subscription, IAM grant, OAuth consent screen, and OAuth client are operator-managed resources. Secrets must remain outside source control.
Install and sync#
Start OAuth, handle the callback, inspect the credential, and sync through the generic capability routes:
GET /api/v1/providers/gmail/oauth/authorize?organization_id=<id>
GET /api/v1/providers/gmail/oauth/callback?state=<state>&code=<code>&scope=<scope>
POST /api/v1/providers/gmail/oauth/callback?organization_id=<id>
GET /api/v1/providers/gmail/credentials?organization_id=<id>
POST /api/v1/providers/gmail/credentials/verify?organization_id=<id>
POST /api/v1/providers/gmail/imports/sync?organization_id=<id>
The encrypted OAuth bundle is bound to the normalized users.getProfile.emailAddress.
Refresh and live verification reject mailbox drift. A sync retries exactly once after a
Gmail access-token 401; ordinary 403 policy/scope failures are not refresh-retried.
Each snapshot reads at most two pages of 50 newest INBOX messages, collapses duplicate
message hits to first-seen thread IDs, and hydrates those threads with format=metadata.
If another page exists, every resulting draft carries a review note that older mail was
not read.
Task projection#
One Gmail thread becomes one proposed TeamBoost task:
| Gmail metadata | TeamBoost draft |
|---|---|
First usable Subject |
title |
| Connected mailbox | required mapped ownerId |
| Earliest mapped sender | creatorId; otherwise unset with a review note until TeamBoost confirms its reserved system actor |
| Earliest mapped non-mailbox sender | requestedById; otherwise unset |
| Gmail labels | gmail plus sanitized, deduplicated labels (maximum 20 labels, 80 characters each) |
| Thread state | status=NOT_STARTED, type=OTHER, priority=UNKNOWN, scheduleStage=PENDING |
An empty subject or unmapped connected mailbox is a blocking projection error. Gmail message bodies are absent from both the normalized task and the preserved source record.
Pub/Sub webhook lifecycle#
Authenticated organization operators manage the watch:
POST /api/v1/providers/gmail/webhooks/subscription?organization_id=<id>
GET /api/v1/providers/gmail/webhooks/subscription?organization_id=<id>
DELETE /api/v1/providers/gmail/webhooks/subscription?organization_id=<id>
Google Pub/Sub delivers externally to:
POST /api/v1/providers/gmail/webhooks
Successful first-time enablement durably queues one bounded initial mailbox snapshot. Notifications wait for that snapshot to complete, so the watch anchor does not create either a missing-history window or competing initial sessions.
The delivery endpoint does not accept TeamBoost auth. It requires a Google OIDC bearer
whose signature, expiry, issuer, audience, verified service-account email, and exact
service account match are checked with google-auth. The JSON body is capped at 64KiB and
must contain the configured subscription plus emailAddress and historyId Pub/Sub data.
Authenticated deliveries are routed through a keyed HMAC account locator; raw mailbox addresses, JWTs, and bodies are not stored in delivery state. Mongo insertion precedes ARQ enqueue. Duplicate Pub/Sub message IDs are idempotent. The worker serializes each mailbox, reads from the committed history cursor, creates at most one review session per delivery, then advances the cursor with compare-and-set. An expired history cursor triggers one bounded full-snapshot recovery. Watches renew daily and a 15-minute reconciliation job recovers missed notifications.
Credential deletion and cross-mailbox OAuth replacement are blocked while a watch is active. Disable the subscription first.
Limits#
- One Gmail mailbox per TeamBoost organization.
- 100 newest inbox messages per synchronous or expired-cursor recovery snapshot.
- 100 changed thread IDs per webhook history job and 100 history pages.
- Eight concurrent thread hydrations during snapshots.
- Domain-wide delegation is not supported; it needs an enterprise multi-mailbox credential, delegated-subject, allowlist, audit, and revocation contract.