```html

Building a Multi-Tenant Charter Booking Pipeline: SCC Events, CloudFront Auth Bypasses, and Guest Pages

This post covers the technical implementation of a charter booking workflow that integrates Boatsetter bookings into ShipCaptainCrew (SCC), automatically provisions guest-facing pages, and notifies crew via magic links. The challenge: coordinating five independent systems (Boatsetter, SCC DynamoDB, JADA Calendar Lambda, CloudFront, and S3) while handling authentication mismatches and path-rewriting rules.

The Problem: Five Systems, Four Auth Mechanisms

When a charter books through Boatsetter, we need to:

  • Create an SCC event (auto-notifies crew with invite links)
  • Log it in JADA Internal Calendar for scheduling visibility
  • Generate a guest-facing page at a friendly URL like /g/boatsetter-may-30/
  • Provide crew with a checklist page and photo upload capability
  • Email crew asking for confirmation

The infrastructure spans multiple domains and authentication patterns:

  • SCC API Gateway: Protected by service key hash (custom auth, not API keys)
  • Dashboard Lambda: Protected by X-Dashboard-Token header
  • JADA Calendar Lambda: Also uses dashboard token
  • CloudFront (SCC): Strips custom headers before forwarding to origin
  • queenofsandiego.com: Uses CloudFront Functions for path rewriting

Technical Implementation

1. SCC Event Creation via Direct API Gateway URL

The first blocker: SCC sits behind a CloudFront distribution that strips custom headers. This prevented direct API calls with the service key. The solution was to bypass CloudFront entirely and hit the API Gateway URL directly.

Discovery process:

  • Located SCC Lambda environment variables to find SERVICE_KEY_HASH
  • Confirmed the hash function in the Lambda code (/tmp/scc-lambda-src/lambda_function.py, line ~2847)
  • Extracted the API Gateway URL from CloudFront origin settings (not the CloudFront domain)

Event creation payload:

POST https://[api-gateway-id].execute-api.[region].amazonaws.com/prod/events
X-Service-Key: [hashed_key]
Content-Type: application/json

{
  "name": "Boatsetter Charter – May 30",
  "start_time": "2024-05-30T10:00:00Z",
  "end_time": "2024-05-30T13:00:00Z",
  "guest_name": "Guest Name",
  "guest_email": "guest@example.com",
  "notes": "Charter details, minus revenue line items (crew can't see earnings)",
  "crew_ids": ["crew_1", "crew_2"],
  "captain_id": "captain_1"
}

The event creation automatically triggers SCC's crew notification system, sending magic-link invites to all assigned crew members.

2. DynamoDB Direct Updates to Hide Revenue

After event creation, we need to remove revenue and captain fee from the notes (crew-facing data). The SCC event update route in Lambda can't selectively redact fields, so we update DynamoDB directly:

aws dynamodb update-item \
  --table-name scc-events \
  --key '{"event_id": {"S": "EVENT_ID"}}' \
  --update-expression "SET #notes = :notes" \
  --expression-attribute-names '{"#notes": "notes"}' \
  --expression-attribute-values '{":notes": {"S": "Cleaned notes without revenue"}}'

This prevents crew from seeing charter earnings while keeping the event fully functional for scheduling.

3. Guest Page Generation and CloudFront Path Rewriting

The guest page lives at queenofsandiego.com (the captain's brand), not sailjada.com. We generate a static HTML file and upload it with a specific naming convention that works with CloudFront Functions.

CloudFront Function logic (read from live distribution):

// CloudFront Functions rewrite /g/{slug}/ to /g/{slug}.html
if (request.uri.startsWith('/g/')) {
  request.uri = request.uri.replace(/\/$/, '') + '.html';
}

S3 upload path:

/g/boatsetter-may-30.html

Accessible as:

https://queenofsandiego.com/g/boatsetter-may-30/

The file contains:

  • Guest charter details (name, date, time, what to bring)
  • Pre-signed S3 upload form for guest photos
  • JavaScript that requests signed URLs from the SCC Lambda's /g/{booking_id}/presign route

4. Crew-Facing Checklist via SCC Frontend

Rather than building a separate crew page, we leverage the existing SCC frontend. The event created in step 1 automatically appears in crew dashboards with:

  • Checklist items (provisioned via event data)
  • Crew confirmation status
  • Time-aware features (shows only active/upcoming events)

Crew access the event through the SCC frontend URL routing, which deep-links to the specific event via its ID.

5. JADA Calendar Integration

The calendar entry uses the same dashboard token auth mechanism as the event creation:

POST https://[dashboard-lambda-url]/calendar
X-Dashboard-Token: [token]
Content-Type: application/json

{
  "title": "Charter: Boatsetter – May 30",
  "start": "2024-05-30T10:00:00Z",
  "end": "2024-05-30T13:00:00Z",
  "type": "charter",
  "booking_id": "XHQGMDH"
}

This calendar is internal-only (not shared with crew), used for captain scheduling and resource planning.

Key Infrastructure Decisions

Why Bypass CloudFront for SCC API Calls?

CloudFront strips custom headers before forwarding to the origin. Since SCC uses custom header authentication (not OAuth or API Gateway's built-in auth), we had two options:

  • Add Origin Custom Header in CloudFront: Would hardcode the service key in CloudFront settings (risky, visible in console)
  • Hit API Gateway directly: Bypasses the header-stripping layer entirely, safer

We chose direct API Gateway calls. The endpoint is internal-only; there's no public DNS record pointing to it.

Why Guest Pages on queenofsandiego.com?

The guest page is branded as the captain's vessel site,