Skip to main content
Version: Latest

Gmail

Proxy for the Gmail v1 REST API. Gmail is fundamentally user-scoped — every message and thread belongs to a single mailbox — so this connector only supports idp_passthrough. Each request carries the caller's own OAuth grant, and the policy layer adds operator-controlled guards on outbound mail and label manipulation.

Overview​

Typical uses:

  • An agent reads the user's unread inbox to summarize their morning.
  • A scheduling bot sends confirmation emails from the user's address.
  • A compliance workflow flags and trashes phishing matches.

Service-account mode (domain-wide delegation impersonating a specific mailbox) is technically possible against Gmail but deliberately not shipped: most operator questions we've heard on agentic Gmail access are about preventing an agent from mass-emailing out of a service mailbox, not enabling it. If your use case does need SA mode, file an issue — the groundwork is already in the schema.

Default policy​

The connector ships policy/gmail.rego (package pbac.connectors.identos.google_gmail). It enforces four things:

1. IdP-routing subject obligation​

subject_obligations contains obl if {
input.resource.type == "urn:connector:identos:google-gmail"
input.connector.upstream_auth.type == "idp_passthrough"
input.subject.idp_provider != "google"
obl := { "type": "require_authn_at", "idp": "google", ... }
}

Same pattern as google-drive — callers from a non-Google session get a step-up obligation before any Gmail call succeeds.

2. Operator-enforced read-only mode​

deny contains msg if {
input.resource.type == "urn:connector:identos:google-gmail"
input.action.method != "GET"
data[...].read_only == true
...
}

A single boolean at data.pbac.operator.connectors["identos.google-gmail"].read_only disables every mutating route: send_message, modify_labels, trash_message, untrash_message, create_draft, create_label, delete_label. Useful during an incident review or when piloting an agent that should observe but not act.

3. Outbound recipient guards​

Operators define two lists and the rego evaluates them on every POST:

{
"allowed_recipient_domains": ["acme.com", "partner.example"],
"blocked_recipient_domains": ["competitor.example"]
}

The denylist is checked first — any recipient matching a blocked domain fails the request immediately. The allowlist is only enforced when non-empty; if set, every To/Cc/Bcc address must match. The gateway adapter populates input.resource.recipients by parsing the send / draft payload.

4. Protected labels​

{
"protected_label_ids": ["Label_42_LEGAL_HOLD"]
}

Labels listed here can't be added, removed, or deleted. Prevents an agent from silently removing a legal-hold marker by calling modify_labels with removeLabelIds=[Label_42_LEGAL_HOLD]. Delete on the protected label itself is also blocked.

Supplying operator data​

KEY=pbac.operator.connectors.identos.google-gmail

curl -X PUT "http://localhost:8080/admin/api/policy-data/by-key?dataKey=$KEY" \
-H "X-Admin-API-Key: $PBAC_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg k "$KEY" --argjson s '{
"read_only": false,
"allowed_recipient_domains": ["acme.com"],
"blocked_recipient_domains": ["competitor.example"],
"protected_label_ids": ["Label_42_LEGAL_HOLD"]
}' \
'{dataKey:$k, payload:($s|tojson)}')"

Scope model​

ScopeUsed for
PBAC scopes (internal)gmail:messages:read, gmail:messages:write, gmail:messages:send, gmail:labels:manageRoute authorization — note that send is a dedicated scope separate from write, so you can grant an agent the ability to label/archive mail without also granting it send
Upstream OAuth scopesgmail.readonly, gmail.send, gmail.modify, gmail.labelsThe Google-side grants the user must consent to

See Google Workspace: multi-connector setup for how gmail's upstream scopes compose with drive and calendar on a single Google IdP.

Manifest reference​

  • ID: identos.google-gmail
  • Version: 1.1.0
  • Resource type: urn:connector:identos:google-gmail

Supported auth modes​

TypeDetails
idp_passthroughrequires IdP google

Setup fields​

