Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Dynamic Pricing Engine for Vacation Rentals

A production pricing automation system managing 6 vacation rental properties across multiple distribution channels. Built as a solo developer to replace manual spreadsheet pricing with an automated pipeline that gathers market data, runs AI-powered analysis, and pushes optimized rates to every booking platform in real time.

The system handles the full lifecycle: ingesting bookings from channel managers, detecting pricing opportunities through pace analysis, generating AI-backed rate recommendations, routing them for owner approval, and syncing approved rates across Airbnb, Booking.com, VRBO, and a direct booking site.

This is a private production system. Code samples in this repository are sanitized -- engineering patterns are fully visible, but proprietary business logic, thresholds, and prompts have been redacted.


Architecture Overview

flowchart TB
    subgraph Channels["Distribution Channels"]
        AB[Airbnb]
        BC[Booking.com]
        VR[VRBO]
        DB[Direct Booking Site]
    end

    subgraph CM["Channel Manager (Channel Manager)"]
        WH[Webhook Events]
        API[Rate Push API]
    end

    subgraph Core["Supabase Backend"]
        direction TB
        EF["Edge Functions<br/>(Deno/TypeScript)"]
        CR["Cron Scheduler<br/>w/ Dependency Ordering"]
        DB2[(PostgreSQL)]
        DLQ["Dead Letter Queue"]
        AL["Advisory Locks"]
    end

    subgraph AI["AI Advisor"]
        GPT[GPT-4 Analysis]
        PA[Pace Analysis]
        VP[Velocity Projections]
    end

    subgraph Auto["Browser Automation"]
        PW[Playwright Runtime]
        VS[VRBO Price Sync]
    end

    subgraph Notify["Notifications"]
        TW[Twilio SMS]
        EM[Email Alerts]
    end

    subgraph Frontend["React Dashboard"]
        DP[Pricing Proposals]
        AD[Approve / Deny]
        AN[Analytics Views]
    end

    subgraph POS["Point of Sale"]
        SQ[Square POS]
        CA[Cafe Analytics]
    end

    Channels <-->|bookings| CM
    CM -->|webhooks| EF
    EF -->|rate updates| API
    EF -->|VRBO rates| PW
    PW --> VR
    API --> AB & BC
    EF <--> DB2
    CR --> EF
    EF --> DLQ
    DLQ -->|retry| API
    EF <--> AL
    EF <--> GPT
    PA --> GPT
    VP --> GPT
    EF --> TW
    EF --> EM
    Frontend <--> EF
    SQ --> EF
Loading

Tech Stack

Layer Technology
Backend Supabase Edge Functions (Deno/TypeScript)
Database PostgreSQL (Supabase)
Frontend React, Vite, Tailwind CSS
AI OpenAI GPT-4 API
Automation Playwright (headless browser)
Channel Manager Channel Manager API + Webhooks
SMS Twilio
POS Square API
Hosting Netlify (frontend), Supabase (backend)
CI/CD Custom deploy scripts, UAT → Prod promotion

Key Features

Automated Pricing Pipeline

The heart of it is a daily pricing pipeline: a chain of about six Supabase Edge Functions that run in dependency order. Each declares its predecessors, and the scheduler holds a function until its upstream steps finish, so a slow or failed step never lets a downstream one run on stale data. Webhook ingestion for bookings and cafe sales and the APIs that serve the dashboard run as their own functions alongside it.

AI Pricing Advisor

GPT-4-powered analysis that evaluates booking pace, occupancy velocity, seasonal patterns, and local events across all properties. Generates specific rate recommendations with reasoning, routed to the owner for approval before any changes go live.

Multi-Channel Distribution

Rates sync to Airbnb, Booking.com, VRBO, and a direct booking site. The channel manager API handles most platforms; VRBO requires browser automation due to API limitations in the channel manager.

Square POS Integration

Cafe revenue data from Square feeds into property analytics, giving a fuller picture of the business operation.

The webhook chain, dead letter queue, advisory locks, owner approval gate, and VRBO automation each get a full walkthrough in the next section.


Engineering Decisions

The business logic is mine and it stays private. The engineering is the part worth talking about. Most of this I built because something broke and I didn't want it breaking again, not because I planned it out up front.

Cron functions declare their dependencies

I started simple. Six cron jobs at six staggered times, spaced out enough that each one should finish before the next kicks off. Worked fine until the channel manager API had a slow morning, the sync step ran long, and gap detection started reading data that was only half written. I pushed rates off stale state and didn't notice until that night.

So now every function calls waitForPredecessors() when it starts and waits on an execution-log table until the steps it depends on have actually finished for that run. If something upstream dies or hangs, the function downstream just stops instead of guessing. It is more plumbing than I wanted, but pricing on stale data is the one thing I am not willing to let happen.

Failed rate pushes go to a dead letter queue, not a retry loop

Pushes fail for two reasons that look the same and need the opposite treatment. A rate limit or a dropped connection should get retried. An invalid rate or a delisted property is never going to work, and pounding on it just hides the failures I actually need to see.

So every failure gets tagged transient or permanent on the way into the queue. Transient retries with backoff and usually fixes itself. Permanent pages me. Retry hard on flaky infrastructure, do not waste time on data that is just broken.

Advisory locks guard every price write

This was a bug before it was a decision. A cron job and a booking webhook both went to reprice the same property in the same second, read the same starting numbers, came up with different rates, and the slower one overwrote the better answer. Happened once, couldn't reproduce it, which is the kind of bug I hate most.

Advisory locks killed it. I lock per property instead of the whole table, so two different properties still reprice at the same time, but two writes to the same one have to take turns.

The webhook cascade is idempotent end to end

One booking sets off a chain: reprice the property, look for gaps the booking just created, adjust the dates around nearby events. Webhooks get redelivered, steps get retried, so any link can fire twice. If those steps weren't idempotent you'd get double adjustments and rates that slowly drift off. Every step is safe to run more than once and retries on its own, so if something dies halfway through, it just replays clean instead of leaving a property half done.

Nothing goes live without me approving it

The advisor is good and I still don't let it touch live rates. It writes a proposal with its reasoning to a table and that's where it sits until I approve it from the dashboard. A bad model run is a suggestion I say no to, not a rate a guest actually sees. This moves real money, so I wanted one person on the hook for every change. That's me.

VRBO is the one channel I run through a browser

Everything else goes through the channel manager API. VRBO doesn't, because the channel manager can't push rates to it. I wasn't going to drop a whole channel over that, so there's a Playwright script that logs into the host dashboard, sets the rates, and then reads them back to make sure it took. It's the ugliest piece of the whole thing and I kept it on purpose. Every real setup has one integration that won't play nice. The job was hiding it behind the same rate-push interface as everything else so the rest of the code never has to care that VRBO is the problem child.


System Metrics

  • 6 properties under active management
  • Dependency-ordered daily pricing pipeline (sync, gap detection, event pricing, AI advisor, rate push)
  • 4 distribution channels synced
  • ~2 years in production
  • 44% ahead of historical booking pace since launch
  • Zero manual spreadsheet pricing since launch

Screenshots

Screenshots of the dashboard, pricing proposals, and analytics views are available on request during interviews. They are not included here to protect guest data and proprietary rate information.


Repository Structure

str-pricing-engine/
  README.md                          # This file
  docs/
    ARCHITECTURE.md                  # System design deep dive
    TECHNICAL_DECISIONS.md           # Engineering tradeoffs
  samples/
    webhook-chain.ts                 # Webhook processing pattern
    cron-dependency-system.ts        # Cron execution ordering
    dead-letter-queue.ts             # DLQ with retry logic
    browser-automation.ts            # Playwright VRBO sync
    ai-advisor-overview.ts           # AI decision flow (pseudocode)

About

Built and operated by a solo developer managing a real vacation rental business. Every design decision was driven by an actual operational problem -- not theoretical best practices. The system runs 24/7 and directly impacts revenue.

About

Production dynamic pricing engine for vacation rentals. GPT-4o advisor, multi-channel sync, serverless Supabase backend

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors