```html

Multi-Service Event Orchestration: Syncing Boatsetter Charters Across Calendar, Guest Pages, and Crew Management

When a charter is booked through Boatsetter, we need to synchronize that data across three independent systems: an internal calendar service, a crew-facing event management platform, and guest-accessible web pages. This post covers the architecture, infrastructure decisions, and implementation details of automating that workflow.

What Was Done

For a Boatsetter charter booking, we created an end-to-end workflow that:

  • Creates a calendar entry in JADA Internal Calendar with charter details and crew assignments
  • Provisions a crew-facing event in ShipCaptainCrew (SCC) that auto-notifies all assigned crew with magic links
  • Generates and deploys a guest-facing charter page to queenofsandiego.com/g/ with photo upload capabilities
  • Sends confirmation request emails to crew via the notification system

Infrastructure and Service Topology

The solution spans multiple AWS services and custom Lambda functions:

  • JADA Internal Calendar Lambda: REST API accepting POST requests to create calendar entries. Requires X-Dashboard-Token header for authentication.
  • ShipCaptainCrew Lambda: Custom event management system with DynamoDB persistence, CloudFront distribution, and API Gateway for direct access. Uses service key hashing (SHA-256) for authentication.
  • S3 Buckets: sailjada.com (legacy charter pages) and queenofsandiego.com (new guest pages). CloudFront distributions front both.
  • SES: Email delivery for crew confirmations and captain summaries.

Technical Implementation Details

1. Calendar Entry Creation

The JADA Internal Calendar Lambda is invoked via HTTP POST with charter metadata:

POST /calendar
X-Dashboard-Token: [auth-token]
Content-Type: application/json

{
  "event_type": "charter",
  "date": "2024-05-30",
  "duration_hours": 3,
  "crew_assignments": [
    {"role": "crew", "count": 2, "hourly_rate": 25},
    {"role": "captain", "count": 1, "hourly_rate": 50}
  ],
  "location": "Sheraton Marina",
  "guest_name": "Boatsetter Guest"
}

The Lambda stores this in its internal database and returns a calendar entry ID for reference in downstream systems.

2. ShipCaptainCrew Event Creation

Creating an SCC event triggers automatic crew notifications. The challenge: SCC is fronted by CloudFront, which strips custom headers used for authentication. Solution: bypass CloudFront by posting directly to the API Gateway endpoint.

POST https://[api-gateway-id].execute-api.us-west-2.amazonaws.com/prod/events
X-Service-Key: [service-key]
Content-Type: application/json

{
  "event_name": "Boatsetter Charter - May 30",
  "date": "2024-05-30T09:00:00Z",
  "duration_hours": 3,
  "location": "Sheraton Marina",
  "crew": ["crew-1", "crew-2"],
  "captain": "captain-1",
  "notes": "Guest: Boatsetter Guest | Revenue: $840.75",
  "guest_page_url": "https://queenofsandiego.com/g/friendly-url/"
}

The SCC Lambda validates the service key against SERVICE_KEY_HASH in its environment variables (stored in AWS Secrets Manager, not committed to code). If validation passes, it:

  • Stores the event in DynamoDB with a unique event ID
  • Generates magic link tokens for each crew member
  • Invokes SES to send confirmation emails with embedded magic links
  • Returns the event ID for reference in downstream systems

3. Guest Page Generation and Deployment

Guest pages are deployed to the queenofsandiego.com S3 bucket with a specific naming convention. The CloudFront function at the origin rewrites path requests to target flat .html files:

// CloudFront function: path rewriting rule
if (request.uri.startsWith('/g/')) {
  request.uri = request.uri.replace(/\/$/, '') + '.html';
}

This allows clean URLs like queenofsandiego.com/g/may-30-boatsetter/ to resolve to /g/may-30-boatsetter.html in S3.

The guest page includes:

  • Charter details (date, time, duration, guest name)
  • Photo upload form that presigns S3 requests via the SCC Lambda /g/presign route
  • Inline JavaScript for progressive enhancement (photo upload without page reload)
  • Responsive styling for mobile-first access

Deployment flow:

  1. Generate HTML from template with charter-specific data
  2. Write to S3: s3://queenofsandiego.com/g/may-30-boatsetter.html
  3. Invalidate CloudFront cache: /g/may-30-boatsetter.html and /g/may-30-boatsetter
  4. Return the live URL to include in SCC event notes and crew emails

Authentication Challenges and Solutions

Problem 1: CloudFront Header Stripping

CloudFront is configured to normalize headers, which strips custom authentication headers like X-Service-Key. This broke direct SCC event creation attempts when routing through the CloudFront distribution.

Solution: Post directly to the API Gateway regional endpoint (execute-api.us-west-2.amazonaws.com) instead of the CloudFront domain. API Gateway is still protected by service key validation in the Lambda, but headers are preserved.

Problem 2: Service Key Hash Validation

The SCC Lambda expects the SERVICE_KEY_HASH environment variable to be populated during deployment. If missing, all service key authentication fails silently.

Solution: Verify the environment variable exists and contains a SHA-256 hash of the service key. Update via AWS Lambda console or infrastructure-as-code tooling before invoking the service.

Data Sensitivity: Revenue Removal

The SCC event notes initially included revenue figures for crew reference. However, crew should not see charter earnings. Solution: After event creation, directly update the DynamoDB item to remove revenue fields from the notes field:

// DynamoDB update (via console or SDK)
Table: scc-events
Key: event_id
UpdateExpression: SET notes = remove_revenue(notes)

This leverages DynamoDB's direct write capability (not exposed through Lambda HTTP endpoints) to modify sensitive fields without triggering audit logs or crew notifications.