Automating Distributed Event Operations: Calendar Sync, Email Dispatch, and Booking Platform Integration
This session involved building out automated infrastructure for managing distributed event operations across multiple booking platforms, calendar systems, and communication channels. The work focused on replacing manual Google Apps Script workflows with serverless Lambda functions, implementing intelligent email dispatch systems, and creating a unified calendar sync layer that bridges GetMyBoat, Google Calendar, and internal event management.
The Problem Space
The organization manages events across multiple channels—web properties (queenofsandiego.com, carole.dangerouscentaur.com, quickdumpnow.com), third-party booking platforms (GetMyBoat, Boatsetter), and Google Calendar. Previously, calendar synchronization and event notifications relied on manual Google Apps Script workflows with polling intervals and email-based handoffs. As event volume increased, this became a bottleneck.
Architecture: Lambda-Based Event Pipeline
The core infrastructure centers on AWS Lambda functions deployed via CloudFormation, with API Gateway v2 providing HTTP endpoints. The primary function lives at:
/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py
This function acts as a unified event broker, supporting multiple actions:
list-calendar-events— Query Google Calendar API for a date rangeadd-calendar-event— Insert events with conflict detectionget-platform-bookings— Fetch reservations from GetMyBoat iCal feedssync-platforms— Bi-directional sync between platforms
The Lambda function is invoked via API Gateway with bearer token authentication, allowing dashboard and backend systems to query calendar state without direct Google API credentials. This pattern centralizes credential management—credentials live in Lambda environment variables, never in client code.
Calendar Sync Implementation
Google Apps Script code at CalendarSync.gs was updated to use the Lambda API instead of direct Google API calls. The key change involved:
// Before: Direct Google Calendar API calls with inline credentials
CalendarApp.getDefaultCalendar().createEvent(title, start, end);
// After: Lambda API call with bearer token
const response = UrlFetchApp.fetch(
`${LAMBDA_API_ENDPOINT}/calendar/add-event`,
{
method: 'post',
headers: { 'Authorization': `Bearer ${DASHBOARD_TOKEN}` },
payload: JSON.stringify({ title, start, end, description }),
muteHttpExceptions: true
}
);
This decoupling allows the GAS script to remain lightweight while delegating OAuth complexity to Lambda. The polling interval was increased from 5 minutes to 15 minutes, reducing API quota consumption while maintaining operational responsiveness.
Event Dispatch System
Two complementary Python scripts handle outbound communications:
Campaign Scheduler (/Users/cb/Documents/repos/tools/campaign_scheduler.py):
- Reads event manifest from
campaign_schedule.json - Renders HTML templates (stored in
/tools/templates/) - Dispatches via SES with bounce/complaint tracking
- Logs delivery state to DynamoDB for audit trail
Platform Inbox Scraper (platform_inbox_scraper.py):
- Polls GetMyBoat and Boatsetter messaging APIs
- Converts platform messages to standardized JSON schema
- Stores in S3 for dashboard consumption
- Triggers Lambda for automated response routing
Both systems use environment variable injection (via repos.env) and are deployed as cron jobs via CloudWatch Events. This keeps infrastructure-as-code out of the application layer.
Email Template Management
HTML email templates are versioned in git and deployed to S3:
/Users/cb/Documents/repos/tools/templates/rady_shell_blast1.html
/Users/cb/Documents/repos/sites/quickdumpnow.com/marketing/templates/qdn_blast1.html
The scheduler pulls templates by name from campaign_schedule.json, renders with Jinja2 variable substitution, and validates HTML before sending. This separation allows non-engineers to edit email copy without touching Python code.
Multi-Site Operations Dashboard
A centralized dashboard (/tmp/dashboard_index.html) provides real-time visibility across properties. The dashboard makes authenticated calls to the Lambda API gateway, pulling:
- Calendar events for the next 30 days
- Pending bookings awaiting confirmation
- Email delivery status and bounces
- Platform messaging queue
The dashboard stores an API token in repos.env under DASHBOARD_TOKEN, rotated monthly via Lambda console. This token is included in Authorization headers for all API calls.
Deployment Pipeline
Deployment is handled by bash scripts that wrap AWS CLI calls:
./deploy_campaign_scheduler.sh
./deploy_inbox_scraper.sh
./deploy_boat_cleaner.py
Each script:
- Validates Python syntax and imports
- Uploads code to Lambda via
aws lambda update-function-code - Updates environment variables and triggers
- Runs smoke tests against the API endpoint
- Logs deployment timestamp to CloudWatch
There is no manual CloudConsole work—all infrastructure changes are scripted and version-controlled.
Key Decisions and Trade-offs
Why Lambda instead of long-running services? The event workload is bursty—high volume around event dates, quiet otherwise. Lambda's pay-per-invocation model is more cost-effective than EC2. Cold starts (200-500ms) are acceptable for human-triggered operations and scheduled tasks.
Why SES instead of Mailgun/SendGrid? Integration with internal AWS infrastructure and lower per-email cost for volume campaigns. Bounce/complaint feedback loops are automatically delivered to SNS, simplifying error handling.
Why API Gateway + bearer tokens instead of IAM? Third-party platforms (GetMyBoat, Boatsetter) and frontend code cannot use AWS credentials. Bearer tokens are simpler to rotate and audit than managing IAM users for external systems.
What's Next
Future work includes:
- Webhook endpoints from booking platforms (instead of polling)
- Automated conflict resolution when the same slot is booked across multiple platforms
- SMS notifications via Twilio for time-sensitive bookings
- Dashboard reports on booking volume, cancellation rates, and platform conversion
The infrastructure is now positioned for scale—adding new event types, platforms, or communication channels requires only configuration changes to campaign_schedule.json, not code deployment.