Skip to main content

Google Sheets

Proxy for the Google Sheets v4 REST API. Dual auth by design — like drive and calendar, the typical production setup runs the connector in one of two modes per managed resource:

  • idp_passthrough mode for human users who want an agent to read or edit spreadsheets they already own. Each request carries the caller's own OAuth grant.
  • google_sa mode uses a Workspace service account with domain-wide delegation. For each call it impersonates the calling user, so the user does not link a Google account, but the call still needs a user token. The user is the token's sub when that is an email, otherwise the user's email, and its domain must match the Workspace domain setup field. No setup field names a user to impersonate. A client_credentials call has no user and gets 502 Upstream auth failed: No user identity in context; cannot impersonate (GoogleServiceAccountProvider.java). The connector sets no Google scopes for this mode, so the gateway asks Google for drive.readonly unless the resource's upstream auth carries a scopes list.

google_sa set up from the Admin Portal fails in 0.65.0. Every call gets 502 Upstream auth failed: No Google SA key configured, because the form stores the secret reference under sa_key_env (ConnectorV1Adapter.java:316-320) and the gateway reads it only from service_account_key_env (GoogleServiceAccountProvider.java:187-196). After you create the resource, rename the key with the admin API:

NAME=sheets-sa   # the managed resource's name
curl -s -H "X-Admin-API-Key: $PBAC_ADMIN_API_KEY" "$PBAC_AS_URL/admin/api/managed-resources/$NAME" \
| jq '.upstreamAuth.service_account_key_env = .upstreamAuth.sa_key_env | del(.upstreamAuth.sa_key_env)' \
| curl -s -X PUT -H "X-Admin-API-Key: $PBAC_ADMIN_API_KEY" -H "Content-Type: application/json" \
"$PBAC_AS_URL/admin/api/managed-resources/$NAME" -d @-

To ask Google for other scopes, append | .upstreamAuth.scopes = ["<scope URL>"] to the jq filter. We ran this on a test instance with a stub token endpoint standing in for Google. After the rename, the gateway sent a signed JWT-bearer assertion with the calling user as sub and drive.readonly, or the scopes we set, as scope, and forwarded the token it got back to the upstream. A user outside the Workspace domain got 502 Email domain '...' does not match configured domain. We have not run a google_sa call through to Google.

Overview​

Typical uses:

  • An agent reads a forecast tab and summarizes the numbers (get_values, batch_get_values).
  • A reporting bot appends rows to a tracking sheet (append_values) or rewrites a range (update_values).
  • A workflow spins up a fresh spreadsheet, adds a tab, and copies a template sheet into it (create_spreadsheet, batch_update, copy_sheet).

Two upstream-shape notes specific to Sheets:

  • No list endpoint. The Sheets API can't enumerate spreadsheets; discover spreadsheet IDs via the Google Drive connector (filter on mimeType = 'application/vnd.google-apps.spreadsheet'), then operate on the ID here.
  • Deletes go through batch_update. Deleting a sheet, row, column, or range is a deleteSheet/deleteDimension/deleteRange request object on batch_update; deleting an entire spreadsheet file is the Drive connector's job. update_values/append_values carry valueInputOption in the query string (RAW vs USER_ENTERED), and ranges use A1 notation (Sheet1!A1:C10).

Default policy​

The connector ships connectors/identos.google-sheets/policy/sheets.rego (package pbac.connectors.identos.google_sheets), which holds no rules.

The IdP-routing obligation that earlier releases documented here guarded on input.connector, a field no entrypoint sets, so it never fired, and it was removed. In idp_passthrough mode the gateway enforces the Google link 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.

Blocked and read-only spreadsheets​

Blocking a specific spreadsheet is operator config on the general file-access control, not a rule in this connector. An identity source matches a spreadsheet by its own id: ids in blocked are denied on reads and writes, ids in write_blocked are denied on writes only (update_values, append_values, clear_values, batch_clear_values, batch_update, copy_sheet), so reads still work. See File access control below for the shape — an identity source pools with any label or location source governing the same connector.

File access control​

Spreadsheets can be gated by Drive Labels and/or by folder/shared-drive location — a spreadsheet is returnable via get_spreadsheet if it matches any governing source's approval (an approved label or an approved folder), and denied if it matches any source's blocked (pooled OR, deny-wins). This is configured through the shared file_access operator control, used across all Google file connectors (Docs, Sheets, Slides, Drive). Full mechanics (the pooled predicate, OR-combine and deny-wins, fail-closed rules) live in the File Access Control guide; for getting label/folder IDs and the required OAuth scopes, see the Google file access setup guide.

