HAMMAD YOUSUF

AI AGENTS

5 min read · 2026-08-09

How to connect an AI agent to Google Sheets and Calendar (service accounts done right)

TL;DR

To connect an AI agent to Google Sheets and Calendar, create a service account in Google Cloud Console, store its JSON key in secret storage, enable the Sheets and Calendar APIs with the narrowest scopes, and — the step everyone misses — share each Sheet and Calendar with the service account's email address like any human collaborator. Most 403 and 404 errors are sharing problems, not code problems. For Calendar writes, always run a freebusy check and normalise timezones first.

The fastest way to connect an AI agent to Google Sheets and Calendar is a service account: create it in Google Cloud Console, download its JSON key into secret storage, enable the Sheets and Calendar APIs with minimal scopes, and then do the step nearly every tutorial skips — share the actual Sheet and Calendar with the service account's email address, exactly as you would with a human collaborator. Miss that last step and you get the 403 error that has sent thousands of developers into their code looking for a bug that is not there. This is the exact setup running behind my production agents — the CRM-update and meeting-prep agents on this site both talk to Google Workspace this way — written as the checklist I wish I had had.

What a service account actually is

A service account is a machine identity: its own "user" with its own email address (something like agent-name@your-project.iam.gserviceaccount.com), authenticating with a cryptographic key instead of a password, with no login screen and no consent popup. This is fundamentally different from OAuth user login, where your code acts as a specific human who clicked "Allow". The distinction that trips up most first attempts: because the service account is its own user, it has access to nothing by default. It cannot see your Sheets. It cannot see your Calendar. It is a stranger until you explicitly invite it — which is the whole key to the setup, and we will get there in step 3. Use a service account when an agent acts as itself around resources you control; use OAuth when your product must act on behalf of arbitrary end users.

Step 1 — create the service account and protect the key

In Google Cloud Console: create (or pick) a project, go to IAM & Admin → Service Accounts, create one with a name that says what it does, and skip the optional project-role grants — Sheets and Calendar access will come from sharing, not IAM roles. Then create a key (JSON type) and it downloads once. That file is a credential that never expires on its own, so treat it accordingly: it goes in an environment variable or a secret manager, never in the repository. Add it to .gitignore before you do anything else, because a service-account key committed to git is compromised the moment the repo is shared — and if it ever does land somewhere it should not, rotate it immediately: create a new key, deploy it, delete the old one. Rotating periodically even without an incident is cheap insurance.

Step 2 — enable the APIs and choose narrow scopes

In the console's API library, enable the Google Sheets API and the Google Calendar API for your project — forgetting this produces its own distinctive error telling you the API has not been used in the project. Then choose scopes deliberately. If the agent only reads a Sheet, request spreadsheets.readonly, not the full spreadsheets scope; if it only manages events, calendar.events rather than the full calendar scope. Scope minimalism is not bureaucratic hygiene: a leaked key with read-only Sheets scope is a much smaller incident than one with write access to every calendar, and narrow scopes make any future security review of your agent dramatically easier. Request the least you need now; widening later is a one-line change.

Step 3 — share the Sheet and Calendar with the service account

ONE TACTIC A WEEK

One tactic a week. No filler.

This is the step that causes the majority of "my code is broken" moments, so here it is plainly: your service account must be explicitly shared on every resource it touches. For a Sheet, open it and share it with the service account's email address — Viewer for read-only, Editor for writes — exactly as if inviting a colleague. For a Calendar, open the calendar's settings, find "Share with specific people", add the service account's email, and pick the permission level ("Make changes to events" for an agent that books). There is no workspace-wide access by default and no hidden admin backdoor: one service account can access as many Sheets and Calendars as you like, but each one must be individually shared. When a teammate creates a new Sheet and the agent mysteriously cannot see it, this is why.

Step 4 — authenticate from your agent code

With googleapis in Node/TypeScript, the pattern is: construct a GoogleAuth (or JWT) client with the service-account credentials — supplied via an environment variable pointing at the key file, or the key's client_email and private_key fields injected from your secret manager — and the scopes you chose in step 2, then pass that auth client to google.sheets({ version: "v4", auth }) or google.calendar({ version: "v3", auth }). The library handles the JWT signing and token exchange; you never manage access tokens by hand. From there, reads and writes are plain calls: spreadsheets.values.get and append for Sheets, events.insert for Calendar. Two production habits worth adopting from day one: keep the spreadsheet and calendar IDs in configuration rather than scattered through code, and log Google's full error payloads — they are verbose JSON, and the verbosity is exactly what you want when something fails at 2am.

Step 5 — the Calendar-specific gotchas: freebusy and timezones

Sheets integration is mostly done at this point; Calendar has two traps left. First, never write an event blind — call the freebusy endpoint for the target window first and only insert if it is clear, because an agent that double-books is worse than no agent. Second, timezones: Calendar stores instants, and if your agent computes "tomorrow at 3pm" on a UTC server for a user in Dubai, you will create meetings four hours wrong. Normalise every datetime to an explicit timezone (for me, Asia/Dubai) before writing, and pass the timeZone field on the event rather than hoping defaults align. The wrong-time bug is nastier than the double-booking bug because nothing errors — the write succeeds, and the failure surfaces as a missed meeting. A confirm-before-write step, where the agent states the exact slot in local time before committing, catches both classes of bug and costs one message.

Common errors and what they actually mean

The decode table from production. 403 "The caller does not have permission": the resource is not shared with the service account's email — go to step 3; it is almost never your code. 404 "not found" on an ID you can see in your own browser: usually the same sharing problem wearing a different mask — Google returns 404 rather than confirming a resource exists to an identity with no access. 403 with "API has not been used in project": step 2, the API is not enabled. 429 or quota errors: you are hitting per-minute limits, so batch reads (one values.batchGet beats fifty gets) and add exponential backoff. invalid_grant on auth: malformed or revoked key — commonly private-key newlines mangled by an env-var layer; the \n sequences must survive into the actual key string. When in doubt, read the full error payload — Google's errors are wordy but they usually name the resource, the identity and the missing permission, which is everything you need.

Hammad Yousuf

AI Marketing Automation Engineer · Dubai, UAE

FAQ

Common questions

What is the difference between a Google service account and OAuth for an AI agent?

A service account is a machine identity with its own email that authenticates with a key — right for agents acting as themselves on resources you control. OAuth acts on behalf of a specific human who granted consent — right when your product must access arbitrary users' data.

Why does my AI agent get a 403 error accessing a Google Sheet?

Almost always because the Sheet was never shared with the service account's email address. A service account is its own user with access to nothing by default — share the Sheet with it like a human collaborator and the 403 disappears. The same cause often masquerades as a 404.

Is it safe to give an AI agent write access to a Google Calendar?

Yes, with guardrails: scope access to a specific shared calendar rather than the whole Workspace, run a freebusy check before every write, normalise timezones explicitly, and use a confirm-before-write step so a human sees the exact slot before it is committed.

Can one service account access multiple Sheets and Calendars?

Yes — one service account can be shared onto any number of Sheets and Calendars, and that per-resource sharing is the only access it has. There is no workspace-wide access by default, which is a security feature: the agent can touch exactly what you invited it to and nothing else.