```html

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_HASH environment variable in the SCC Lambda
  • POST to /api/events with 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-notifications which 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.html contains 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}/presign endpoint 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.com at 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 infrastructure
  • queenofsandiego.com — guest-facing pages and public assets, newer CloudFront-fronted distribution

DynamoDB Tables:

  • scc-events — partition key event_id, range key created_timestamp
  • scc-crew-assignments — tracks crew sign-ups for each event, indexed by crew_id and event_id

Lambda Functions:

  • dashboard-api — general orchestrator for calendar and SCC calls, requires valid X-Dashboard-Token
  • scc-api — handles event CRUD, photo presigning, and crew notifications
  • calendar-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/{