Gmail
Connect Gmail to Project88 — read, search, send, and label emails from any agent.
The Gmail integration connects an authenticated Gmail account to your org via Composio. Once connected, every agent in the org has access to Gmail tools by default (you can scope them per agent on the Tools tab).
Connect
⌘K→ Settings → Connections- Search "Gmail" in the catalog (900+ toolkits are surfaced dynamically
via the
composio-authEdge Function v6). - Click Connect. An OAuth popup opens and is closed by a static
public/oauth-callback.htmlpage thatpostMessages the result back. - The tokens are stored encrypted in Supabase Vault via the
insert_integrationRPC. The plaintext token never reaches your browser.
You'll see Gmail in your Connections list with a "Connected" status.
Tools exposed
The integration unlocks Gmail tools dynamically. Common ones include:
| Tool | What it does |
|---|---|
GMAIL_FETCH_EMAILS | Search / list emails matching a query |
GMAIL_SEND_EMAIL | Send a new email |
GMAIL_REPLY_TO_EMAIL | Reply to a thread |
GMAIL_CREATE_LABEL | Create a Gmail label |
GMAIL_LABEL_THREAD | Apply a label |
The proxy applies description overrides to about a dozen of these tools so the LLM picks them up reliably — Composio's stock descriptions are sometimes generic and the LLM ignores them.
Auto-fallback to Outlook
If an agent tries Gmail tools and there's no Gmail connection, the proxy auto-falls-back to the equivalent Outlook tools when an Outlook integration exists. The user-facing experience is "the agent reads your email" without the user having to know which provider is connected.
Inbox mode
Once Gmail is connected, the Inbox mode is populated automatically — three-panel email client with folders, labels, search, attachments, and inline reply.
Real-time inbox: Gmail push notifications
When configured, new messages toast on every app surface (not just Inbox) within a few seconds of landing in Gmail. See Inbox → Real-time inbox for the user-facing behavior. Setup on the platform side is a one-time GCP Pub/Sub + secrets pass:
- Create a Pub/Sub topic in the same GCP project that owns the
OAuth client, and grant
gmail-api-push@system.gserviceaccount.comthePub/Sub Publisherrole on it. - Create a push subscription on the topic pointing at
https://<project>.supabase.co/functions/v1/gmail-webhook?token=<shared-secret>. The webhook authenticates on the shared secret (verify_jwtis off, since Google sends no Supabase JWT) — both the URL param and the stored secret are trimmed before comparison, so a paste with a trailing newline doesn't silently 403 every push. - Set the Supabase edge secrets:
GMAIL_PUBSUB_TOPIC— the topic's fully-qualified name (projects/<project>/topics/<topic>), used bygmail-watchwhen it registers a per-integrationusers.watch.GMAIL_WEBHOOK_SECRET— the same shared secret you baked into the push-subscription URL.
Once both secrets exist, gmail-watch registers a watch per
integration on account connect, and a 12-hour pg_cron job renews
every registered watch (Gmail watches expire in ≤7 days). Without
these secrets everything no-ops gracefully — the Inbox keeps
polling as before, no crashes, no console noise.
The end-to-end pipeline: Gmail users.watch → GCP Pub/Sub →
gmail-webhook edge function → history.list delta →
email_events INSERT → Supabase Realtime → toast + refetch in
EmailProvider. A 10-min recency guard prevents backfill after
watch re-plug from toast-storming. Migration 149 adds the
gmail_watch_state and email_events tables backing it.
Outlook accounts stay on the existing 60 s label-count poll — Microsoft Graph webhooks are a separate effort.
Disconnect
Settings → Connections → Disconnect on the Gmail card. The integration row is soft-deleted; the Vault secret is revoked.