```html

Automating GetMyBoat Inquiry Handling: Building a Structured Response Pipeline for Charter Operations

Sail JADA's booking workflow had a critical gap: while confirmed bookings flowed seamlessly from GetMyBoat through Gmail into Google Calendar via platform_inbox_scraper.py, the inquiry stage remained entirely manual. Customers would submit inquiries, but there was no systematic way to acknowledge them, block calendar slots provisionally, or track negotiation state. This post documents how we closed that gap by extending our existing automation stack to handle inquiries as first-class booking objects.

The Problem: Inquiries Were Invisible to Automation

GetMyBoat inquiries arrive in Gmail like any other message, but they require human judgment:

  • Is the requested date available?
  • Which vessel matches their party size and preferences?
  • Should we offer alternative dates if their first choice is booked?
  • Has payment been secured, or is this still negotiation?

Previously, Carole handled this manually: reading the inquiry, crafting a response, placing a tentative hold on Google Calendar, and tracking follow-ups via email. With her departure, that workflow vanished. We needed to automate the inquiry intake and provisional booking stages without losing the judgment calls that require human review.

Architecture: Extending the Existing Scraper

Rather than building a new system, we extended platform_inbox_scraper.py (located at ~/JADA/automation/platform_inbox_scraper.py) to classify incoming GetMyBoat emails into two categories:

  • Confirmed Bookings: Customer has paid, dates locked → dispatch to crew/cleaners (existing behavior)
  • Inquiries: Customer has asked availability, prices, or custom requests → new handler

The classifier works by regex-matching against GetMyBoat's standard email templates. A confirmed booking includes a confirmation number and "Your booking is confirmed"; an inquiry includes "Customer inquiry" or "wants to know about" but no payment receipt.

Implementation: The Inquiry Handler Module

We created a new module, inquiry_handler.py, within the same directory:

# ~/JADA/automation/inquiry_handler.py

class GetMyBoatInquiry:
    def __init__(self, email_body, sender, subject):
        self.email_body = email_body
        self.sender = sender
        self.subject = subject
        self.inquiry_id = self._extract_inquiry_id()
        self.requested_dates = self._parse_dates()
        self.party_size = self._parse_party_size()
        self.vessel_preference = self._parse_vessel_preference()
        self.message = self._extract_message()
    
    def _extract_inquiry_id(self):
        # GetMyBoat inquiry emails contain an inquiry ID in the subject or body
        match = re.search(r'Inquiry #(\d+)', self.subject)
        return match.group(1) if match else None
    
    def _parse_dates(self):
        # Extract requested date range; often in format "Jan 15-17, 2024"
        date_pattern = r'(\w+ \d{1,2}(?:-\d{1,2})?,?\s*\d{4})'
        matches = re.findall(date_pattern, self.email_body)
        return [parse(m) for m in matches]
    
    def _parse_party_size(self):
        # Look for "party of X" or "X guests"
        match = re.search(r'(?:party|group|guests?)\s+(?:of\s+)?(\d+)', self.email_body, re.I)
        return int(match.group(1)) if match else None
    
    def _parse_vessel_preference(self):
        # Extract vessel name if mentioned
        match = re.search(r'(?:JADA|vessel|boat|yacht).*?([A-Z][a-zA-Z]+)', self.email_body)
        return match.group(1) if match else None
    
    def _extract_message(self):
        # Pull the customer's actual message for human review
        lines = self.email_body.split('\n')
        # Typically starts after the inquiry metadata section
        return '\n'.join(lines[-5:])

Once parsed, the inquiry object is stored in a DynamoDB table named JADA_GetMyBoat_Inquiries with attributes:

  • inquiry_id (partition key)
  • timestamp (sort key)
  • sender_email, sender_name
  • requested_dates (JSON)
  • party_size, vessel_preference
  • message (full customer message)
  • status (enum: pending, responded, confirmed, declined)
  • calendar_hold_event_id (optional; points to Google Calendar tentative event)

Provisional Calendar Holds: The Missing Piece

When an inquiry is detected, we immediately create a tentative Google Calendar event using the CalendarSync.gs infrastructure. This blocks the slot so that concurrent inquiries don't double-book the same dates.

The event is created with:

  • Title: 📋 INQUIRY: [Customer Name] – [Vessel]
  • Description: Links to the DynamoDB record and includes a quick-access response template
  • Transparency: transparent (doesn't block the slot in terms of availability displayed to customers)
  • Color: Gray (to visually distinguish from confirmed bookings)
  • Duration: The requested date range

The event ID is stored back in the DynamoDB record. If the inquiry is declined, the event is deleted; if confirmed, it's updated to opaque and colored green.

Response Workflow: Semi-Automated with Human Gate

Rather than auto-responding (which could miss nuance or custom pricing), we:

  1. Send a Slack notification to the #bookings channel with a summary: customer name, dates, party size, and a link to the full message in DynamoDB.
  2. Include two buttons: "Quick Respond (Standard Offer)" and "Review Details" (links to a web form).
  3. If "Quick Respond" is clicked, a templated response is drafted and placed in a Google Doc (in the JADA Booking Responses shared folder) for final human review before sending.
  4. The human operator reviews, optionally customizes, then sends the response via GetMyBoat's web interface or an API call.

The templated response includes current pricing, availability alternatives (if the requested dates are unavailable), and a link to pay the deposit.

Infrastructure: New AWS Resources

This system required minimal new infrastructure:

  • DynamoDB Table: JADA_GetMyBo