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_namerequested_dates(JSON)party_size,vessel_preferencemessage(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:
- 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.
- Include two buttons: "Quick Respond (Standard Offer)" and "Review Details" (links to a web form).
- If "Quick Respond" is clicked, a templated response is drafted and placed in a Google Doc (in the
JADA Booking Responsesshared folder) for final human review before sending. - 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