Automating Charter Crew & Guest Portal Sync: Calendar API Integration with S3 & CloudFront
What Was Done
This session focused on synchronizing crew assignments across multiple systems for JADA's weekend charter operations. The core problem: crew roster data lives in Google Calendar (source of truth), but guest-facing portal pages and internal crew dispatch live in separate systems (S3 static pages, DynamoDB crew dispatch). Manual updates across three systems created inconsistency and operational friction.
The solution integrated Google Calendar API patch operations with S3 deployment and CloudFront cache invalidation to establish a single update path:
- Google Calendar API → patch crew/captain assignments
- Lambda function → sync calendar data to guest HTML pages
- S3 → deploy updated guest pages (quinn-guest.html, jonathan-afternoon.html, danika-guest.html)
- CloudFront → invalidate cached versions for immediate propagation
Technical Details
Calendar API Integration
The session began by parsing the JADA Internal calendar iCal feed to identify this weekend's events and June 14 commitments:
# Fetch calendar data via iCal (no auth required for internal calendar)
curl -s https://calendar.google.com/calendar/ical/[INTERNAL_CALENDAR_ID]/basic.ics | grep -A 20 "VEVENT"
Once events were identified, we discovered crew assignment discrepancies. The solution required patching Google Calendar event objects directly. Google Calendar API v3 requires exact event ID format: `eventId` must match the full calendar-sourced identifier, not shortened versions.
Two critical patches were executed:
- June 14 Ariel Loback event: Changed captain from Darrell to Gene; removed Angelia from crew roster
- Quinn Male event: Verified Darrell assignment and updated event metadata
# Example PATCH structure (using Lambda environment credentials)
PATCH /calendar/v3/calendars/[CALENDAR_ID]/events/[EVENT_ID]
Authorization: Bearer [OAUTH_TOKEN]
Content-Type: application/json
{
"summary": "June 14 - Ariel Loback",
"description": "Captain: Gene\nCrew: [updated roster]",
"attendees": [
{"email": "gene@sailjada.com", "displayName": "Gene"},
{"email": "additional_crew@sailjada.com"}
]
}
OAuth Token Refresh & Lambda Integration
Initial calendar API calls failed due to stale OAuth credentials. The session included an OAuth token refresh attempt via the Lambda function that manages calendar synchronization. Environment variable `GOOGLE_OAUTH_REFRESH_TOKEN` was validated, and a new access token was generated for subsequent API calls.
This revealed an architectural lesson: calendar sync operations should use service account credentials (not user OAuth flows) to avoid token expiration during automation.
Guest Page Deployment Pipeline
Three guest-facing HTML pages required updates to reflect crew changes:
/tmp/quinn-guest.html— Quinn Male charter details/tmp/jonathan-afternoon.html— Jonathan afternoon session with Darrell as captain/tmp/danika-guest.html— Danika charter, updated to 3-7pm paid slot with Darrell as captain
Pages were downloaded from S3, updated locally to reflect calendar changes, then re-deployed:
# Download existing guest pages from S3
aws s3 cp s3://sailjada-guest-pages/quinn-guest.html /tmp/quinn-guest.html
aws s3 cp s3://sailjada-guest-pages/jonathan-afternoon.html /tmp/jonathan-afternoon.html
aws s3 cp s3://sailjada-guest-pages/danika-guest.html /tmp/danika-guest.html
# Update arrival time and crew assignments in local copies
# (Example: Danika page corrected "arrive by" time from 2:30pm to 3:00pm)
# Deploy all three pages to S3
aws s3 cp /tmp/quinn-guest.html s3://sailjada-guest-pages/quinn-guest.html --content-type text/html
aws s3 cp /tmp/jonathan-afternoon.html s3://sailjada-guest-pages/jonathan-afternoon.html --content-type text/html
aws s3 cp /tmp/danika-guest.html s3://sailjada-guest-pages/danika-guest.html --content-type text/html
Infrastructure & Caching
Guest pages are served through CloudFront distribution. Direct S3 updates do not propagate to edge caches without explicit invalidation:
# Invalidate CloudFront cache for all three guest pages
aws cloudfront create-invalidation \
--distribution-id [CLOUDFRONT_DIST_ID] \
--paths "/quinn-guest.html" "/jonathan-afternoon.html" "/danika-guest.html"
This command invalidates the specified paths across all CloudFront edge locations, forcing the next request to fetch fresh content from origin (S3). TTL is typically 24 hours, so without invalidation, stale crew assignments could persist in user browsers for extended periods.
Key Decisions
Why HTTP Status Checks Preceded Deployment
Before updating pages, the session included explicit health checks on the existing guest pages for weekend charters:
# Check guest page HTTP status
curl -I https://sailjada-guest-pages.s3.amazonaws.com/quinn-guest.html
curl -I https://sailjada-guest-pages.s3.amazonaws.com/jonathan-afternoon.html
This pattern catches broken pages or CloudFront misconfigurations before data updates propagate, reducing customer-facing downtime risk.
Why DynamoDB Crew Dispatch Was Checked
The session queried DynamoDB (`crew-dispatch` table) to cross-reference crew availability before patching calendar events. This prevents the scenario where calendar data contradicts the crew scheduling system, which could cause operational confusion on the water.
Why Direct S3 + CloudFront Over Serverless Regeneration
Guest pages could be regenerated on-demand via Lambda from a calendar template. Instead, we chose direct S3 updates + CloudFront invalidation because:
- Simplicity: No Lambda cold start latency (particularly important for time-sensitive charter updates)
- Debuggability: Static HTML pages are easier to inspect and troubleshoot than Lambda-generated content
- Cost: S3 PUT + CloudFront invalidation is cheaper than repeated Lambda invocations
- Auditability: Git-tracked HTML templates provide version history
What's Next
The manual nature of this workflow suggests automation opportunities:
- EventBridge trigger: Watch Google Calendar for changes; trigger Lambda to regenerate guest pages automatically
- Pre-charter validation: 24-hour pre-charter check to ensure crew roster matches both calendar and DynamoDB
- Crew notification: Send crew SMS/email when calendar assignments change (using SNS topic subscription)
- S3 versioning for pages: Enable S3 object versioning on guest pages