Managing Multi-Tenant Charter Operations: Calendar Sync, Guest Onboarding, and Event Data Consistency
This session focused on a critical operational challenge: ensuring that crew assignments, guest information, and event details remain synchronized across multiple systems during active charter bookings. We worked through a weekend of three simultaneous charters (Quinn, Jonathan, and Danika events) and resolved cascading data inconsistencies across Google Calendar, DynamoDB, S3-hosted guest pages, and email/SMS notification systems.
The Problem: Fragmented Event State
JADA's charter operations rely on several independent systems that don't automatically sync:
- Google Calendar — source of truth for crew scheduling and event metadata
- DynamoDB — operational database for event details, waiver tracking, and crew dispatch
- S3 + CloudFront — guest-facing pages (waiver forms, boarding information, photo upload codes)
- Email/SMS — notifications sent to guests and crew
When event details change (crew assignments, times, contact information), manual propagation across all four systems becomes error-prone. This session demonstrated exactly how: we discovered that crew names didn't match calendar entries, guest pages had stale arrival times, and some events existed in only some of the systems.
Approach: Source-of-Truth Validation and Systematic Sync
We established Google Calendar as the authoritative source and worked backward through the dependency chain.
Step 1: Parse Calendar and Identify Discrepancies
We fetched the JADA Internal Calendar via its iCal endpoint and parsed event records for the weekend through June 16:
GET https://calendar.google.com/calendar/ical/[CALENDAR_ID]/public/basic.ics
This gave us three events:
- quinn-guest — May 30–31, crew listed as Quinn Male and (incorrectly) June 14 Ariel Loback
- jonathan-sunset — May 30–31, crew and contact info missing from calendar description
- danika-guest — May 31–June 1, crew correct but event times were wrong (listed as 5–7pm instead of 3–7pm)
We also checked DynamoDB (table: events, partition key: EID) and found that Quinn and Jonathan events didn't exist—only Danika was present, with incorrect time values and missing crew.
Step 2: Fix Calendar Source Records
Using the Google Calendar API (via Lambda function jada-calendar-patch), we corrected crew assignments:
PATCH https://www.googleapis.com/calendar/v3/calendars/[CALENDAR_ID]/events/[EVENT_ID]
{
"summary": "danika-guest",
"description": "Captain: Gene\nCrew: [list]\nArrive by: 3:00 PM on May 31"
}
Why we did this: Calendar is the single source of truth that crew and guests reference. Once corrected here, we could propagate outward with confidence.
Step 3: Sync Guest Pages (S3 + CloudFront)
Guest pages are static HTML deployed to S3 bucket sailjada-guest-pages under keys:
quinn-guest.htmljonathan-afternoon.htmldanika-guest.html
We retrieved existing pages from S3, updated static content (arrival times, crew names), and redeployed:
aws s3 cp quinn-guest.html s3://sailjada-guest-pages/quinn-guest.html --cache-control "max-age=300"
aws s3 cp jonathan-afternoon.html s3://sailjada-guest-pages/jonathan-afternoon.html
aws s3 cp danika-guest.html s3://sailjada-guest-pages/danika-guest.html
Then invalidated CloudFront distribution E2K8VXYZ1A2B to flush edge caches:
aws cloudfront create-invalidation --distribution-id E2K8VXYZ1A2B --paths "/*"
Why S3 + CloudFront: Static HTML hosting with global edge caching ensures guests receive consistent information quickly, even if the origin is temporarily unreachable. Invalidation ensures updates propagate within ~1 second.
Step 4: Sync DynamoDB Event Records
We created or updated event records in the events DynamoDB table:
{
"EID": "quinn-guest",
"EventName": "Quinn Male Charter",
"StartTime": "2026-05-30T14:00:00Z",
"EndTime": "2026-05-31T10:00:00Z",
"Captain": "Quinn Male",
"Crew": ["Assistant"],
"Status": "confirmed",
"GuestWaiverRequired": true
}
Similarly for Jonathan and Danika, correcting Danika's times to 3–7 PM (15:00–19:00).
Why DynamoDB: It serves as the operational ledger for crew dispatch, waiver tracking, and Lambda-based automations. Keeping it in sync ensures downstream processes (waiver Lambda, email/SMS triggers) have correct data.
Step 5: Guest Onboarding — Waiver and Contact Sync
The waiver system (Lambda function: jada-waiver-submit, endpoint: /api/waiver) reads event data from DynamoDB and generates guest-specific waiver pages. We verified:
- Waiver page correctly reflects updated event times (3–7 PM for Danika)
- Minor handling (guardian signature) works for underage guests (Quinn was flagged as minor)
- Photo upload codes are generated and embedded in guest pages
For guests lacking contact info in the calendar, we retrieved booking details from Gmail (Boatsetter confirmation emails) and updated DynamoDB, then sent SMS/email boarding notifications:
aws sns publish --phone-number "+1[GUEST_PHONE]" --message "Charter confirmed. Waiver: [URL] Photo code: [CODE]"
Infrastructure and Automation Decisions
Why Not a Single Database?
We didn't consolidate everything into DynamoDB because Google Calendar serves non-technical stakeholders (crew, captains, office staff) who need a familiar UI. The Lambda patch function acts as a two-way sync, but Google Calendar remains primary for human editing.
Why CloudFront for Guest Pages?
Guest pages are served to people on boats with intermittent connectivity. CloudFront's edge caching ensures they load from nearby CDN nodes even if the origin S3 bucket is slow. The 300-second cache control balances freshness with reliability.
Why Lambda for Calendar Patching?
Direct Google Calendar API calls require OAuth tokens, which expire. We encapsulated token refresh logic in a Lambda function (jada-calendar-patch) that runs with IA