Enquiries
All channels · click a row to open details
| Contact | Source | Destination | Status | Package interest | Assigned | Notes | Received |
|---|
Pipeline
Leads by conversion stage
Hotel & Stay Prices
Base nightly rate · all locations
| Property | Tier | Base /night | Peak /night | Off /night |
|---|
Tour Packages
Per-person from prices shown on homepage
| Package | Route | Duration | Base /person | Peak /person |
|---|
Date-based Price Rules
Overrides applied automatically by date · first matching rule wins
FromToRule name
SnowlineFloret HotelAlpine
Change Log
All price updates and lead status changes
System Architecture
How leads flow from each channel to the dashboard — read-only source of truth design
Source 1
WhatsApp Business
Customer sends a message → Zapier catches webhook → appends row to Master Sheet
via ZapierSource 2
Instagram DMs
DM or comment → Make/Zapier → normalised row → Master Sheet
via MakeSource 3
Website form
Contact form submits → Google Apps Script → appends row to Master Sheet
Apps Script↓
Source of Truth (owner only)
Master Google Sheet
All leads land here. Protected: automation can only append rows — it cannot edit or delete existing data. Staff have view-only access to this sheet.
Append-only API
Owner-protected
↓ ↓
Owner access
This dashboard
Reads Master Sheet via API. Owner can update lead status, add notes, change prices. All writes go back to Master Sheet only — never overwrites raw data.
Password protected
Staff access
Staff Google Sheet
A second sheet that syncs from Master. Staff can only edit: Status, Notes, Assigned-to columns. They cannot delete rows or see raw source data. Changes sync back to Master every 5 min.
No delete permission
Google Apps Script (website form → Master Sheet)
Paste this script in Google Sheets → Extensions → Apps Script. Deploy as Web App (anyone can access). Use the deployment URL as your form's POST endpoint.
function doPost(e) {
var data = JSON.parse(e.postData.contents);
var sheet = SpreadsheetApp.openById('YOUR_SHEET_ID')
.getSheetByName('Leads');
sheet.appendRow([
new Date(), // timestamp
data.name,
data.phone,
data.email,
data.destination,
data.package,
data.message,
'website', // source
'new', // status — automation sets only
'', // notes — staff fills this
'', // assigned_to
]);
return ContentService
.createTextOutput(JSON.stringify({status:'ok'}))
.setMimeType(ContentService.MimeType.JSON);
}
Zapier recipe — WhatsApp Business → Master Sheet
1. Trigger: WhatsApp Business → New Message
2. Action: Google Sheets → Create Spreadsheet Row
3. Map: Sender Name → name · Sender Phone → phone · Message Body → message · set source = "whatsapp" · set status = "new"
4. Turn on the Zap. New WhatsApp enquiries land in Master Sheet automatically.
2. Action: Google Sheets → Create Spreadsheet Row
3. Map: Sender Name → name · Sender Phone → phone · Message Body → message · set source = "whatsapp" · set status = "new"
4. Turn on the Zap. New WhatsApp enquiries land in Master Sheet automatically.
Protecting the Master Sheet (critical)
In Google Sheets: Data → Protect sheet and ranges
→ Protect columns A–I (all raw capture columns) — only you can edit
→ Allow staff to edit only columns J (Notes), K (Assigned), L (Status)
→ Share the sheet with staff as Editor but with column-level protection above
→ Automation service account gets only appendRow scope — not read/write full sheet
→ Protect columns A–I (all raw capture columns) — only you can edit
→ Allow staff to edit only columns J (Notes), K (Assigned), L (Status)
→ Share the sheet with staff as Editor but with column-level protection above
→ Automation service account gets only appendRow scope — not read/write full sheet
Settings
Change password
Google Sheets integration
Price API Secret
This must match the PRICES_SECRET environment variable set in your
Vercel project settings for floret-journeys.
The dashboard sends this as a Bearer token when publishing prices.