Configuration uses a two-level model:

  1. Define a policy per source — under pbac.operator.controls.file_access.sources, each source declares a type (label, location, or identity — the resource's own id), a required allowlist (any-of; file must match ≥1), a blocked denylist (file withheld if it matches any — always wins), and an optional write_blocked denylist that applies to mutating calls only. Approving a whole folder is a location source with the folder ID in required (Google Drive does not allow labels on folders):
curl -X PUT "http://localhost:8080/admin/api/policy-data/by-key?dataKey=pbac.operator.controls.file_access" \
-H "X-Admin-API-Key: $PBAC_ADMIN_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"dataKey": "pbac.operator.controls.file_access",
"payload": {
"sources": {
"google-drive-labels": {
"type": "label",
"required": ["<approved-label-id>"],
"blocked": ["<restricted-label-id>"]
}
},
"governed": {
"urn:connector:identos:google-sheets": ["google-drive-labels"]
}
}
}'

The call replaces the whole row, so keep the sources and governed entries of every other connector you govern in the payload.

  1. Activate per connector — under governed, list (as an array) which sources govern each connector's resource type; all listed sources pool into one OR-of-approvals, deny-wins decision — a file is returnable if it matches any source's required and withheld if it matches any source's blocked. Omitting a connector leaves it ungoverned (inert; no check). Once a source is configured and a connector is governed by it, enforcement is fail-closed: reads that cannot verify the file's values are denied (403).

The control resolves spreadsheet IDs from tool invocations to fetch labels/location; file and scope requirements are documented in the spec at docs/superpowers/specs/2026-08-11-general-file-access-control-design.md.

Scope model​

ScopeUsed for
PBAC scopes (internal)sheets:read, sheets:writeRoute authorization
Upstream OAuth scopesopenid, userinfo.email, spreadsheets, drive.metadata.readonly, drive.labels.readonly, drive.labelsGoogle-side grants the user consents to (idp_passthrough mode). google_sa mode sets none and gets drive.readonly

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

Manifest reference​

  • ID: identos.google-sheets
  • Version: 1.3.0
  • Resource type: urn:connector:identos:google-sheets

Supported auth modes​

TypeDetails
idp_passthroughrequires IdP google
google_sasetup fields: sa_key_env, domain

Setup fields​

IDLabelDefaultSecret?Notes
base_urlAPI base URLhttps://sheets.googleapis.comno—
upstream_auth.typeAuthenticationidp_passthroughnoidp_passthrough forwards each user's own Google OAuth token (recommended). google_sa uses a Workspace service account with domain-wide delegation impersonating a fixed user.
sa_key_envService account key—yesPick a secret containing the service-account JSON. Required only for the google_sa auth mode. / shown when upstream_auth.type == 'google_sa'
domainWorkspace domain—noplaceholder: example.com / Required only for the google_sa auth mode. / shown when upstream_auth.type == 'google_sa'

Scopes​

Scope
sheets:read
sheets:write

Routes​

MethodPatternScopeResource template
POST/v4/spreadsheetssheets:write—
GET/v4/spreadsheets/{spreadsheet_id}sheets:readsheets://{{spreadsheet_id}}
GET/v4/spreadsheets/{spreadsheet_id}/values/{range}sheets:readsheets://{{spreadsheet_id}}
GET/v4/spreadsheets/{spreadsheet_id}/values:batchGetsheets:readsheets://{{spreadsheet_id}}
PUT/v4/spreadsheets/{spreadsheet_id}/values/{range}sheets:writesheets://{{spreadsheet_id}}
POST/v4/spreadsheets/{spreadsheet_id}/values/{range}:appendsheets:writesheets://{{spreadsheet_id}}
POST/v4/spreadsheets/{spreadsheet_id}/values/{range}:clearsheets:writesheets://{{spreadsheet_id}}
POST/v4/spreadsheets/{spreadsheet_id}/values:batchClearsheets:writesheets://{{spreadsheet_id}}
POST/v4/spreadsheets/{spreadsheet_id}/sheets/{sheet_id}:copyTosheets:writesheets://{{spreadsheet_id}}
POST/v4/spreadsheets/{spreadsheet_id}:batchUpdatesheets:writesheets://{{spreadsheet_id}}

MCP tools​

NameScopeDescription
create_spreadsheetsheets:writeCreate a new spreadsheet. Pass the Sheets Spreadsheet resource body (e.g. {properties:{title}, sheets:[...]}).
get_spreadsheetsheets:readGet a spreadsheet's metadata and (optionally) cell data. Use the includeGridData flag sparingly — it can be large.
get_valuessheets:readRead values from a single A1 range (e.g. "Sheet1!A1:C10").
batch_get_valuessheets:readRead values from multiple A1 ranges in one call.
update_valuessheets:writeOverwrite values in an A1 range. Pass values as a 2-D array; set valueInputOption (RAW or USER_ENTERED).
append_valuessheets:writeAppend rows after the last row of a table within the given A1 range.
clear_valuessheets:writeClear values (not formatting) from a single A1 range.
batch_clear_valuessheets:writeClear values from multiple A1 ranges in one call.
copy_sheetsheets:writeCopy a single sheet from this spreadsheet into another spreadsheet.
batch_updatesheets:writeApply one or more structural updates (add/delete sheet, insert/delete rows & columns, formatting, etc.) via Sheets batchUpdate request objects. This is also how you DELETE sheets/rows/columns/ranges.