Multi-Service Orchestration for Dynamic Charter Booking: Calendar, Events, Guest Pages, and Crew Notifications
When a charter gets booked through Boatsetter, we need to coordinate across four independent systems simultaneously: create an internal calendar entry, generate a crew-facing event with auto-notifications, build a guest-facing confirmation page, and scaffold a crew operations checklist. This post walks through the architecture and execution flow that makes this happen in one coherent booking workflow.
The Problem: Siloed Booking Data
A Boatsetter booking arrives as a webhook or manual entry. It contains guest info, charter duration, location, and revenue figures. But that data lives in isolation. Crew members don't know they're needed. The captain sees no event. The guest has nowhere to upload photos. Operations has no checklist. We built a system to fan that single booking fact out to four independent infrastructure layers.
Architecture Overview
- JADA Internal Calendar: Google Calendar via Lambda function, authenticated with service account credentials stored in AWS Secrets Manager
- ShipCaptainCrew (SCC) Event System: DynamoDB-backed event store with CloudFront-fronted API Gateway, requiring hashed service key auth
- Guest Confirmation Page: Static HTML generated per-booking, deployed to S3 bucket for
queenofsandiego.com, served through CloudFront with path-rewriting functions - Crew Operations Checklist: SCC-driven crew-facing page with dynamic event deep-linking and presigned S3 photo upload URLs
Technical Execution Flow
1. Calendar Entry Creation
The JADA Internal Calendar lives in Google Workspace. To create an entry programmatically, we call a Lambda function that:
- Retrieves the service account key from Secrets Manager (stored as JSON under key
jada-google-service-account) - Authenticates to Google Calendar API using the service account
- Creates an all-day event in the shared calendar with booking ID, guest name, and charter time as the event title
- Returns the calendar event ID for audit logging
Lambda endpoint and authentication are managed through API Gateway with custom authorizers that validate an X-Dashboard-Token header. This token is rotated quarterly and stored in parameter store, not hardcoded.
2. ShipCaptainCrew Event Creation
SCC is a crew scheduling and operations system. When we create an event there, it auto-notifies all registered crew members via magic links (no password required). The flow:
- Hash the service key (SHA-256) and compare against
SERVICE_KEY_HASHenvironment variable in the SCC Lambda - POST to
/api/eventswith booking details, crew list, charter time, and location - SCC Lambda validates auth and writes event record to DynamoDB table
scc-events - Trigger SNS topic
scc-crew-notificationswhich invokes SES to email all crew with a personalized magic link - Store the event ID returned by SCC for guest page deep-linking
Critical detail: CloudFront strips custom headers before forwarding to the origin API Gateway. To bypass this, we discovered the direct API Gateway endpoint (format: https://{api-id}.execute-api.{region}.amazonaws.com/prod/events) and route through that when the CloudFront path rewriting would strip auth headers.
3. Guest Confirmation Page Generation
The guest page is a self-contained HTML file that serves as a booking confirmation and photo upload hub. Generation process:
- Template HTML file stored at
/templates/guest-page-template.htmlcontains placeholders for{{BOOKING_ID}},{{GUEST_NAME}},{{CHARTER_TIME}}, and{{PRESIGN_URL}} - Generate a unique guest page filename (we use 8-character alphanumeric identifiers like
xhqgmdh) - Call the SCC
/g/{booking_id}/presignendpoint to get a time-limited presigned S3 URL for photo uploads (valid for 7 days post-charter) - Render the template with live data and write to S3 bucket
queenofsandiego.comat path/g/{page_id}.html - Invalidate CloudFront distribution (ID: obtained from environment config) to cache-bust the new page
The S3 bucket has a CloudFront function attached that rewrites incoming requests. The function reads the path /g/XHQGMDH and internally rewrites it to /g/XHQGMDH.html before hitting S3, so URLs remain clean.
4. Crew Checklist Page
The crew-facing page is generated by the SCC frontend itself, accessed via deep-link. We:
- Retrieve the SCC event ID from the event creation response
- Generate a crew portal link pointing to
https://scc.internal/events/{event_id}with an optional magic token query parameter - Email this link to all crew listed on the event (via SNS → SES pipeline)
- When crew click the link, SCC serves a dynamic HTML page with:
- Charter details (time, location, guest count, compensation)
- Interactive pre-charter checklist (fuel check, supply inventory, guest safety briefing, etc.)
- Presigned photo upload widget (same as guest page, coordinates team documentation)
- Post-charter expense logging (mileage, supplies purchased, tips received)
Infrastructure Details
S3 Buckets:
sailjada.com— primary charter booking assets, legacy infrastructurequeenofsandiego.com— guest-facing pages and public assets, newer CloudFront-fronted distribution
DynamoDB Tables:
scc-events— partition keyevent_id, range keycreated_timestampscc-crew-assignments— tracks crew sign-ups for each event, indexed bycrew_idandevent_id
Lambda Functions:
dashboard-api— general orchestrator for calendar and SCC calls, requires validX-Dashboard-Tokenscc-api— handles event CRUD, photo presigning, and crew notificationscalendar-sync— Google Calendar integration (invoked by dashboard-api)
CloudFront Functions:
- Distribution for
queenofsandiego.com(ID in config) has a viewer-request function that rewrites/g/{