Automating Boat Cleaning Dispatch and Calendar Sync: Replacing Manual Workflows with Lambda APIs
What Was Done
This session involved consolidating scattered manual cleaning service coordination into an automated dispatch system integrated with Google Calendar. The core problem: Carole was manually tracking boat cleaning requests across email, spreadsheets, and calendar notes. The solution involved:
- Building a Python-based boat cleaner dispatch script that reads requests from a platform inbox
- Creating a CloudFormation-compatible Lambda API endpoint to trigger dispatches
- Integrating Google Calendar syncing via Google Apps Script to surface scheduled cleans to Carole's calendar
- Setting up email notifications via AWS SES to confirm dispatch status
Technical Details: The Dispatch System Architecture
Core Scripts and Entry Points
The dispatch system is built around three Python modules in /Users/cb/Documents/repos/tools/:
dispatch_boat_cleaner.py— Main dispatcher that pulls cleaning requests from a designated inbox and enqueues themplatform_inbox_scraper.py— Scrapes incoming platform notifications (GetMyBoat, Boatsetter) for new cleaning jobscampaign_scheduler.py— Schedules recurring dispatch batches (secondary, for bulk operations)
The dispatcher works by:
- Polling a designated email inbox (configured via
repos.env) for new cleaning requests - Parsing request metadata (boat name, date, location, special instructions)
- Creating a calendar event in Google Calendar via the Lambda API
- Sending confirmation email to Carole and the cleaning contractor
- Logging the dispatch in a DynamoDB table for audit purposes
Why This Approach?
Rather than building a custom web form or dashboard, we leveraged existing email flows because:
- Carole already receives cleaning requests via email from GetMyBoat and Boatsetter
- Email parsing is simpler and more resilient than relying on platform APIs that may change
- SES-based confirmations integrate seamlessly with existing notification patterns
- Minimal training required — no new dashboards to learn
Infrastructure: Lambda, Calendar API, and Google Apps Script
Lambda Function Configuration
The dispatch trigger is invoked via AWS Lambda API Gateway. The function is deployed via deploy_boat_cleaner.sh, which:
- Packages the Python dispatcher with dependencies
- Updates the Lambda function code in the existing calendar API Lambda
- Registers a new API Gateway route (e.g.,
POST /dispatch-boat-cleaner) under the dashboard API Gateway
The Lambda function configuration includes:
- Environment Variables: Inbox credentials (via SecretsManager), SES sender identity, DynamoDB table name
- Execution Role: Attached policy allowing
ses:SendEmail,dynamodb:PutItem,secretsmanager:GetSecretValue - Timeout: 60 seconds (intentionally short; batches are queued, not processed inline)
- Memory: 256 MB (sufficient for inbox scraping and API calls)
Google Calendar Sync via Apps Script
The CalendarSync.gs file in /Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/ serves as the synchronization layer. Key functions:
function addCalendarEvent(eventData) {
// eventData: {title, date, description, calendarId}
const calendar = CalendarApp.getCalendarById(eventData.calendarId);
const event = calendar.createEvent(
eventData.title,
eventData.date,
new Date(eventData.date.getTime() + (2 * 60 * 60 * 1000)), // 2-hour duration
{description: eventData.description}
);
return event.getId();
}
function syncBoatCleanerToCalendar(dispatchId) {
// Called after dispatch is confirmed
// Pulls details from DynamoDB, creates calendar event
}
This script is deployed as a standalone Google Apps Script project (not tied to a Sheet). It uses service account credentials stored in repos.env for automated access. Polling interval: every 5 minutes.
Integration with Existing Systems
GetMyBoat and Boatsetter Credentials
Boat platform API keys are stored in AWS SecretsManager under boat-platforms/getmyboat and boat-platforms/boatsetter. The inbox scraper checks both platforms for new booking notifications, then standardizes them into a common dispatch format before queuing.
Email Notification Flow
Dispatch confirmations are sent via SES from ops@queenofsandiego.com using pre-built HTML templates in /Users/cb/Documents/repos/tools/templates/:
rady_shell_blast1.html— Cleaner confirmation (includes job details, rate, expected duration)rady_shell_blast2.html— Carole confirmation (includes contractor assigned, ETA)
Templates are populated via Python string substitution, not a templating engine, to avoid dependencies.
Key Decisions and Trade-offs
Email-First Design Over Dashboard
We chose email-driven dispatch over a custom dashboard because Carole's workflow is already email-centric. Adding another tool would fragment her attention. By automating what happens after email arrives, we reduce friction.
Polling vs. Webhooks
GetMyBoat and Boatsetter both support webhooks, but we implemented polling instead because:
- Polling is idempotent and survives network interruptions
- No public endpoint exposure required (reduced attack surface)
- Easier to test and debug locally
- 5-minute polling interval is acceptable for booking notifications (not real-time critical)
DynamoDB for Audit Logs
Each dispatch is logged to DynamoDB with full metadata (timestamp, boat name, contractor, cost, status). This enables:
- Reconciliation against invoices from cleaning contractors
- Historical analysis of cleaning frequency and costs
- Replay capability if a dispatch fails mid-process
Deployment and Validation
The deployment script deploy_boat_cleaner.sh performs:
#!/bin/bash
# Package dispatcher with dependencies
pip install -r requirements.txt -t ./package
cd package && zip -r ../dispatcher.zip . && cd ..
zip dispatcher.zip ../dispatch_boat_cleaner.py
# Update Lambda function
aws