Skip to main content

Gmail

The Gmail connector pins your gateway to https://gmail.googleapis.com/gmail/v1 and reaches exactly one mailbox: the one whose owner authorised the token. It is good for an agent that triages an inbox, pulls the thread behind a question, or reports what arrived overnight. Sending, trashing and permanent deletion exist as capabilities but arrive switched off.

What you need

Gmail is OAuth-only — there is no API key to paste. You register your own Google OAuth client, and your gateway runs the authorisation code flow against it and keeps the connection refreshed from then on.

Needs gateway 1.1.0

From 1.1.0 the gateway performs the OAuth exchange itself and refreshes the access token two minutes before it expires, indefinitely. Older gateways have no OAuth endpoint at all. curl -s http://127.0.0.1:7376/health reports the version you are running.

Register your own OAuth client

The steps are the same for every Google connector and are written out once on the Google Drive page: Cloud console project, consent screen, OAuth client id and secret, and the redirect URI — http://127.0.0.1:7376/oauth/callback for a local gateway, https://<your-gateway-host>/oauth/callback for a tunnelled one, because Google permits loopback addresses on a custom port for installed apps but requires HTTPS for web clients.

Two things differ here. Enable the Gmail API rather than the Drive one. And request only the scopes matching the capabilities you intend to enable:

ScopeCovers
https://www.googleapis.com/auth/gmail.readonlymessages.list, messages.read, threads.list, labels.list, profile.read
https://www.googleapis.com/auth/gmail.sendmessages.send
https://www.googleapis.com/auth/gmail.modifymessages.trash — read-only is not enough to trash a message
https://mail.google.com/messages.delete — permanent deletion, bypassing the trash

Request the smallest set that covers what you plan to enable. gmail.readonly and gmail.send cover six of the eight capabilities; if you stop there, the two deletion capabilities will be refused by Google even if you switch them on. Note what the last row costs you: https://mail.google.com/ is full mailbox access, not a deletion permission, so granting it to reach messages.delete hands your OAuth client everything. If you can live with messages going to the trash instead of vanishing, stop at gmail.modify.

Do not leave the app in Testing

Google treats the Gmail scopes as restricted, which makes it tempting to leave the app in Testing with yourself as a test user. Don't: a refresh token issued while the app is in Testing expires seven days after it is issued, and refreshing it does not extend that, so the connection breaks weekly however well the gateway behaves. Set the app's user type to Internal if you are on Google Workspace, or publish it to In production — unverified is fine for your own mailbox, behind a one-off "Google hasn't verified this app" screen, and verification is what you need to hand the app to other people rather than to use it yourself. The Drive page has the detail, including the Workspace-admin Trusted route.

Add the connection

Both halves of this are gateway-side today: the console's Add connection dialog cannot finish an OAuth connection yet. Create the connection with POST /v1/connections/from-preset and "preset_id": "gmail", exactly as the Drive page shows.

Then authorise the connection_id that came back, with an operator token, as the Drive page describes but with Gmail's scopes:

BASE=http://127.0.0.1:7376
curl -s -X POST $BASE/v1/connections/<connection_id>/oauth/start \
-H "authorization: Bearer $TOKEN" -H 'content-type: application/json' -d '{
"client_id": "<your-client-id>",
"client_secret": "<your-client-secret>",
"redirect_uri": "http://127.0.0.1:7376/oauth/callback",
"scopes": ["https://www.googleapis.com/auth/gmail.readonly"]
}'

Open the authorize_url it returns and approve. Google redirects back to your gateway, which exchanges the code itself and seals the access token, refresh token and client secret as the connection's credential at that moment — never displayed again. permaura.com is not part of the exchange and never sees your mail or your tokens. The redirect_uri has to match what you registered with Google character for character, the authorize link is single-use and expires after 10 minutes, and with a device enrolled for approvals the flow has to be started from the gateway's own host rather than over a tunnel.

An agent still cannot touch the connection until a grant allows it.

What the agent can do

ActionWhat it doesOn by default
messages.listList messages, filtered by q and maxResultsYes
messages.readRead a single message by idYes
threads.listList threads, filtered by qYes
labels.listList labelsYes
profile.readRead the mailbox profileYes
messages.sendSend a message from raw MIMENo — approval required
messages.trashMove a message to trashNo — approval required
messages.deletePermanently delete a messageNo — approval required

All three writes land switched off, and all three carry a standing approval requirement on top. Even after you enable messages.send, every outbound message stops and waits for a human on a paired device before Google sees it — an agent cannot mail from your address on its own.

Good to know

  • The credential renews itself. A Gmail access token lives about an hour; the gateway refreshes it two minutes before expiry, on the invoke path, using the refresh token it sealed during the exchange. If Google ever refuses the refresh for good — you revoke the app's access from your Google account, or the app is still in Testing and its refresh token has aged out — run the OAuth start again on the same connection rather than rebuilding it.
  • Quota is metered in units, not calls. Google allows 6,000 quota units per minute per user. The capabilities here cost 5 (messages.list), 20 (messages.read, messages.trash), 10 (messages.delete) and 100 (messages.send) units each, so an agent paging through a large mailbox burns quota far faster than the request count suggests.
  • One mailbox only. Every path is scoped to users/me, so the connection can never read another person's mail — the token decides whose inbox that is.
  • No redaction is configured. This connector declares no redaction fields, so message bodies, addresses and headers reach the agent as Google returns them. That is the whole point of an inbox triage agent, but it means the mailbox's contents are in scope for whatever the agent does next.
  • q is the Gmail search syntax. The same operators as the Gmail search box (from:, newer_than:, has:attachment), so a tightly written grant plus a narrow q is the practical way to keep an agent out of the rest of the mailbox.