```html

Automating Boat Cleaning Dispatch and Calendar Sync: Integrating Third-Party Booking Platforms with Google Calendar

What Was Done

This session focused on building automation infrastructure to dispatch boat cleaning services and synchronize third-party booking platform calendars (GetMyBoat, Boatsetter) with Google Calendar. The work eliminated manual calendar entry bottlenecks and created a scalable dispatch system that integrates multiple boat rental platforms with internal scheduling systems.

  • Created Python dispatch automation script for boat cleaning tasks
  • Extended Google Apps Script (GAS) CalendarSync module to handle iCal feeds from GetMyBoat and Boatsetter
  • Built email notification templates for dispatch teams
  • Deployed automation to Lambda-backed APIs for real-time calendar synchronization
  • Integrated calendar hold system with existing marketing campaign infrastructure

Technical Details: Dispatch Automation

Dispatch Script Architecture

The dispatch system was implemented in two components:

/Users/cb/Documents/repos/tools/dispatch_boat_cleaner.py — The core dispatch logic that:

  • Reads booking events from the calendar system via Lambda API
  • Calculates cleaning time windows based on checkout/check-in intervals
  • Generates dispatch notifications with venue-specific requirements
  • Sends notifications to team channels via SES (Amazon Simple Email Service)

/Users/cb/Documents/repos/tools/deploy_boat_cleaner.sh — Deployment wrapper that:

  • Validates Python environment and dependencies
  • Tests connectivity to Lambda calendar API before deploying
  • Schedules the dispatch script as a recurring CloudWatch event
  • Logs execution results to CloudWatch Logs for auditing

Why This Architecture? Direct Lambda invocation avoids polling overhead. By using CloudWatch Events to trigger the dispatch script on a schedule (typically 30-minute intervals for same-day bookings), we reduce API calls while maintaining responsiveness. SES integration ensures notifications bypass corporate email filters that often block automation-generated messages.

Calendar Synchronization: Google Apps Script Integration

CalendarSync.gs Enhancements

The existing CalendarSync.gs module in /Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/ was extended to:

  • Parse iCal feeds from GetMyBoat and Boatsetter APIs
  • Map external booking IDs to Google Calendar event UIDs for idempotency
  • Handle timezone conversions (critical for multi-region boat rentals)
  • Create "hold" events 24 hours before check-in for cleaning prep
  • Update existing events when bookings are modified on external platforms

Key Function: Calendar Event Ingestion

// Pseudo-code showing the integration pattern
function syncExternalBookings() {
  const platforms = ['getmyboat', 'boatsetter'];
  
  platforms.forEach(platform => {
    const iCalUrl = getCredential(`${platform}_ical_url`);
    const events = parseICalFeed(iCalUrl);
    
    events.forEach(externalEvent => {
      const existingEvent = findEventByExternalId(externalEvent.id);
      
      if (existingEvent) {
        updateCalendarEvent(existingEvent, externalEvent);
      } else {
        createCalendarEvent(externalEvent);
        // Create 24h prep hold
        createHoldEvent(externalEvent, 24);
      }
    });
  });
}

This pattern ensures that platform bookings automatically appear in Google Calendar without manual intervention, and prep holds prevent double-booking cleaning teams.

Infrastructure & Deployment

Lambda Calendar API Integration

The calendar system is served by a Lambda function that exposes endpoints:

  • POST /calendar/add-event — Creates calendar events (used by dispatch system)
  • GET /calendar/list-events — Queries events by date range
  • PUT /calendar/update-event — Modifies existing events

The Lambda function is deployed behind API Gateway (HTTP API v2 for cost optimization) with authentication tokens stored in repos.env. Request/response payloads are cached at CloudFront level for 5-minute TTL to reduce Lambda invocation costs.

GAS Project Configuration

The CalendarSync.gs file is deployed to a Google Apps Script project linked via `.clasp.json`. The project ID is maintained in version control (without sensitive credentials) to allow CI/CD deployment:

// .clasp.json structure (credentials stored separately)
{
  "scriptId": "[GAS_PROJECT_ID]",
  "rootDir": "./apps-script-replacement",
  "timeZone": "America/Los_Angeles"
}

Deployment uses the `clasp push` command to update the script on Google's servers, with a post-deployment hook that verifies the sync triggers are properly installed.

Email Template & Campaign Integration

Dispatch notifications use HTML templates stored in:

  • /Users/cb/Documents/repos/tools/templates/rady_shell_blast1.html
  • /Users/cb/Documents/repos/tools/templates/rady_shell_blast2.html

These templates include:

  • Booking details (guest name, boat model, venue, times)
  • Cleaning requirements and special instructions
  • Team assignment and escalation contact
  • One-click status update links (dispatch completed, issues, delays)

Templates are rendered using Jinja2-style variable substitution, allowing dynamic content insertion without exposing sensitive booking data in plaintext logs.

Key Design Decisions

Why Google Calendar as the Hub? Google Calendar is already integrated with business operations. Using it as the single source of truth reduces context switching and prevents data duplication across multiple scheduling systems.

Why iCal Polling Instead of Webhooks? Third-party platforms (GetMyBoat, Boatsetter) don't reliably support outbound webhooks. Polling via iCal feeds (updated every 30 minutes) provides eventual consistency with minimal infrastructure complexity. For time-critical bookings, the 30-minute window is acceptable given cleaning prep time is typically 2-4 hours.

Why "Hold" Events 24 Hours Prior? This creates a buffer between booking confirmation and cleaning execution. Team leads can review requirements, confirm cleaning crew availability, and identify venue-specific constraints (access codes, restricted hours) one day in advance.

Why SES Over Slack/Teams? Dispatch notifications must reach team members who may not check internal chat during off-hours. SES emails with receipts provide audit trails and don't require team members to maintain constant integrations.

What's Next

  • Expand dispatch logic to calculate cleaning duration dynamically based on boat type/size
  • Add