How I Built a Google Workspace Automation Tool That Saves 5 Hours a Week

How I Built a Google Workspace Automation Tool That Saves 5 Hours a Week

A full case study on building TaskNotch — an AI-powered SaaS that connects Gmail, Calendar, Sheets, and 6 more Google services into one natural language interface.

April 20, 20265 min readby Arefin Khan

Most productivity tools make you adapt to them. TaskNotch does the opposite — you tell it what you need in plain English, and it handles the rest across your entire Google Workspace.

This is the full story of how I built it, what broke, and what I'd do differently.

The Problem Worth Solving

A client came to me with a specific frustration: their team was spending 2-3 hours every Monday manually moving data between Gmail, Google Sheets, and Calendar. They'd copy meeting notes from emails into spreadsheets. They'd create calendar events from email threads. They'd send follow-up emails based on sheet data. All manual. All repetitive.

The ask: automate this. The constraint: everything already lived in Google Workspace, and they weren't moving.

What I Built

TaskNotch is a SaaS platform with a chat interface where you type natural language commands and the app executes them across your connected Google account.

Example interactions:

  • "Summarise my unread emails from this week and add the action items to my task list"
  • "Schedule a follow-up call with everyone who opened my proposal email last Tuesday"
  • "Create a Sheet from the attendees of today's meeting and send them the notes"

Under the hood, every message goes through GPT-4 function calling, which decides which Google API to call, with what parameters, and in what order.

The Stack

Frontend: Next.js 15 (App Router) + React 19 UI: Tailwind CSS + shadcn/ui Backend: Supabase (database + auth + realtime) AI: OpenRouter → GPT-4o function calling APIs: Gmail, Calendar, Sheets, Docs, Drive, Tasks, Meet, Slides Auth: Google OAuth 2.0 with incremental scopes Deployment: Vercel

The Hard Parts

1. OAuth Scope Management

Google OAuth doesn't let you request all scopes upfront without triggering a scary permissions screen that kills conversion. The solution was incremental authorization — request only the scopes needed for the current action, then layer in more as the user does more things.

const getRequiredScopes = (intent: FunctionIntent): string[] => { const scopeMap: Record<FunctionIntent, string[]> = { read_email: ['https://www.googleapis.com/auth/gmail.readonly'], send_email: ['https://www.googleapis.com/auth/gmail.send'], create_event: ['https://www.googleapis.com/auth/calendar.events'], read_sheet: ['https://www.googleapis.com/auth/spreadsheets.readonly'], write_sheet: ['https://www.googleapis.com/auth/spreadsheets'], }; return scopeMap[intent] ?? []; };

This reduced the initial permissions screen to 2 scopes instead of 9, and conversion on the OAuth step went up significantly.

2. Token Refresh at Scale

Access tokens expire after 1 hour. In a multi-user SaaS, you can't ask users to re-authenticate constantly. The fix: store refresh tokens encrypted in Supabase and silently refresh before every API call.

async function getValidToken(userId: string): Promise<string> { const { access_token, expires_at, refresh_token } = await getStoredTokens(userId); if (Date.now() < expires_at - 60_000) { return access_token; // Still valid, use it } // Refresh silently const refreshed = await oauth2Client.refreshAccessToken(refresh_token); await updateStoredTokens(userId, refreshed); return refreshed.access_token; }

3. GPT-4 Function Calling Reliability

LLMs are non-deterministic. The same input can produce different function call sequences on different runs. For a productivity tool handling real data, that's a problem.

The solution was a two-stage approach: first classify the intent (read vs write vs create), then route to a tighter prompt with stricter function definitions. Write operations always require an explicit confirmation step before execution.

const functions = [ { name: "send_email", description: "Send an email. Use only when user explicitly asks to send.", parameters: { type: "object", properties: { to: { type: "string", description: "Recipient email address" }, subject: { type: "string" }, body: { type: "string" }, }, required: ["to", "subject", "body"], }, }, // ...12 more function definitions ];

Requiring required fields on every function definition reduced hallucinated parameters by about 80% in testing.

4. Rate Limits Across 9 APIs

Each Google API has its own rate limit. Gmail allows 250 quota units per user per second. Sheets has a 100 requests per 100 seconds limit. When TaskNotch chains multiple API calls in one action, hitting a rate limit mid-chain means partial execution — which is worse than no execution.

The fix: a queue-based execution layer with exponential backoff and rollback markers so partial chains can be retried cleanly.

The Result

The client's team went from 2-3 hours of manual work on Mondays to about 20 minutes. The automation handles email triage, sheet updates, and calendar creation. The remaining 20 minutes is reviewing what the system did, which is a much better use of time.

Specific numbers:

  • 9 Google APIs connected under a single OAuth flow
  • ~40 function definitions across all supported actions
  • Sub-2 second average response time for read operations
  • Zero data loss incidents since launch

What I'd Do Differently

Use a proper queue from day one. I added the execution queue after encountering rate limit issues in production. Building it upfront would have saved 3 days of debugging.

Separate read and write permission scopes earlier. Users are much more comfortable granting read access first. Mixing read and write scopes in the initial OAuth request increased drop-off.

Log everything. When GPT-4 calls the wrong function, you need a full trace to debug it. Adding structured logging for every function call made debugging 10x faster.

The Tech That Made This Possible

None of this would work without GPT-4's function calling capability, which became reliable enough for production use in late 2024. Before that, getting an LLM to reliably call the right API with the right parameters was too unpredictable.

The Google Workspace APIs themselves are well-documented but quirky — especially around OAuth and pagination. The Gmail API's labelIds filter, for example, behaves differently depending on whether you query it via list or get. You only find these things by shipping to real users.

If You Have a Similar Problem

If your team is spending time on repetitive work between Google Workspace tools — email to sheet, email to calendar, sheet to email — this kind of automation is more buildable than most people think. The hard parts (OAuth, token refresh, rate limiting) are solved problems once you've done it once.

If you want to discuss what this would look like for your specific workflow, get in touch.