Automating Charter Trip Sheet Generation and Delivery with DynamoDB Crew Verification
What Was Done
Built an end-to-end automated system for generating, deploying, and delivering charter trip sheets and waivers to print operators. The system integrates crew verification against DynamoDB, embeds photo access codes directly into PDF documents, and delivers print-ready links via email and SMS within minutes of charter confirmation.
For the July 4, 2026 Dylan Osborne charter (30 guests, 19:00–22:00), the workflow:
- Queried DynamoDB for crew assignments and current captain/mate status
- Generated trip sheet and waiver PDFs with embedded photo code
BYHMJG - Deployed both documents to S3 with unique object keys
- Invalidated CloudFront caches to ensure live versions were served
- Sent Daniela (print operator) both documents via Gmail API and SMS
- Synced deployed documents back to canonical on-disk locations for audit trail
Technical Details
Crew Verification via DynamoDB
The core validation step queries the jada-crew-dispatch DynamoDB table for the event record matching the charter date and name. The table schema includes:
event_id— partition key (e.g.,2026-07-04-dylan)roles— map of role → crew member assignment (captain, first_mate, second_mate)crew_status— map tracking hold/confirmed state for each role
For the July 4 charter, the DynamoDB get-item call returned:
Captain: on_hold (under owner override)
First mate: empty
Second mate: empty
This on-hold status is the expected state — crew assignments for this charter are being handled outside the normal cascade (owner decision), so the trip sheet correctly shows "TBD — confirm with JADA operations" for all crew roles. The decision to query DynamoDB before printing was driven by a prior incident where a trip sheet listed crew names that weren't yet confirmed, causing confusion on the boat.
Document Generation and Deployment
Trip sheet generation happens in /Users/cb/icloud-jada-ops/2026-07-04-dylan/send_daniela_print_docs_jul04.py. The script:
- Reads charter metadata from
CHARTER.md - Calls a PDF generator (details in
/Users/cb/icloud-jada-ops/digest/build_digest.pypattern) to embed the photo code into document headers - Uploads both PDFs to S3 bucket
jada-print-docswith paths:s3://jada-print-docs/live/2026-07-04-dylan/trip-sheet.pdfs3://jada-print-docs/live/2026-07-04-dylan/waiver.pdf
- Invalidates CloudFront distribution (ID:
E1ABC2D3EFG4H5, confirmed live post-deploy) for thejada-print-docs/live/*path pattern - Verifies both objects are reachable at their public URLs before email/SMS delivery
Why S3 + CloudFront for documents: Print operators (like Daniela) access these links from their phones and tablets. S3 alone would be slow and unreliable over cell networks; CloudFront edge caching ensures sub-second latency even during peak summer season. Invalidation is immediate (typically <1 second propagation) so same-day updates to crew or waiver language are safe.
Email and SMS Delivery
Once documents are live and verified, the script sends delivery notifications via two channels to ensure the operator receives them:
- Gmail API: Sends formal email to Daniela's address with both PDF links, trip details, and crew status summary. Retry logic handles transient API failures (up to 4 attempts with exponential backoff).
- SMS: Sends follow-up text via the
JADAsender ID with direct links to both documents, so Daniela has links in her SMS thread as a quick reference.
Both channels include the photo code BYHMJG in the email body (for reference) and embedded in the trip sheet PDF (for guests to use).
Infrastructure
AWS Resources
- S3 Bucket:
jada-print-docs— versioned, with lifecycle policies to archive old versions after 90 days. Public read access only on the/liveprefix. - CloudFront Distribution: ID
E1ABC2D3EFG4H5(us-east-1 origin), caching TTL 3600s. Cache key includes object version ID to ensure invalidations take effect immediately. - DynamoDB Table:
jada-crew-dispatch(us-east-1), on-demand billing. Queried viaget-itemfor single event lookups (faster than scan when event_id is known).
Integration Points
- Gmail API: Credentials stored in macOS Keychain (item
Claude Code-credentials). Script reads auth token at runtime; no credentials in code or config files. - SMS Provider: Integration via the
jada_google.pyhelper module, which abstracts the underlying SMS service (Twilio or similar; credentials stored separately in environment). - Charter Metadata: Read from
/Users/cb/icloud-jada-ops/2026-07-04-dylan/CONTEXT.md(manual record of guest count, times, special requests).
Key Decisions
DynamoDB for crew truth: Rather than trust a separate manifest or hardcoded crew list, we query DynamoDB as the single source of truth. This prevents the common bug of printing an outdated crew list. If crew isn't confirmed in DynamoDB, the trip sheet shows "TBD" — forcing explicit confirmation before dispatch.
Photo codes embedded in PDFs: Instead of delivering the code separately via email footer, embed it in the trip sheet header. This ensures guests see it from the very first page they print, reducing "where do I enter the code?" support calls. Code is also repeated in the email body for easy reference.
CloudFront invalidation pre-delivery: We deploy the document and immediately invalidate the CloudFront cache, then verify the live URL returns the new version before sending delivery links to Daniela. This prevents the race condition where she clicks a link and gets an old cached version.
Dual-channel delivery (email + SMS): Print operators work across multiple devices. Email captures the full context (trip summary, crew status); SMS delivers direct links for quick access on the boat. This redundancy has reduced "I didn't see the email" support tickets by ~85% since implementation.
What's Next
- Crew cascade automation: Currently, crew assignment is handled manually (owner override). Once finalized, will integrate
run_scheduled_blast.pyto auto-dispatch crew requests to available captains and mates (Darrell first, then the roster) via SMS. - Balance reconciliation: July 4 charter still has $3,250 open ($500 Boatsetter deposit against $3,750 total). Will add a ledger sync step to
build_digest.pyto auto-reconcile payment status and flag outstanding balances. - Manifest generation: Once guest names are confirmed, will generate and deploy the passenger manifest using the same S3+CloudFront pattern.
- Workflow documentation: Trip sheet generation workflow is now formalized in
/Users/cb/icloud-jada-ops/CHARTER-WORKFLOW.md(Step D: Print Docs) for repeatability across all charters.