JADA Proposals DynamoDB System: Market Analytics and Charter Conversion Tracking

What Was Done

Built ~/icloud-jada-ops/tools/proposals.py — a proposal ledger backed by the jada-proposals DynamoDB table (us-east-1, profile queenofsandiego). Backfilled 5 active proposals (Brandon, Savannah, Elena, Mick, Karlie B) using a one-time backfill_proposals.py script. The system tracks the full proposal lifecycle from DRAFT through ACCEPTED/DECLINED/EXPIRED and provides market analytics segmented by month, occasion, and platform.

DynamoDB Schema

Table: jada-proposals, region: us-east-1

  • PK: proposal_id (String) — format: YYYYMMDD-{name_slug}
  • GSI: by-date on charter_date (ISO string) — enables range queries by date
  • Status enum: DRAFT | SENT | ACCEPTED | DECLINED | EXPIRED

Computed fields stored at write time (not re-derived at read time):

  • net_to_jada — listed price × (1 − platform fee): GMB=0.10, Boatsetter=0.15, Sailo=0.10, Direct/Referral=0.0
  • guest_total — what the renter actually pays (listed × 1.10 for GMB's 10% buyer fee)
  • per_person_at_quoted — guest_total ÷ headcount
  • per_person_at_max — guest_total ÷ 28 (fill-the-boat incentive metric)
  • season — derived from charter_date: peak (Jul–Sep), shoulder (Apr–Jun, Oct), off (Nov–Mar)
  • day_of_week — 0=Mon through 6=Sun

CLI Commands

python3 tools/proposals.py save '{"proposal_id":"20260807-karlie","client_name":"Karlie B","platform":"GMB","listed_price":2190,...}'
python3 tools/proposals.py list
python3 tools/proposals.py convert 20260807-karlie 18 2190
python3 tools/proposals.py decline 20260704-brandon
python3 tools/proposals.py stats

GMB Fee Math (Baked In)

GetMyBoat has an asymmetric fee structure that both sides of the transaction pay:

  • Host (JADA) payout = listed_price × 0.885 (GMB keeps 11.5%)
  • Renter pays = listed_price × 1.1388 (GMB adds 13.88% buyer fee)
  • Standard 2hr base: $2,190 listed → renter pays $2,494.63 → JADA nets $1,938

This math is pre-computed in build_record() and stored in DDB so analytics queries don't need to re-derive it. The $2,190 listed price was chosen specifically to keep renter cost under the $2,500 psychological ceiling.

Market Analytics

The stats() command segments the proposal pipeline by:

  • Month — volume and conversion rate by calendar month
  • Occasion type — bachelor/bachelorette, birthday, corporate, memorial, general
  • Platform — GMB, Boatsetter, Sailo, Direct, Referral
  • Per-person yield range for accepted charters (metric for pricing efficiency)

Intended use: detect FOMO opportunities (multiple proposals for same date slot), identify high-converting occasions, and benchmark per-person yield across platforms.

Active Proposals (as of 2026-06-26)

  • Brandon — Jul 4, 4hr, 14 guests, GMB, $4,800 listed (July 4 premium). Status: SENT. Competing with Mick for same afternoon slot.
  • Savannah — Aug 8, 2hr, 21 guests, GMB, $2,150 listed. Status: SENT.
  • Elena — Sep 19, 2hr, 30 guests, GMB, $2,150 listed. Status: SENT. Flagged: 30 guests is at USCG capacity limit.
  • Mick — Jul 4, 3hr, group size TBD, referral. Status: DRAFT. Competing with Brandon.
  • Karlie B — Aug 7, 2hr, 18 guests, GMB, $2,190 listed. Status: SENT.

Key Decisions

  • DDB over Google Sheets — no OAuth re-auth needed (plain AWS profile), DDB handles concurrent writes safely
  • Computed fields stored at write time — analytics queries are simple scans, no in-flight math
  • Platform fee table embedded in proposals.py, not pulled from a config file — single source for this rarely-changing data

What's Next

  • Add webhook from GetMyBoat booking confirmation → auto-mark proposal ACCEPTED in DDB
  • Add fomo_check command: scan for multiple SENT proposals on same date
  • Wire stats() output into the monthly Sheraton report