```html

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-Token header
  • 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-Token header matching hardcoded token in Lambda environment
  • SCC: Requires X-Service-Key header; Lambda hashes it with SHA-256 and compares against SERVICE_KEY_HASH environment 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 XHQGMDH is stored as s3://queenofsandiego.com/xhqgmdh.html
  • CloudFront function intercepts /g/xhqgmdh and rewrites to /xhqgmdh.html internally
  • 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:

  1. Write /{slug}.html to s3://queenofsandiego.com/
  2. Invalidate /{slug}.html in CloudFront (dist ID: {distribution-id})
  3. 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