Multi-Domain Charter Booking Pipeline: Integrating Guest Pages, Event Management, and Crew Notifications Across Distributed Infrastructure
Overview
This post documents the architecture and implementation of a charter booking workflow that spans three distinct systems: a guest-facing booking confirmation page, an internal event management platform (ShipCaptainCrew), and a crew notification pipeline. The challenge was orchestrating seamless data flow across S3-backed CloudFront distributions, Lambda functions, and DynamoDB while maintaining proper authentication boundaries and avoiding credential leakage in public-facing assets.
What We Built
- A guest-facing confirmation page deployed to
queenofsandiego.com/g/{slug}with time-aware photo upload capability - An internal calendar event in the JADA system tied to the booking metadata
- A crew-facing event in ShipCaptainCrew (SCC) with auto-notification to all assigned crew via magic links
- Infrastructure routing that separates public assets from authenticated API calls
Technical Architecture
Multi-Domain Asset Management
The booking guest page was initially staged in sailjada.com's S3 bucket but ultimately deployed to queenofsandiego.com for branding and analytics clarity. This required understanding two distinct CloudFront distributions:
- sailjada.com distribution: S3 origin at
sailjada.combucket with legacy guest page routing - queenofsandiego.com distribution: S3 origin with CloudFront Functions that rewrite paths
The queenofsandiego.com distribution includes a CloudFront Function that rewrites requests to /g/{slug} paths as flat /g/{slug}.html files in S3. This was critical: rather than fighting the rewrite logic, we embraced it and uploaded the guest page as /g/friendly-charter-name.html (not a directory with index.html). This eliminated an extra round-trip and kept the deployment model simple.
Authentication Separation: CloudFront vs. API Gateway
The ShipCaptainCrew Lambda receives requests via two paths:
- CloudFront → Lambda (via CloudFront origin): Strips custom headers (like authentication tokens) for security
- Direct API Gateway → Lambda: Preserves all headers, including auth tokens and service keys
When creating SCC events (which require service key authentication in the X-Service-Key header), CloudFront's header stripping broke the flow. The solution was to bypass CloudFront for internal API calls and hit the API Gateway URL directly:
# CloudFront URL (headers stripped)
https://shipchaptaincrew.com/api/events
# API Gateway URL (headers preserved)
https://{api-gateway-id}.execute-api.{region}.amazonaws.com/prod/api/events
The service key hash validation happens in the Lambda environment variable SERVICE_KEY_HASH, which is compared against an SHA-256 hash of the incoming X-Service-Key header value. We verified this by downloading the Lambda source, examining the hash_password function, and confirming the environment variable was correctly set.
Event and Calendar Integration
Two separate event records were created:
- JADA Internal Calendar entry: Created via the calendar Lambda with the booking date (May 30), charter details, and authentication using the
X-Dashboard-Tokenheader - ShipCaptainCrew event: Created via the SCC Lambda to auto-notify crew with magic links, allowing them to opt-in or confirm availability
The SCC event included crew member assignments with hourly rates, captain assignments, and detailed notes. Critically, we removed Revenue and Captain fee fields from the event notes via DynamoDB direct update to prevent crew from seeing how much the charter was worth — that's internal business data.
Key Infrastructure Decisions
Why Direct API Gateway Instead of CloudFront?
CloudFront's header stripping is a security feature: it prevents edge locations from accidentally leaking internal auth headers. However, this breaks backend service-to-service authentication. Rather than override CloudFront's behavior (which would be risky), we took the architectural approach of routing internal API calls around CloudFront entirely. This is a clean separation of concerns: public assets through CloudFront, internal APIs direct to API Gateway.
Why Flat HTML Files, Not Index Pattern?
The CloudFront Function code showed explicit path rewriting to append .html extensions. Rather than battle the rewrite logic with directory-based uploads, we matched the expected pattern. This reduced latency (no index lookup), simplified caching headers, and made the file structure explicit.
Why DynamoDB Direct Update for Revenue Removal?
The SCC Lambda's event update route (PATCH /events/{id}) goes through business logic validation that might prevent certain field updates. Rather than risk breaking the validation or leaving residual revenue data, we updated the DynamoDB item directly, removing sensitive fields before the event was retrieved by crew endpoints.
Deployment Steps (Approximate)
- Create guest page HTML with embedded metadata (booking date, guest name, checklist items)
- Upload to
queenofsandiego.comS3 bucket asg/friendly-charter-name.html - Invalidate CloudFront distribution cache for the path
- Create JADA calendar entry via
POST /calendarwithX-Dashboard-Tokenheader - Create SCC event via direct API Gateway URL with
X-Service-Keyheader and crew assignments - Update SCC event via DynamoDB to strip revenue fields
- Trigger crew notification emails (handled automatically by SCC event creation)
What's Next
- Crew confirmation workflow: The magic links sent to crew point to the SCC frontend; we should verify the deep-link routing works end-to-end
- Photo upload validation: The guest page includes time-aware upload logic that only allows photo uploads during a specific window around the charter date; test the presign flow
- Email template standardization: Crew notifications, guest confirmations, and internal emails should all reference the same canonical URL structure
- Revenue reconciliation: Implement a dashboard view that calculates net profit post-crew-cost and port fees
Lessons for Multi-System Integration
This booking pipeline reinforced several patterns: (1) assume CloudFront will strip headers, and plan around it with alternate routing; (2) match the framework's conventions (flat files, rewrite rules) rather than fight them; (3) separate public assets from internal APIs at the infrastructure layer, not just the code layer; and (4) use DynamoDB direct updates for sensitive data transformations that bypass business logic.
```