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 connectors/identos.google-gmail/policy/gmail.rego
(package pbac.connectors.identos.google_gmail). It holds three kinds of
rule.
1. IdP-routing subject obligation
subject_obligations contains obl if {
input.resource.type == "urn:connector:identos:google-gmail"
input.subject.idp_provider != "google"
obl := { "type": "require_authn_at", "idp": "google", ... }
}
This rule does not fire today. It reads input.subject.idp_provider, and the
subject the AS builds carries the IdP as input.subject.idp, so the comparison
is undefined and the rule body fails. What does stop a caller without a Google
link is the gateway itself. Before it forwards a call it checks for a usable
linked Google token, and without one it answers 401 with
"error": "idp_link_required" and a link_url.
Read-only mode is not a Gmail rule. Use the
Operator read-only mode control,
which denies every scope that writes on the resources you list under
pbac.operator.controls.resource_read_only.
2. Outbound recipient guards
Operators define two lists. The rules apply to calls under
gmail:messages:send and gmail:messages:write:
{
"allowed_recipient_domains": ["acme.com", "partner.example"],
"blocked_recipient_domains": ["competitor.example"]
}
Any recipient in a blocked domain denies the call. The allowlist applies
only when it is non-empty, and then every To/Cc/Bcc address must match
it. The rules read the To, Cc and Bcc tool arguments from
input.context.rs_context.tool_arguments. A raw RFC822 message hides its
recipients, so it is denied while recipient domain rules are configured.
The rules see the same arguments on both gateway paths. An MCP call passes its
tool arguments. A REST call through /gateway/<name>/gmail/v1/... has its JSON
body, query string and path parameters merged into tool_arguments. A REST send
carries the composed raw message rather than To/Cc/Bcc, so it is denied
while recipient domain rules are configured.
3. 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 '{
"allowed_recipient_domains": ["acme.com"],
"blocked_recipient_domains": ["competitor.example"],
"protected_label_ids": ["Label_42_LEGAL_HOLD"]
}' \
'{dataKey:$k, payload:($s|tojson)}')"
Scope model
| Scope | Used for | |
|---|---|---|
| PBAC scopes (internal) | gmail:messages:read, gmail:messages:write, gmail:messages:send, gmail:labels:manage | Route 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 scopes | gmail.readonly, gmail.send, gmail.modify, gmail.labels | The 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.2.0 - Resource type:
urn:connector:identos:google-gmail
Supported auth modes
| Type | Details |
|---|---|
idp_passthrough | requires IdP google |
Setup fields
| ID | Label | Default | Secret? | Notes |
|---|---|---|---|---|
base_url | API base URL | https://gmail.googleapis.com | no | — |
Scopes
| Scope |
|---|
gmail:messages:read |
gmail:messages:write |
gmail:messages:send |
gmail:labels:manage |
Routes
| Method | Pattern | Scope | Resource template |
|---|---|---|---|
GET | /gmail/v1/users/me/profile | gmail:messages:read | — |
GET | /gmail/v1/users/me/messages | gmail:messages:read | — |
GET | /gmail/v1/users/me/messages/{message_id} | gmail:messages:read | gmail://message/{{message_id}} |
GET | /gmail/v1/users/me/threads | gmail:messages:read | — |
GET | /gmail/v1/users/me/threads/{thread_id} | gmail:messages:read | gmail://thread/{{thread_id}} |
POST | /gmail/v1/users/me/messages/send | gmail:messages:send | — |
POST | /gmail/v1/users/me/messages/{message_id}/modify | gmail:messages:write | gmail://message/{{message_id}} |
POST | /gmail/v1/users/me/messages/{message_id}/trash | gmail:messages:write | gmail://message/{{message_id}} |
POST | /gmail/v1/users/me/messages/{message_id}/untrash | gmail:messages:write | gmail://message/{{message_id}} |
GET | /gmail/v1/users/me/labels | gmail:messages:read | — |
POST | /gmail/v1/users/me/labels | gmail:labels:manage | — |
DELETE | /gmail/v1/users/me/labels/{label_id} | gmail:labels:manage | gmail://label/{{label_id}} |
GET | /gmail/v1/users/me/drafts | gmail:messages:read | — |
POST | /gmail/v1/users/me/drafts | gmail:messages:write | — |
MCP tools
| Name | Scope | Description |
|---|---|---|
get_profile | gmail:messages:read | Get the authenticated user's Gmail profile (email address, total messages, history ID). |
list_messages | gmail:messages:read | List messages in the authenticated user's mailbox. Supports Gmail search syntax (e.g. "from:alice newer_than:7d"). |
get_message | gmail:messages:read | Get a single message by ID. Use format=full for headers+body, format=metadata for headers only. |
list_threads | gmail:messages:read | List conversation threads. Same query syntax as list_messages. |
get_thread | gmail:messages:read | Get a single conversation thread with all its messages. |
send_message | gmail:messages:send | Send 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_labels | gmail:messages:write | Add or remove labels on a message (e.g. mark as read by removing UNREAD, archive by removing INBOX). |
trash_message | gmail:messages:write | Move a message to the Trash. Reversible for 30 days via untrash_message. |
untrash_message | gmail:messages:write | Restore a trashed message to the inbox. |
list_labels | gmail:messages:read | List all labels in the authenticated user's mailbox (both system labels and user-created). |
create_label | gmail:labels:manage | Create a new user label in the mailbox. Returns the label with its id, which modify_labels needs. |
delete_label | gmail:labels:manage | Delete a user-created label. System labels (INBOX, SENT, etc.) cannot be deleted. |
list_drafts | gmail:messages:read | List unsent draft messages in the mailbox, with their ids and message metadata. |
create_draft | gmail:messages:write | Create 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.
| Key | Type | Description |
|---|---|---|
allowed_recipient_domains | array | Outbound 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_domains | array | Outbound email domains that must never receive mail (evaluated before allowed_recipient_domains; easier to manage a small denylist than a large allowlist). |
protected_label_ids | array | System/user label IDs that modify_labels and delete_label must not touch. Useful for regulatory holds (LEGAL, LITIGATION_HOLD, etc.). |
Send feedback about this page