GEMINI LABJP
MODEL — Gemini 3.5 Flash is generally available as Google's top pick for agentic and coding tasksAGENT — Managed Agents enter public preview in the Gemini API, running in isolated Linux sandboxesWEBHOOK — Event-driven webhooks now cover the Batch API and long-running ops, removing pollingSECURITY — From June 19, requests from unrestricted API keys are blocked — review your key limitsDEPRECATED — Two image-preview models shut down June 25 — migrate any preview-dependent flowsCODEASSIST — Since June 18, individual Code Assist extensions and CLI stopped serving Pro/Ultra tiersMODEL — Gemini 3.5 Flash is generally available as Google's top pick for agentic and coding tasksAGENT — Managed Agents enter public preview in the Gemini API, running in isolated Linux sandboxesWEBHOOK — Event-driven webhooks now cover the Batch API and long-running ops, removing pollingSECURITY — From June 19, requests from unrestricted API keys are blocked — review your key limitsDEPRECATED — Two image-preview models shut down June 25 — migrate any preview-dependent flowsCODEASSIST — Since June 18, individual Code Assist extensions and CLI stopped serving Pro/Ultra tiers
Articles/Workspace
Workspace/2026-06-29Advanced

Keeping Apps Script + Gemini Automations on Least Privilege: Explicit Scopes and Catching Scope Creep

Apps Script automations that call Gemini quietly accumulate OAuth scopes. Here is how to declare explicit scopes in appsscript.json, catch scope creep in CI, and avoid forcing every user to re-consent.

Apps Script3Gemini API155OAuthSecurity3Google Workspace12

Premium Article

One morning I made a tiny edit to a Sheets automation that had been running quietly for about six months, redeployed it, and was met with a re-authorization screen. The line item read: "Read, compose, send, and permanently delete all your email from Gmail." All I had changed was appending one row to a sheet. I had not touched Gmail at all.

It asked for that permission anyway.

The reason was mundane. Months earlier I had called GmailApp once during a test, commented the line out, and forgotten to delete it. Apps Script statically scans your code and infers scopes from APIs that look used. A single line inside a comment was enough for it to request one of the broadest scopes available.

When you run several automations across Workspace as an indie developer, these auto-inferred scopes quietly swell over time. A script in production ends up holding read and write permissions it never actually needs. It is a dull but heavy liability: it widens the blast radius of any incident without you ever deciding to.

This article is about cutting that liability. Using a typical automation that spans Gmail, Sheets, and the Gemini API, we will declare the minimum scopes in appsscript.json, catch creep in CI, and avoid the re-consent accidents that scope changes cause.

Why auto-inferred scopes are dangerous

Apps Script has two ways to decide scopes. If you declare nothing, it infers them from your code. If you list them under oauthScopes in appsscript.json, inference stops and only the scopes you declared are requested.

Auto-inference is dangerous because three problems stack on top of each other.

ProblemWhat actually happens
It grabs oversized scopesA single GmailApp.search() pulls in "full read/write/delete of mail." You wanted read-only, but you now hold delete.
Dead code grants powerCalls left in comments or unreachable branches still feed inference. You request permissions you never exercise.
Change is invisibleNothing records who widened a permission or when. It never enters review, so creep goes unnoticed.

The principle of least privilege is that code holds only the permissions it needs right now. Auto-inference is fundamentally at odds with that.

The automation we will use

Let's work from a concrete setup, close to one I actually run:

  • Read unread mail under a specific Gmail label (never send, never delete)
  • Pass the body to the Gemini API to summarize and classify
  • Append the result to a single spreadsheet

The permissions this automation truly needs come down to three:

  1. Read Gmail, and nothing more (gmail.readonly)
  2. Read/write the one spreadsheet it is bound to (spreadsheets.currentonly)
  3. Outbound HTTP requests, to call the Gemini API (script.external_request)

No send permission. No Drive-wide permission. Left to inference, send and delete rights creep right in.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Step-by-step way to declare oauthScopes in appsscript.json and shut off the broad scopes Apps Script auto-assigns
A complete, copy-ready CI script that diffs declared scopes against an allowlist and fails on creep
How to roll out scope changes in stages so you never force every user into a surprise re-consent
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Workspace2026-04-09
Google Workspace × Gemini API Automation: Production Notes on 12 Apps Script Patterns
12 Gemini API + Apps Script patterns for Gmail, Docs, Sheets, and Calendar automation—plus the production snags I hit running this across four sites and an app support inbox: swallowed 429s, JSON code fences, the 6-minute cap, and flash-vs-pro routing, with measured numbers.
API / SDK2026-05-18
Why Your Apps Script Stops Mid-Batch When Calling the Gemini API — UrlFetchApp Timeouts and the 6-Minute Execution Limit
When Apps Script calls the Gemini API, two limits collide: UrlFetchApp's response timeout and the 6-minute script runtime cap. Here is how to tell them apart and how I work around them with chunking, checkpoints, and time-based triggers.
Workspace2026-05-06
Google Workspace Gemini Features Suddenly Stopped Working: Admin and User Troubleshooting Guide
When Gemini for Google Workspace unexpectedly disappears or stops working, knowing where to look matters. This guide walks through 7 common causes—from license expiration to browser cache—split by admin-side and user-side fixes.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →