Automating Event Calendar Sync and Service Dispatch: Building a Lambda-Backed Calendar Integration System
Managing event calendars across multiple platforms is a persistent operational challenge, especially when coordinating bookings from third-party services like GetMyBoat and Boatsetter. This session focused on replacing a deprecated Google Apps Script-based calendar sync with a Lambda-backed system and implementing a dispatch automation pipeline for service coordination.
The Problem: Manual Calendar Management at Scale
The existing infrastructure relied on Google Apps Script (GAS) to manually poll external calendar feeds and sync events. This approach had several drawbacks:
- GAS execution limits and unreliable polling intervals
- No centralized event storage or audit trail
- Manual email notifications for service dispatch
- Tight coupling between calendar sync and email delivery
- Difficulty scaling across multiple event sources
Architecture: Lambda + API Gateway + Google Calendar API
The new system decouples calendar synchronization from notification delivery by using AWS Lambda as the primary orchestration layer:
┌─────────────────────────────────────────────────┐
│ External Calendars (GetMyBoat, Boatsetter) │
└──────────────┬──────────────────────────────────┘
│ iCal feeds
▼
┌─────────────────────────────────────────────────┐
│ AWS Lambda: CalendarSync Handler │
│ - Parse iCal feeds │
│ - Transform to Google Calendar events │
│ - Deduplicate & conflict check │
│ - Dispatch service notifications │
└──────────────┬──────────────────────────────────┘
│ Google Calendar API
▼
┌─────────────────────────────────────────────────┐
│ Google Calendar (Primary Event Store) │
└─────────────────────────────────────────────────┘
File Structure:
/Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/CalendarSync.gs— Primary Lambda handler function/Users/cb/Documents/repos/tools/dispatch_boat_cleaner.py— Service dispatch script for cleaning coordination/Users/cb/Documents/repos/tools/campaign_scheduler.py— Generic event scheduling engine
CalendarSync Lambda Function: Design & Implementation
The Lambda function implements a state machine pattern with the following action handlers:
add-calendar-event— Insert event into Google Calendar with conflict detectionlist-calendar-events— Query events by date range and optional filtersdispatch-service— Trigger external service notifications (email, SMS, webhooks)sync-external-feeds— Fetch and parse iCal feeds from GetMyBoat/Boatsetter
Key Implementation Details:
# Lambda handler entry point
def lambda_handler(event, context):
action = event.get('action')
params = event.get('params', {})
if action == 'add-calendar-event':
return add_event_to_calendar(params)
elif action == 'list-calendar-events':
return query_calendar(params)
elif action == 'dispatch-service':
return dispatch_notification(params)
else:
return {'status': 'error', 'message': f'Unknown action: {action}'}
The function uses the Google Calendar API client library to authenticate via a service account key stored in Lambda environment variables (never hardcoded). Events are added with:
- Idempotency keys in the event description to prevent duplicate syncs
- Source attribution (GetMyBoat, Boatsetter, manual entry) in event metadata
- Conflict detection — before inserting, query for overlapping bookings on the same resource
- Automatic color coding — different calendar colors by event type (booking, internal hold, service window)
Dispatch Automation: The dispatch_boat_cleaner.py Pipeline
Service dispatch is handled by a Python-based task scheduler that runs on a cron trigger:
File: /Users/cb/Documents/repos/tools/dispatch_boat_cleaner.py
Key functions:
- get_upcoming_events(calendar_id, days_ahead=3)
- determine_service_needs(event, resource_config)
- send_dispatch_email(service_provider, event_details)
- log_dispatch_to_dynamodb(event_id, dispatch_record)
Workflow:
- Lambda queries Google Calendar for all events in the next 3 days
- For each event, determine required services (cleaning, provisioning, crew assignments)
- Check service provider availability (stored in DynamoDB:
ServiceProviderstable) - Send dispatch email via SES to assigned provider with event details, timing, special instructions
- Log dispatch record to DynamoDB with status and timestamp for audit trail
Integration with Campaign Scheduling
A separate campaign scheduler was implemented to handle bulk event operations and marketing blasts:
File: /Users/cb/Documents/repos/tools/campaign_scheduler.py
File: /Users/cb/Documents/repos/tools/campaign_schedule.json
campaign_schedule.json structure:
{
"campaigns": [
{
"name": "rady_shell_weekly_blast",
"event_source": "calendar",
"filter": "event_type=promotional",
"email_template": "rady_shell_blast1.html",
"recipients": "marketing_list",
"dispatch_window": "-7 days",
"retry_policy": "exponential_backoff"
}
]
}
This decouples event scheduling from marketing campaigns, allowing independent tuning of each pipeline.
Deployment Strategy: Layered Push Model
CalendarSync.gs Deployment:
# Via Google Apps Script CLI (clasp)
$ cd /Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/
$ clasp push
# Verifies .clasp.json project binding
Project ID: [from .clasp.json]
Deployed to: https://script.google.com/macros/d/[deployment-id]/usercallable
Python Scripts Deployment:
# Via deployment shell scripts
$ bash /Users/cb/Documents/repos/tools/deploy_campaign_scheduler.sh
$ bash /Users/cb/Documents/repos/tools/deploy_inbox_scraper.sh
# Creates zip archive, uploads to S3, updates Lambda function code
# S3 bucket: [deployment-bucket-name] (from env config)
# Lambda function: [calendar-sync-function-name] (from repos.env)
Infrastructure Components
AWS Resources: