Hey folks,
This one is about operations automation — not AI agents, not dashboards, not a platform rebuild. Just two workflows that were quietly eating hours every week at a 100+ property vacation-rental portfolio, and how we wired them up with n8n so the ops team could stop doing the same manual work every morning.
One workflow finds money sitting between reservations. The other keeps pool heaters on when guests arrive and off when they leave — across four cities, two equipment vendors, and a spreadsheet nobody fully trusted. Both problems sound small until you multiply them by a hundred properties.
Here's what we built, what broke along the way, and the pattern that held up across both.
Two problems, same shape
At Heirloom, the pain showed up in two places that looked unrelated but shared the same failure mode: too many systems, too much judgment, not enough consistency.
Gap nights. When two confirmed reservations leave a one- or two-night hole on the calendar, that's lost revenue. Someone has to spot the gap, price an offer, pick which adjacent guest to contact, and hand the ops team a ready-to-send message. Doing that by hand across a large portfolio means most gaps never get touched.
Pool heaters. Over a hundred properties across New Orleans, Savannah, Nashville, and Austin run Hayward OmniLogic or Jandy iAqualink systems. Heater rules depend on season, pool size, heater type, guest check-in status, and calendar blocks in Guesty — Turnover, Early Check-in, Maintenance, and a dozen variants recorded inconsistently by different reps. The team tracked all of this in Google Sheets and cross-checked vendor portals. The sheet drifted from reality. Heaters ran when they shouldn't. Guests arrived to cold pools.
Neither problem needed a new product. Both needed a reliable loop: pull state from the systems of record, apply the business rules, write the result somewhere humans can audit, and act.
Two unrelated ops problems, one automation shape: pull state, apply rules, leave an audit trail, act.
Why n8n
We had options. A cron job in a repo. A one-off Lambda. A full internal tool. n8n won for a specific reason: these workflows are integration-heavy, not compute-heavy. They fan out across Guesty, Google Sheets, Redis, Hayward, and iAqualink. They need retries, branching, and a visual graph that ops and engineering can both read. n8n is built for exactly that — glue code with a UI, scheduled triggers, and Code nodes when the logic gets hairy.
The split we landed on: n8n orchestrates, a service computes when the logic outgrows a workflow. Gap night stayed entirely in n8n. Pool heaters got a companion FastAPI service because the rules engine and multi-vendor API coordination were too much to maintain inside Code nodes.
Gap night: finding money between reservations
The gap-night workflow runs on a daily cron at 7 AM. Every morning it asks one question across the portfolio: did any new confirmed reservations create a one- or two-night hole we can sell?
The flow is linear once you see it:
- Authenticate to Guesty (token cached in Redis so we're not re-OAuthing on every node)
- Fetch reservations confirmed in the last 24 hours to discover which listings changed
- Pull all confirmed reservations for those listings over the next 30 days
- Walk consecutive bookings per listing and flag 1-night and 2-night gaps
- Fetch calendar pricing for each gap date
- Calculate the offer price
- Pick which adjacent guest gets the offer
- Log everything to Google Sheets
- Create a Guesty task for the ops team with the full breakdown, email template, and call script
Every morning: find gaps, price them, pick a guest, hand the ops team a ready-to-act task.
The pricing engine
The pricing logic is where most of the business knowledge lives, and it all runs inside n8n Code nodes. The pipeline has six steps:
Listed rate comes from the Guesty Calendar API for the gap date, falling back to the listing's base price, then to a $200 default.
Normalization strips the single-night premium. Most properties charge roughly 70% more for one-night stays to cover turnover. For 1-night gaps, we divide the listed rate by 1.7. Two-night gaps keep the listed rate as-is.
Floor ensures the normalized rate never drops below bedrooms × $50.
Discount depends on three inputs: bedroom count, day of the week, and how many days until the gap. A Monday 14 days out on a 2-bedroom gets 75% off. A Saturday 64 days out gets only 10% off. The further out, the less aggressive the offer.
Minimum flat rate caps how low we'll go — $50 for a 1BR, $80 for a 2BR, up to $200 for a 5BR — regardless of what the discount math says.
Event multiplier bumps the price during high-demand windows: New Year's Eve at 1.5×, Independence Day at 1.4×, Memorial Day and Labor Day at 1.3×.
A Friday gap 14 days out on a 2-bedroom with a $1,400 calendar price lands around $330. A Christmas Eve gap on the same property might land at $132 after the holiday multiplier kicks in. The ops team doesn't do any of that math. The task arrives with the full breakdown.
Who gets the offer
Pricing is only half the problem. The other half is who do we ask? Every gap has two adjacent reservations — the guest checking out and the guest checking in. The selection logic runs in priority order:
- Platform priority: Manual/Website bookings beat VRBO, which beats Booking.com, which beats Airbnb. Direct guests are easier to upsell.
- The 50% rule: the offer can't exceed 50% of the guest's existing reservation total. If both adjacent guests fail this check, the gap is skipped entirely.
- Tiebreaker: when both guests pass, the higher-paying reservation wins.
The Guesty task that lands in the ops queue includes everything: the pricing breakdown, why this guest was chosen, a ready-to-send email ("Would you like to extend your stay?" or "Would you like to arrive early?"), and a call script for offers over $300. Tags on the task flag whether auto-send is eligible or a call is required.
Pool heaters: orchestration vs. computation
Pool heater management is the harder problem, and it's the one that taught us when n8n should stop and a service should start.
The ops team had a detailed SOP: heat pools to 85°F on guest arrival day in non-summer months, keep electric heaters running continuously, pre-heat large pools (>15,000 gallons) the night before check-in, turn everything off after checkout, handle summer differently, and apply property-specific overrides for indoor pools and spa-only configurations. Calendar blocks in Guesty — Turnover, Early Check-in, Last-minute, Maintenance, Repair — could override the defaults.
That logic isn't hard to describe. It's hard to run reliably across Hayward's XML API, iAqualink's JSON API, and Guesty's booking data — where system IDs change per installation, equipment status lives in different formats, and the spreadsheet the team trusted didn't match what the vendor portals showed.
n8n orchestrates the loop. PHMDS owns the rules and the vendor APIs. Google Sheets stays the human-readable audit trail.
What PHMDS does
We built the Pool Heater Management Data Service — a FastAPI app on GCP Cloud Run — to be the single place that understands all three systems. A call to /api/v1/hayward/info triggers dozens of downstream requests: pull the property list, parse the config file to map system IDs (they're different per installation), fetch real-time telemetry, look up the property in Guesty, check today's availability, and return one unified row. Same shape for iAqualink, different API format.
The rules engines live here, not in n8n. Summer vs. non-summer. Large pool pre-heat the night before. Electric heaters that stay on continuously. Guest in-house vs. checkout day vs. arrival day. Calendar block overrides. Property-specific exceptions — like the indoor pool at 4417 Dryades that runs at 80°F October through April for humidity control, or the Ashlyn properties in Savannah where spas stay at 104°F and pools go off November through February.
n8n never calls Hayward or iAqualink directly. It reads the Google Sheets, calls PHMDS to populate them if empty, loops each property through apply-rules, waits for rate limits, re-fetches live telemetry, and batch-writes the updated rows back. The sheet is the audit trail the ops team already knew how to read.
What we learned the hard way
The sheet was stale. That was the first bug report — heaters showing "On" in the spreadsheet while iAqualink showed everything off. The automation's whole value proposition is that the sheet reflects reality after every run, so we made the write-back step non-optional: always re-fetch, always sync.
Calendar block labels are inconsistent. Guesty captures Turnover and Last-minute blocks, but different reps record them differently. The rules engine does fuzzy matching on block type strings, but we still need ongoing input from the field team to keep labels clean. Automation doesn't fix bad data entry — it just applies rules consistently to whatever data exists.
Simulation mode is the default. PHMDS ships with SIMULATE_RULES_APPLICATION = True so we can run the full loop — rules, API calls, sheet sync — without actually toggling heaters in production. Flipping that flag is deliberate, not accidental.
What a morning looks like now
Two concrete pictures.
Gap night, 7:04 AM. A new 2-night reservation on a 3-bedroom in Savannah confirmed yesterday. It created a 1-night gap on the following Friday. The workflow fetches the calendar price ($800), normalizes it to $471, applies a 50% Saturday discount (10 days out), and lands on a $236 offer. The guest checking out booked direct — platform priority picks them over the Airbnb guest on the other side. The 50% rule passes. A Guesty task appears: "Upsell – 1-night Gap (Friday, March 14)" with the full pricing breakdown, email template, and a call-required tag because the offer exceeds $300. The ops team acts on it before their first coffee is finished.
Pool heater, manual run. PHMDS health check passes. n8n reads 47 rows from the Hayward sheet and 12 from the iAqualink sheet. For each property, it posts to apply-rules. A large pool in Nashville with a guest arriving tomorrow gets the heater turned on tonight — night-before pre-heat rule. An electric pool in Austin stays at 80°F regardless. A property with a Maintenance block in Guesty gets heaters forced off. After both loops complete, n8n waits a minute for Guesty rate limits, re-fetches live telemetry from both vendors, and writes 59 updated rows back to the sheets. The ops team opens the sheet and sees current state, not yesterday's guess.
Takeaways
A few things I'd tell past-me before starting either of these.
- Match the tool to the problem shape. Gap night is integration glue with a pricing formula — n8n Code nodes are enough. Pool heaters are multi-vendor API coordination with a rules engine — that earns a service. Don't force everything into one layer.
- The sheet is the audit trail, not the source of truth. Google Sheets are where humans look. Vendor APIs and Guesty are where truth lives. The automation's job is to sync them, not to trust the sheet as-is.
- Leave humans a ready-to-act artifact. Gap night doesn't auto-send every offer — it creates a Guesty task with the email, call script, and pricing breakdown baked in. The ops team still makes the call on high-value offers. Automation does the prep; humans do the judgment.
- Cache the expensive calls. Guesty OAuth on every node would be slow and brittle. Redis token caching and deliberate wait nodes between batch operations keep the workflows reliable under rate limits.
- Simulate before you toggle physical equipment. Pool heaters are not Slack messages. A wrong toggle is a guest arriving to a cold pool or a pump running dry. Default to simulation, prove the loop, then flip the flag.
- Business rules belong in one place. Whether it's a Code node or a Python rules engine, the pricing formula and the heater SOP should each live in exactly one location. If the ops team and the automation disagree, the automation should win — because it's the one applying the rules consistently.
If you're wiring up n8n for ops automation — especially across systems that were never designed to talk to each other — I'd love to compare notes. Reach out.
Further reading
Patterns and tools that shaped these builds:
- n8n documentation — workflow triggers, Code nodes, HTTP Request nodes, and credential management.
- Guesty Open API — reservations, calendar pricing, tasks, and conversation endpoints we used in both workflows.
- n8n Google Sheets integration — how we used sheets as the human-readable audit layer.
- FastAPI — the framework behind PHMDS, deployed to Cloud Run for the pool heater rules engine.
Until next time, Sahej.