Leaders Insights
Leaders Insights

Stay at the top of your field, a little every day.

DomainsMarketingDataFinanceAI
ResourcesLearnTestToolsBlogGlossary
© 2026 Leaders Insights — All rights reserved.
Tracks/AI Essentials/Gemini & Google AI/Agents and automation/Automating with apps script and workspace
2/4+180 XP

Agents and automation

1Agents on Google AI: the agent development kit+1902Automating with apps script and workspace+1803Guardrails: permissions, review, and cost+1504Multi-agent orchestration with the ADK+210

Automating with apps script and workspace

# Automating with apps script and workspace

Google Apps Script plus the Gemini APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → can turn Workspace from a place you do work into a place that does work for you: auto-label incoming email by intent, draft replies, or compile a weekly Sheet report from raw rows without you touching it. The unlock is that Apps Script already lives inside Gmail, Sheets, Drive, and Calendar with authenticated access to your data, and Gemini gives it judgment. You wire the two together with a single HTTP call.

This lesson shows the wiring, a concrete working example, and the guardrails that matter once the script runs unattended on a trigger.

Why Apps Script is the right glue

Apps Script is Google's serverless JavaScript runtime built into Workspace. You do not provision anything. A script can read your Gmail threads (GmailApp), write to a spreadsheet (SpreadsheetApp), and run on a time-based trigger, all under your account's permissions.

What it does not have natively is reasoning. That is the Gemini part. You call the Gemini APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → from inside the script with
UrlFetchApp
, send it text, and get structured output back.

