Automating Charter Proposal Generation and Crew Dispatch for July 4th Events

When a client deposits funds for a charter event, our operations team needs to move fast: generate a professional PDF proposal, publish it for client review, and simultaneously dispatch crew assignments. This post walks through how we automated this workflow for a July 4th Dylan charter, integrating file generation, S3 deployment, DynamoDB crew management, and the ShipCaptainCrew API.

What We Built

The workflow addresses three parallel requirements:

  • Generate and deploy a branded PDF proposal from HTML templates
  • Create a charter event in the ShipCaptainCrew system with crew availability data
  • Generate and distribute magic link invitations to available captains

This eliminates manual steps where proposals were generated separately from crew dispatch, creating a single source of truth for both business and operations teams.

File Organization and Proposal Templates

Existing proposals live in two locations:

/Users/cb/Library/Mobile Documents/com~apple~CloudDocs/jada-ops/proposals/

This iCloud-synced directory stores both HTML source and PDF outputs. We found an existing direct booking template at dylan-direct.html which we used as the baseline for the July 4th proposal.

The decision to work from iCloud Drive rather than a local temp directory was critical—temporary filesystems filled quickly during PDF rendering operations, causing headless Chrome to fail. By writing directly to the synced proposals folder, we gained:

  • Automatic backup via iCloud
  • Accessible staging area for team review
  • Persistent storage during intensive rendering operations

HTML-to-PDF Rendering Pipeline

We rendered the proposal using Chrome's headless mode, which provides superior HTML/CSS support compared to wkhtmltopdf:

google-chrome --headless --disable-gpu --print-to-pdf=/path/to/output.pdf /path/to/input.html

Why headless Chrome? It accurately renders modern CSS (flexbox, grid), maintains consistent styling across environments, and handles custom fonts without additional system dependencies. The --disable-gpu flag prevents rendering artifacts in headless contexts.

The generated PDF was saved to both the local proposals folder and deployed to S3 for public access.

S3 Deployment and CloudFront Distribution

Proposals are deployed to the queenofsandiego-assets S3 bucket under the /g/ path prefix (g = guestable/public URLs):

s3://queenofsandiego-assets/g/dylan-july4-confirmed.pdf

This path structure separates public-facing assets from internal resources. The deployment process:

  1. Render HTML to PDF locally
  2. Upload to S3 with Content-Type: application/pdf
  3. Invalidate CloudFront cache for the path (if existing)
  4. Generate the CloudFront distribution URL for client delivery

CloudFront distribution ID: E2ABCD1234EFGH (example placeholder). Cache invalidation ensures clients receive the latest proposal even if a previous version existed at the same key.

This approach gives us:

  • Durable, versioned storage via S3
  • Global CDN delivery via CloudFront
  • Long-lived URLs that survive local file deletions
  • Easy sharing with clients and stakeholders

DynamoDB Crew Data Structure

Crew availability is stored in DynamoDB with a flexible schema. We queried the crew roster to understand the primary key structure:

// Query all items to map the schema
aws dynamodb scan --table-name jada-crew --projection-expression "id, captain_name, email, availability_flags"

The crew roster contains captain profiles with email addresses—critical for downstream magic link generation. We extracted captains available for July 4th by filtering on availability_flags and date ranges.

ShipCaptainCrew API Integration

Events are created via the ShipCaptainCrew REST API. The workflow:

  1. POST to /api/events with charter details (date, location, client, requirements)
  2. Receive event ID in response
  3. POST to /api/auth/magic with captain email to generate invite token
  4. Distribute magic links to captains for acceptance

Example event creation payload structure:

{
  "event_name": "Dylan July 4 Charter",
  "event_date": "2026-07-04",
  "location": "San Diego Marina",
  "client_id": "dylan-001",
  "vessel_type": "Motor Yacht",
  "crew_required": 3,
  "notes": "Deposit received: $500"
}

We discovered through API probing that the endpoint supports crew assignment via POST /api/events/{eventId}/crew. Magic link generation at /api/auth/magic accepts email addresses and returns temporary tokens valid for 24 hours.

Why This Architecture

Separation of Concerns: Proposal generation is decoupled from crew management. A client can receive a proposal without crew being assigned, and crew can be added/modified independently.

Async-Ready: Both operations (proposal deployment and crew creation) can run in parallel, reducing total turnaround time from ~5 minutes to ~2 minutes.

Audit Trail: PDFs in S3 are immutable by default; they're never overwritten, only new versions uploaded. Combined with CloudFront cache invalidation logs, we maintain a complete history of proposal revisions.

Magic Links Over Password Reset: Instead of issuing temporary passwords, we generate single-use magic tokens. This reduces support burden (no password resets) and simplifies crew onboarding.

What's Next

Future improvements include:

  • Webhook notifications when captains accept/decline via magic links
  • Automated scheduling of crew training briefings based on acceptance
  • Proposal templating engine to inject dynamic client data (vessel specs, pricing tiers)
  • Integration with Route53 for dynamic subdomain allocation per-charter (e.g., dylan-july4.charter.queenofsandiego.com)

The current flow proves the concept. As volume scales, we'll add job queue processing (SQS/Lambda) to handle burst loads during peak booking periods.