IDLabelDefaultSecret?Notes
base_urlAPI base URLhttps://gmail.googleapis.comno—

Scopes​

Scope
gmail:messages:read
gmail:messages:write
gmail:messages:send
gmail:labels:manage

Routes​

MethodPatternScopeResource template
GET/gmail/v1/users/me/profilegmail:messages:read—
GET/gmail/v1/users/me/messagesgmail:messages:read—
GET/gmail/v1/users/me/messages/{message_id}gmail:messages:readgmail://message/{{message_id}}
GET/gmail/v1/users/me/threadsgmail:messages:read—
GET/gmail/v1/users/me/threads/{thread_id}gmail:messages:readgmail://thread/{{thread_id}}
POST/gmail/v1/users/me/messages/sendgmail:messages:send—
POST/gmail/v1/users/me/messages/{message_id}/modifygmail:messages:writegmail://message/{{message_id}}
POST/gmail/v1/users/me/messages/{message_id}/trashgmail:messages:writegmail://message/{{message_id}}
POST/gmail/v1/users/me/messages/{message_id}/untrashgmail:messages:writegmail://message/{{message_id}}
GET/gmail/v1/users/me/labelsgmail:messages:read—
POST/gmail/v1/users/me/labelsgmail:labels:manage—
DELETE/gmail/v1/users/me/labels/{label_id}gmail:labels:managegmail://label/{{label_id}}
GET/gmail/v1/users/me/draftsgmail:messages:read—
POST/gmail/v1/users/me/draftsgmail:messages:write—

MCP tools​

NameScopeDescription
get_profilegmail:messages:readGet the authenticated user's Gmail profile (email address, total messages, history ID).
list_messagesgmail:messages:readList messages in the authenticated user's mailbox. Supports Gmail search syntax (e.g. "from:alice newer_than:7d").
get_messagegmail:messages:readGet a single message by ID. Use format=full for headers+body, format=metadata for headers only.
list_threadsgmail:messages:readList conversation threads. Same query syntax as list_messages.
get_threadgmail:messages:readGet a single conversation thread with all its messages.
send_messagegmail:messages:sendSend an email. Supply either a pre-built RFC 822 'raw' payload (base64url) or the structured To/Cc/Bcc/Subject/Body fields and the connector will assemble the MIME message for you.
modify_labelsgmail:messages:writeAdd or remove labels on a message (e.g. mark as read by removing UNREAD, archive by removing INBOX).
trash_messagegmail:messages:writeMove a message to the Trash. Reversible for 30 days via untrash_message.
untrash_messagegmail:messages:writeRestore a trashed message to the inbox.
list_labelsgmail:messages:readList all labels in the authenticated user's mailbox (both system labels and user-created).
create_labelgmail:labels:manageCreate a new user label in the mailbox. Returns the label with its id, which modify_labels needs.
delete_labelgmail:labels:manageDelete a user-created label. System labels (INBOX, SENT, etc.) cannot be deleted.
list_draftsgmail:messages:readList unsent draft messages in the mailbox, with their ids and message metadata.
create_draftgmail:messages:writeCreate a draft message (not sent). Same message payload shape as send_message.

Operator data schema​

Keys the operator can supply under data.pbac.operator.connectors["identos.google-gmail"].* — consumed by the connector's policy.

KeyTypeDescription
read_onlybooleanGlobal kill-switch for all mutating routes (send, modify, trash, drafts, labels). When true, only read operations are allowed.
allowed_recipient_domainsarrayOutbound email domains the caller is allowed to send to (e.g. 'acme.com'). Empty means no restriction. If set, every To/Cc/Bcc recipient must match.
blocked_recipient_domainsarrayOutbound email domains that must never receive mail (evaluated before allowed_recipient_domains; easier to manage a small denylist than a large allowlist).
protected_label_idsarraySystem/user label IDs that modify_labels and delete_label must not touch. Useful for regulatory holds (LEGAL, LITIGATION_HOLD, etc.).