```html

Automating Event Scheduling and Service Dispatch: Replacing Google Apps Script with Lambda-Driven Calendar APIs

What Was Done

During this session, we migrated event synchronization and scheduling workflows away from fragile Google Apps Script polling mechanisms toward a robust Lambda-driven calendar API architecture. The primary achievements were:

  • Deployed a new CalendarSync.gs replacement that uses direct Lambda invocation instead of timed triggers
  • Created a centralized calendar event API endpoint backed by Lambda, eliminating polling delays and reducing Google API quota pressure
  • Built a dispatch automation system for service coordination tasks (boat cleaning scheduling)
  • Integrated calendar operations into the existing dashboard infrastructure for real-time status tracking

Technical Architecture: From Polling to Event-Driven

The original implementation relied on Google Apps Script's time-based triggers, which polled external calendar sources at fixed intervals. This approach had three critical problems:

  • Latency: Events could take up to 30 minutes to sync depending on polling interval
  • API quota exhaustion: Repeated polling burned Google Calendar API quotas even when no changes occurred
  • Operational invisibility: No visibility into sync failures or trigger execution within the dashboard

The new architecture inverts this model. Instead of Apps Script polling, we:

  1. Expose calendar operations through a Lambda function with explicit action names
  2. Call Lambda directly from our dashboard via API Gateway endpoints
  3. Track all calendar state changes in the dashboard task system
  4. Eliminate polling in favor of on-demand invocation

Infrastructure Changes

Lambda Function Updates

The existing Lambda function (name abstracted for security) was enhanced to support multiple calendar actions. Key actions implemented:

  • add-calendar-event — Adds single or batch events to Google Calendar
  • list-calendar-events — Queries calendar within date range, returns JSON
  • sync-external-calendar — Imports events from iCal feeds (GetMyBoat, Boatsetter schedules)
  • remove-calendar-event — Deletes events by ID

Each action accepts a JSON payload with parameters like eventName, startTime, endTime, calendarId, and externalSource.

API Gateway Integration

The Lambda function is exposed via API Gateway v2 endpoints. The calendar operations endpoint structure:

POST /api/v1/calendar/{action}
Headers: Authorization: Bearer {dashboard-token}
Body: {
  "action": "add-calendar-event",
  "eventName": "Sea Scout Wednesday Hold",
  "startTime": "2025-04-30T18:00:00Z",
  "endTime": "2025-04-30T19:30:00Z",
  "calendarId": "primary"
}

This replaces the previous pattern of calling Apps Script endpoints with URLFetchApp.

Google Calendar API Credentials

Credentials are stored in the Lambda environment (not in GAS), using service account authentication. This centralizes credential management and enables rotation without updating multiple GAS projects.

CalendarSync.gs Replacement Strategy

The file /Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/CalendarSync.gs was updated to act as an administrative interface rather than a polling daemon:

  • Removed: All setTrigger() calls and time-based execution handlers
  • Removed: Direct Calendar.Events.insert() calls (now delegated to Lambda)
  • Added: Functions that construct properly-formatted payloads for the Lambda API
  • Added: Request logging that records sync attempts in a Google Sheet for audit trails

The key decision here: GAS now serves as a UI for manually triggering syncs or viewing logs, not as an autonomous scheduler. This improves debuggability because every calendar action is explicitly logged.

Service Dispatch System: Boat Cleaning Example

To handle operational tasks like boat cleaning coordination, we created:

dispatch_boat_cleaner.py

This Python script sits in /Users/cb/Documents/repos/tools/ and handles the workflow:

  • Reads boat availability from GetMyBoat/Boatsetter iCal feeds via the Lambda sync-external-calendar action
  • Identifies gaps in the cleaning schedule
  • Generates dispatch instructions with time windows and contact info
  • Logs dispatch records to CloudWatch for downstream integration

deploy_inbox_scraper.sh

A deployment wrapper that:

#!/bin/bash
# Load environment (repos.env contains API endpoints, tokens)
source /Users/cb/Documents/repos/repos.env

# Invoke dispatch script
python3 /Users/cb/Documents/repos/tools/dispatch_boat_cleaner.py \
  --calendar-api-endpoint "$CALENDAR_API_URL" \
  --auth-token "$DASHBOARD_TOKEN" \
  --output-format json

# Log results to dashboard
curl -X POST "$DASHBOARD_API_URL/tasks/t-boat-dispatch/notes" \
  -H "Authorization: Bearer $DASHBOARD_TOKEN" \
  -d '{"note": "Dispatch script executed, see CloudWatch logs"}'

This script is typically triggered by a cron job or manual invocation when boat schedules need reconciliation.

Dashboard Integration

Calendar events are now surfaced in the dashboard task system. When a Lambda calendar action succeeds, the response includes:

  • Event IDs (for future deletion/modification)
  • Sync timestamps
  • Error details if the action failed

This information is logged to task cards (e.g., t-21de9456, t-a69ba26b) so that stakeholders see the status without querying Google Calendar directly.

Key Decisions and Rationale

Why Lambda Over Apps Script for Calendar Sync?

Apps Script is excellent for UI automation and Google Workspace integration, but poor for backend orchestration. Lambda gives us:

  • Proper error handling and retry logic (CloudWatch visibility)
  • Credential isolation from GAS audit logs
  • Scalability for batch operations (adding 50 events in parallel)
  • Integration with existing dashboard authentication (IAM roles)

Why iCal Polling via Lambda Instead of Direct OAuth?

GetMyBoat and Boatsetter expose schedules via iCal feeds (read-only URLs). While less "pure" than OAuth, this approach:

  • Requires zero account-specific credentials (feed URL only)
  • Avoids OAuth token rotation complexity
  • Reduces