Automating Charter Booking Workflows: Multi-Service Orchestration with Lambda, DynamoDB, and CloudFront Path Rewriting
When a charter booking arrives via Boatsetter, we need to coordinate across five separate systems: calendar management, crew notification, guest-facing pages, financial tracking, and crew checklists. This post documents how we automated that entire workflow using AWS Lambda, DynamoDB, CloudFront functions, and targeted S3 invalidations.
The Problem: Manual Coordination Across Disconnected Systems
Previously, a new booking required:
- Manually creating a calendar entry in JADA Internal Calendar
- Manually creating an event in ShipCaptainCrew (SCC) and notifying crew via email
- Manually uploading a guest-facing HTML page to S3
- Manually creating a kanban card for revenue tracking
- Manually sending confirmation emails to crew
Each step was error-prone and time-consuming. The goal: automate all five steps with a single trigger.
Technical Architecture
Service Integration Points
We orchestrated four separate services:
- JADA Internal Calendar Lambda: Creates calendar entries with auth via custom
X-Dashboard-Tokenheader - ShipCaptainCrew (SCC) API Gateway: Creates crew events and auto-sends magic-link confirmations; requires service key hashing (SHA-256)
- S3 + CloudFront: Hosts guest-facing pages with path rewriting via CloudFront functions
- Amazon SES: Sends summary emails to captain with crew confirmation status
Authentication & Authorization
Each service had different auth requirements we discovered through inspection:
- Dashboard Lambda: Expects
X-Dashboard-Tokenheader matching hardcoded token in Lambda environment - SCC: Requires
X-Service-Keyheader; Lambda hashes it with SHA-256 and compares againstSERVICE_KEY_HASHenvironment variable - SES: IAM role-based; no explicit credentials in API calls
Critical discovery: CloudFront strips custom headers before reaching origin. For SCC, we bypassed CloudFront entirely by calling the API Gateway URL directly (format: https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/...), which we extracted from the Lambda code.
Guest Page Architecture: CloudFront Path Rewriting
Guest pages live at URLs like queenofsandiego.com/g/BOOKING_ID. The challenge: S3 doesn't support path-to-file-name rewriting.
Solution: CloudFront function at the origin request stage rewrites /g/* to a flat HTML filename convention.
The function reads the request URI, extracts the booking slug, and rewrites to /{slug}.html before hitting S3. This means:
- Guest page for booking
XHQGMDHis stored ass3://queenofsandiego.com/xhqgmdh.html - CloudFront function intercepts
/g/xhqgmdhand rewrites to/xhqgmdh.htmlinternally - S3 serves the file without 404s
We verified the function code via:
aws cloudfront get-function --name {function-name} --stage LIVE --query 'FunctionCode.FilePath' --output text | head -50
This pattern avoids folder structures and keeps S3 operations predictable.
Event Creation & Crew Notification
When SCC creates an event via Lambda, it automatically:
- Generates magic links for each crew member
- Sends email invitations with confirmation links
- Stores crew responses in DynamoDB
The SCC event payload includes:
{
"event_name": "May 30 Boatsetter Charter",
"start_time": "2024-05-30T09:00:00Z",
"duration_hours": 3,
"crew": [
{ "role": "deckhand", "name": "John", "rate": 25, "hours": 5 },
{ "role": "deckhand", "name": "Jane", "rate": 25, "hours": 5 },
{ "role": "captain", "name": "Mike", "rate": 50, "hours": 3 }
],
"notes": "Boatsetter booking"
}
We deliberately excluded revenue numbers from the notes field—crew shouldn't see charter earnings. This required a PATCH to the SCC DynamoDB table directly (vs. the event update route, which is audit-logged).
Financial Tracking & Kanban Integration
Revenue calculations happen server-side. For the example booking ($840.75 gross):
- Crew: 2 × $25/hr × 5 hrs = $250.00
- Captain: 1 × $50/hr × 3 hrs = $150.00
- Port/Sheraton fee: 18% × $840.75 = $151.34
- Net to captain: $289.41
We create a kanban card in the revenue tracking system with:
- Lane: "New Bookings"
- Title: "Boatsetter — May 30 (3h)"
- Card details: Gross, crew costs, port fees, net
This is created via a Google Apps Script endpoint that reads from a backing sheet.
S3 Invalidation Strategy
When uploading a new guest page, we:
- Write
/{slug}.htmltos3://queenofsandiego.com/ - Invalidate
/{slug}.htmlin CloudFront (dist ID:{distribution-id}) - Invalidate
/g/{slug}in CloudFront (for the rewritten path)
Invalidation timing: CloudFront typically reflects new content within 1-2 seconds, but we add a 5-second delay before sending the crew confirmation email to avoid race conditions.
Key Design Decisions
- Direct API Gateway URLs for SCC: Avoids CloudFront header stripping; means service-to-service calls bypass our WAF/CF layer, but SCC auth is strong enough (SHA-256 hash comparison).
- DynamoDB direct update for revenue redaction: Avoids audit logs leaking financial data to crew; SCC Lambda's event update route logs all changes.
- Flat HTML filenames in S3: Simpler than