Skip to content

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.