Two ways to authenticate the call:

  • API key from [Google AI Studio](https://aistudio.google.com): fastest to start. Good for personal automations and prototypes.
  • Vertex AI with a service account: the right choice when the automation belongs to an organization, needs IAM controls, data residency, or VPC boundaries. See cloud.google.com/vertex-ai.

For a personal weekly report, the APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → key path is fine. We will use that and flag where you would switch.

The example: a weekly support-email report

The goal: every Monday at 7am, scan last week's emails labeled support, have Gemini classify each by category and urgency, and append a tidy summary row to a tracking Sheet.

The trick that makes this reliable is structured output: you ask Gemini to return JSON matching a schemaschemaA schema is the formal blueprint that defines how data is structured, named, typed, and related within a database, file, or message.View full definition → instead of prose. The Gemini APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → supports a responseSchema so the model is constrained to valid JSON. That removes the fragile "parse the model's paragraph" step.

The classification call

Here is the core: one function that sends a batch of email subjects and snippets and gets back typed classifications.

javascript
const GEMINI_KEY = PropertiesService.getScriptProperties().getProperty('GEMINI_KEY');
const MODEL = 'gemini-2.5-flash';

function classifyEmails(emails) {
  const url = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${GEMINI_KEY}`;
  const prompt = 'Classify each support email. Categories: billing, bug, feature_request, other. ' +
    'Urgency: low, medium, high.\n\n' +
    emails.map((e, i) => `[${i}] Subject: ${e.subject}\nSnippet: ${e.snippet}`).join('\n\n');

  const payload = {
    contents: [{ parts: [{ text: prompt }] }],
    generationConfig: {
      responseMimeType: 'application/json',
      responseSchema: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            index: { type: 'integer' },
            category: { type: 'string', enum: ['billing', 'bug', 'feature_request', 'other'] },
            urgency: { type: 'string', enum: ['low', 'medium', 'high'] }
          },
          required: ['index', 'category', 'urgency']
        }
      }
    }
  };

  const res = UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  });

  if (res.getResponseCode() !== 200) {
    throw new Error(`Gemini API ${res.getResponseCode()}: ${res.getContentText()}`);
  }
  const body = JSON.parse(res.getContentText());
  return JSON.parse(body.candidates[0].content.parts[0].text);
}

Notes a senior engineer would care about:

  • The key lives in Script Properties, never in source. Set it once under Project Settings, or with PropertiesService.getScriptProperties().setProperty('GEMINI_KEY', '...').
  • gemini-2.5-flash is the right tier here. Flash is fast and cheap, and classification is exactly its sweet spot. Reserve Pro for tasks needing deeper reasoning (long synthesis, hard multi-step judgment). Check current model names at ai.google.dev before deploying, since tiers evolve.
  • muteHttpExceptions: true lets you read the error body instead of getting a generic throw. You will want that text in your logs.
  • Batch the emails into one call. Sending one request per email burns quota and time. One prompt with all of last week's snippets is far cheaper and the schemaschemaA schema is the formal blueprint that defines how data is structured, named, typed, and related within a database, file, or message.View full definition → keeps results aligned by index.

Wiring it into gmail and sheets

The orchestration around the call is plain Apps Script:

javascript
function weeklyReport() {
  const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
  const query = `label:support after:${Utilities.formatDate(since, 'GMT', 'yyyy/MM/dd')}`;
  const threads = GmailApp.search(query, 0, 50);

  const emails = threads.map(t => {
    const msg = t.getMessages()[0];
    return { subject: msg.getSubject(), snippet: msg.getPlainBody().slice(0, 400) };
  });
  if (emails.length === 0) return;

  const results = classifyEmails(emails);

  const sheet = SpreadsheetApp.openById('YOUR_SHEET_ID').getSheetByName('Weekly');
  const counts = { billing: 0, bug: 0, feature_request: 0, other: 0, high: 0 };
  results.forEach(r => {
    counts[r.category]++;
    if (r.urgency === 'high') counts.high++;
  });
  sheet.appendRow([new Date(), emails.length, counts.billing, counts.bug,
    counts.feature_request, counts.other, counts.high]);
}

Then set a trigger so it runs unattended: in the editor, click the clock icon (Triggers), or do it in code once:

javascript
function installTrigger() {
  ScriptApp.newTrigger('weeklyReport')
    .timeBased().onWeekDay(ScriptApp.WeekDay.MONDAY).atHour(7).create();
}

Run installTrigger once. From then on weeklyReport fires every Monday morning with no human in the loop. That is the whole point, and also exactly where the risk begins.

Guardrails for unattended runs

A script that runs while you sleep, holds your Gmail scope, and calls an external model needs discipline. The failure modes are different from interactive promptingpromptingPrompt engineering is the practice of designing and refining text inputs to guide large language models toward accurate, relevant, and reliable outputs.View full definition → because nobody is watching the output.

1. Cap the blast radius

This example only reads Gmail and appends to a Sheet. It never deletes, never sends, never modifies threads. Keep automations read-and-append by default. The moment a script can send email or delete files unattended, a bad model output or a bug becomes an action you cannot undo.

If you do progress to auto-replies or auto-labeling that moves mail, add a dry-run mode: write the proposed action to a Sheet for a week and review it before letting the script execute for real.

2. Request only the scopes you need

Apps Script infers OAuth scopes from the APIs you call, but you should pin them explicitly in the manifest so a stray new function cannot silently widen access. In appsscript.json:

json
{
  "oauthScopes": [
    "https://www.googleapis.com/auth/gmail.readonly",
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/script.external_request"
  ]
}

gmail.readonly means even a buggy version literally cannot send or delete. That single line is your strongest guardrail.

3. Never trust model output as control flow

The model returns categories. Validate them against your enum before using them as object keys, exactly as the schemaschemaA schema is the formal blueprint that defines how data is structured, named, typed, and related within a database, file, or message.View full definition → enforces. If a value arrives that you did not expect, log it and skip rather than crashing the whole run or writing garbage. Structured output makes this rare, but defense in depth is cheap.

4. Handle quota, latency, and partial failure

UrlFetchApp calls fail sometimes. Apps Script also has execution-time limits per run. Wrap the APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → call in a retry with backoff for transient errors, and design the job so a failure on Monday does not silently lose Monday's data:

javascript
function fetchWithRetry(url, options, tries = 3) {
  for (let i = 0; i < tries; i++) {
    const res = UrlFetchApp.fetch(url, options);
    if (res.getResponseCode() < 500) return res;
    Utilities.sleep(1000 * Math.pow(2, i));
  }
  throw new Error('Gemini API failed after retries');
}

5. Watch cost and privacy

Flash is inexpensive, but a runaway loop or a trigger misconfigured to run hourly adds up. Set a billing alert on the project. On privacy: you are sending email content to an APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition →. For personal use the AI Studio key is acceptable, but for anything touching customer or employee data, move to Vertex AI, where you get enterprise data handling, IAM, and the ability to keep data in a region. That is not a nice-to-have for organizational deployments; it is the line between a prototype and something compliance will approve.

Build a Gmail automation with Apps Script and Gemini

Watch on YouTube

Knowledge check

1. According to the lesson, what does the combination of Apps Script and the Gemini API fundamentally provide that neither has fully on its own?

2. Why does the lesson recommend using structured output (a responseSchema) when calling Gemini from the script?

3. In which scenario would you prefer Vertex AI with a service account over a Google AI Studio API key?

MULTIPLE CHOICE

4. Select ALL statements that correctly describe why Apps Script is described as 'the right glue' for this kind of automation.

Select all the correct answers.

MULTIPLE CHOICE

5. Select ALL tasks the lesson presents as things this Apps Script + Gemini automation can do for you.

Select all the correct answers.

When to reachreachThe number of unique people exposed to your message in a given period. Unlike impressions, reach counts each person once, no matter how often they see it.View full definition → past Apps Script

Apps Script is perfect for "this lives inside Workspace and does a bounded task on a schedule." It is the wrong tool once you need true multi-step agent behavior: tools that call other tools, planning, memory across steps, or orchestration you want to test and version like real software.

At that point the Gemini ecosystem gives you better-fit options:

  • [Agent Development Kit (ADK)](https://google.github.io/adk-docs/): an open-source framework for building agents with tools, planning, and multi-agent orchestration, deployable on your own infrastructure or Vertex AI Agent Engine. Use it when the logic outgrows a single script.
  • Gemini CLI and Gemini Code Assist: for developer-side automation in your terminal and IDE, not Workspace document workflows.
  • Gems: for reusable, instruction-tuned assistants inside the Gemini app, when a human is in the loop and you do not need code at all.

The decision rule: if the task is "read some Workspace data, ask Gemini one focused question, write the result somewhere in Workspace, on a schedule," Apps Script wins on simplicity. If the task involves an agent making decisions about which actions to take across many steps, graduate to ADK on Vertex AI.

A pattern worth stealing: grounding the report

You can make the weekly summary smarter by having Gemini draft a short narrative of trends, not just counts. Add a second call that takes the week's counts plus the previous week's row and asks for a two-sentence "what changed" note appended to the Sheet. Keep that prompt tightly scoped and still schemaschemaA schema is the formal blueprint that defines how data is structured, named, typed, and related within a database, file, or message.View full definition →-bound (a single summary string field) so it stays predictable in an unattended run. Small reasoning, big readability gain, still cheap on Flash.

Key Takeaways

  • Apps Script is the glue, Gemini is the judgment. Read Workspace data with native services, call the Gemini APIAPIApplication Programming Interface: a standardised interface that lets applications communicate and exchange data without knowing each other's internal workings.View full definition → with UrlFetchApp, write results back, all under your own auth and a time-based trigger.
  • Use structured output (`responseSchema`) and batch your requests. Constrained JSON removes fragile parsing, and one batched call per run beats one call per item on cost, speed, and quota.
  • Make read-and-append the default and pin minimal OAuth scopes. gmail.readonly in appsscript.json is a stronger safety guarantee than any code comment. Add a dry-run review before any script sends, moves, or deletes.
  • Engineer for unattended failure: retries with backoff, validation against your enums, billing alerts, and logged errors you can actually read.
  • Graduate deliberately. Stay on Apps Script for bounded scheduled tasks; move to ADK on Vertex AI when you need real agent orchestration or enterprise data controls.

Previous

Agents on Google AI: the agent development kit

Next

Guardrails: permissions, review, and cost