Boulder Creek
Operations
Setup runbook · about 10 minutes, done once

The Booking Sheet

This is already done. The Boulder Sheet is live, the script is deployed, the calendar is connected, and the board is reading all of it. Steps 1, 3 and 4 below are history, not homework. Keep this page to look up what a column means, and to redeploy the script after any edit to Code.gs (step 2).

When this is done, the Sheet is the master for its rows and the calendar is the master for its events. The team edits those. Nobody edits the board. The board just shows what they say.

Web app · a tiny program Google runs for you at an address. Ours reads the Sheet and hands the rows to the board.
The /exec URL · that address. Anything that opens it gets the current data back as text.
1

The spreadsheet already done

Upload 26-0717-BC-BOOKING-SHEET-READY.xlsx (it's in the GDrive 08-BOOKING-SYSTEM folder) to the Boulder Google Drive, open it, then File > Save as Google Sheets. Done. The tabs, headers, and the whole current season are already in it.

Google keeps the original file and gives you a live Sheets copy; work in the copy. If you'd rather build the spreadsheet yourself, see the appendix at the end.

The field tables below are reference now, not instructions. The ready file already carries every column. Use the tables when you want to know what a column means or how to fill a cell.

Units tab · field names, in order

FieldWhat it is
idShort name-tag for the unit, lowercase with dashes: cedar-chalet. Every row needs one.
nameThe name people say: Cedar Chalet
typeOne of: Lodging, Event, Training, Outdoor. Bed math only counts Lodging rows.
groupThe section it sits under on the board: Cabins & Suites, Dorm Village, Event & Training, Outdoor
sleepsHow many beds, a number. 0 for event spaces.
event_capacityHow many people the space holds for an event. Empty for plain lodging.
configThe bed or room mix, in words: 1 King, 1 Queen, 5 Twin
rateThe headline rate, just the number: 300
rate_basisWhat that rate means, in words: per night (occ 6, +$30/extra)
off_season_rateOff-season number, or empty if not set
kitchenTRUE or FALSE
shared_bathTRUE or FALSE
family_friendlyTRUE or FALSE
quietTRUE or FALSE
bookable_nowTRUE or FALSE
map_xMap position, left to right (percent, 0 to 100) on the Campus view aerial. Leave empty to use the position saved in the board's data.json.
map_yMap position, top to bottom (percent, 0 to 100). Same note.
notesAnything worth knowing about the unit

Bookings tab · field names, in order

FieldWhat it is
idShort name-tag for the booking, lowercase with dashes: amanda-likhita-aug. Every row needs one; it is how clicks and links find the booking.
nameWhat the team calls it: Women's Retreat, Amanda & Likhita
orgThe organization behind it, or empty
event_typeIn words: Retreat, Wedding, Lodging stay, Family reunion, Produced event, Partner event, Community event, Corporate retreat
statusExactly one of these words: Open, Inquiry, Qualified, Quoted, Pending contract, Confirmed. (Held still works; it is the old word for Pending contract.)
party_sizeHow many people, a number, or empty
arriveArrival date: 2026-08-27. A real Sheet date works too.
departDeparture (checkout) date. Nights = depart minus arrive.
day_eventTRUE for a one-day event. Set depart to the next day so it paints on its date. Otherwise leave empty.
spacesThe unit ids this booking uses, comma-separated in one cell: cedar-chalet, grotto. Ids must match the Units tab id column. Leave empty if units are not itemized yet.
extrasEverything beyond buildings and beds, free text in one cell: amenity access (pool, sauna, volleyball), activities, which part of campus, whatever helps. Line breaks inside the cell are fine and show as written on the booking's detail panel. Or empty.
bedsBeds the party uses, a number, or empty
quoteQuoted total, just the number, or empty
depositDeposit amount, just the number, or empty
deposit_paidTRUE once paid, FALSE while owed, empty if no deposit yet
contactThe person's name
emailTheir email
phoneTheir phone
notesThe story of the booking: counts due, open questions, anything the team should see
partiesReserved for multi-party bookings, comma-separated. Fine to leave empty.
assumedOptional. Field names whose values are a best guess rather than on file, comma-separated: spaces, party_size. The board marks those with a dashed "assumed" tag. Leave empty when everything is confirmed.

The board needs id, name, status, arrive and depart on every booking, and id, name, type, group and sleeps on every unit. Everything else can sit empty; the board shows "not on file". Column order does not matter, the header names do.

2

The little program redo after any Code.gs edit

  1. In the Sheet, open Extensions > Apps Script.
  2. Delete the starter code in the editor.
  3. Paste in everything from Code.gs (open it here) and hit the save icon.
  4. Click Deploy > New deployment.
  5. Click the gear next to "Select type" and pick Web app.
  6. Set Execute as: Me, and Who has access: Anyone with the link.
  7. Click Deploy, approve the permissions when Google asks (it is your own script reading your own Sheet), and copy the URL that ends in /exec.
Check it worked: paste that /exec URL into a browser tab. You should see your rows come back as text, starting with {"units":
3

Point the board at the Sheet already done

  1. Open booking/app.js in the site repo.
  2. The first line of code reads:
    const CONFIG = { SHEET_JSON_URL: "" };
  3. Paste the /exec URL between the quotes:
    const CONFIG = { SHEET_JSON_URL: "https://script.google.com/macros/s/XXXX/exec" };
  4. Push the change. Vercel redeploys the site on its own.
4

The team calendar already done

Optional · 2 more minutes

The Boulder team's standard process is the Google Calendar: the event goes on the calendar, and the dorm and the notes go in the event's description. The board can read that calendar directly. So the team keeps doing exactly what they do today: put the event on the calendar, dorm and notes in the description. The board picks it up on its own.

The calendar ID ships inside Code.gs โ€” it points at BCR Events (NEW), the calendar the team edits. Nothing to find or type. The script only reads the calendar, never writes to it. Two things to know:

  1. The account that deployed the web app (events@bouldercreekretreat.com) must be able to see that calendar โ€” it should sit under Other calendars in that account's calendar list. If the feed ever answers calendar_error: No calendar found, share the calendar to that account with See all event details and fetch again. Sharing needs no redeploy.
  2. Ever pointing at a different calendar means editing the CONFIG line at the top of Code.gs and deploying a new version.

How calendar events land on the board:

  • The event title becomes the booking name and the dates carry over. The feed reads events from July 1 through about two months past the end of the year. A one-day event shows as a day event.
  • Any line of the description that names a unit (Cedar Chalet, Tamarack Dorm) puts that unit on the booking. Spelling matters, capitals do not.
  • Every other line of the description shows under Notes on the booking's detail panel, exactly as written.
  • Calendar bookings show as Confirmed, with a note saying they came from the Boulder calendar. On the calendar means it is real.
  • If the same booking lives in both places (same name, same arrival date), the Sheet row wins and the calendar copy is skipped. Move a booking into the Sheet when it needs the richer record: money, contacts, a status other than Confirmed.
5

Done. Here is how it behaves now.

  • The board is live off the Sheet. Edit a row, reload the board, see it.
  • With the calendar connected, calendar events show on the board too. Edit the event, reload the board, see it.
  • One master per fact. The Sheet owns its rows, the calendar owns its events. Nobody edits the board.
  • If the Sheet feed ever fails (deployment removed, no signal, whatever), the board quietly falls back to the last data.json in the repo and notes it in the browser console. Fix the feed and it comes right back.
  • If you later edit the script, redeploy it: Deploy > Manage deployments > pencil icon > Version: New version > Deploy. The /exec URL stays the same.

Where a change goes

  • Dates, new events, day-to-day notes — the team calendar (BCR Events). The board shows every calendar event as Confirmed; whatever the description says beyond unit names rides along as the booking's notes.
  • Money, contacts, statuses, headcounts — the Booking Sheet. Copy the event's name and arrival date into a row and fill the richer fields; from then on the Sheet row is the record, and the calendar copy quietly steps aside on the board. The calendar event can stay for the team's calendar view.
  • Rates — the pricing workbook first; the price page follows it.
  • One master per fact, never two. The board edits nothing — every booking's detail panel links to the place it is edited.

Updating the script

Any change to Code.gs means a fresh paste: replace everything in Extensions > Apps Script with the new file โ€” the calendar ID ships inside it, so there is nothing to re-type. Then Deploy > Manage deployments > pencil icon > Version: New version > Deploy. The /exec URL does not change, so the board needs nothing.

+

Building it by hand instead

Appendix

If you ever start from a blank Google Sheet instead of the ready file: make two tabs named exactly Units and Bookings (double-click a tab name to rename it). Row 1 of each tab holds the field names from the tables in step 1, spelled exactly (lowercase, with underscores). Every row under row 1 is one unit or one booking. Column order does not matter, the header names do.