Building Hooper's Vacation Rentals: A 6-Agent AI Team Takes on the Short-Term Rental Market
My brother-in-law Terry runs a small real estate business in rural Pennsylvania. Among his properties is a vacation rental: Railroad Retreat, a 2-bed, 2-bath raised ranch at 530 Railroad St in Trevorton, PA, with a 3-bay garage and mountain views that legitimately make you want to stay for a week.
For the past couple of years, Terry had been listing it on Airbnb. Guests loved the place. Terry loved hosting. Airbnb, however, loved taking its cut of every booking. Service fees, host fees, cleaning fee caps, and the general tax of routing everything through a platform that adds friction on both sides of the transaction, it adds up fast.
The problem wasn't the guests. Many of them were repeat visitors who knew the property, knew Terry, and would happily book directly if only there were a way to do it. The problem was the infrastructure: where do you send someone who wants to skip the middleman? A phone number? A Google Form? That's not exactly "trust-inspiring."
So Terry asked me if I could build him a direct-booking website. And I said yes, partly because I wanted to help, and partly because I'd been looking for an excuse to build something genuinely different from my usual work.
This is the story of that build.

The Problem: Airbnb's Fee Sandwich#
Before getting into the technical build, it's worth understanding what Terry was actually paying to be on Airbnb. The fee structure works like this:
- Host service fee: Airbnb charges hosts 3% of every booking as a baseline
- Guest service fees: Guests pay 14-16% on top of the nightly rate
- Platform rules: Airbnb controls pricing display, refund policies, and communication norms
- Algorithm dependency: Your property's visibility is at the mercy of an algorithm that rewards certain behaviors and punishes others
For a repeat guest who already knows the property, those fees are pure friction. A guest paying $200/night sees $230-240 by checkout after Airbnb's cut. Terry sees less than the $200 after his 3% deduction. Both parties lose money compared to a direct transaction. The platform captured the value of the relationship they already built.
The solution: a direct-booking website where returning guests can reach Terry without going through Airbnb. No platform fees, no algorithm, no permission required to communicate.
Why Direct Booking Websites Work
Direct booking sites are most effective for hosts with repeat guests or strong word-of-mouth. You're not trying to replace Airbnb discovery (that still works). You're giving established guests an alternative for their second, third, and fourth stays.
The Stack Decision#
Before I wrote a line of code, I needed to decide what I was building with. The requirements were specific:
- SEO-friendly: The site needed to rank for local search terms, so server-side rendering was non-negotiable
- Admin dashboard: Terry is not technical. He needed a way to manage properties, review inquiries, and update content without touching code
- Email notifications: Guests submit inquiry forms, Terry gets notified. Simple, reliable
- Authentication: A basic auth flow for Terry to access the admin panel, with appropriate security
- Rate limiting: Inquiry forms would be public-facing, so some protection against spam/abuse was needed
- Image storage: Property photos needed to live somewhere accessible without a CDN budget
- Real-time availability: The booking calendar needed to reflect actual availability, not static dates
I landed on this stack:
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 16 with TypeScript | SSR, App Router, well-documented |
| Styling | Tailwind CSS v4 + shadcn/ui | Fast iteration, consistent components |
| Database | Supabase (PostgreSQL) | Managed Postgres, auth, storage in one service |
| Auth | Supabase Auth | Built into the same service as the database |
| File Storage | Supabase Storage | Also built in, CDN-backed, no separate service |
| Resend | Simple API, reliable delivery | |
| Rate Limiting | Upstash Redis | Managed Redis with a generous free tier |
| Deployment | Vercel | Auto-deploy from GitHub, Next.js optimized |
One service (Supabase) handling the database, auth, and image storage was the right call for a project of this scope. Terry doesn't need separate bills and separate dashboards for each piece of infrastructure.
Supabase as an All-in-One Backend
Supabase is genuinely excellent for small-to-medium projects where you want PostgreSQL without the operational overhead. Database, row-level security, auth, storage, and a REST API all come included. For a single-property vacation rental site, it handles everything.
The Design Direction: Rural Aesthetic#
One of the more interesting parts of this project was figuring out what the site should feel like. Hooper's Vacation Rentals is not a corporate Airbnb competitor. It's a small, personal operation run by a real person who lives nearby and genuinely cares about guests having a good experience. The design needed to communicate that.
The brief I gave the design agents was: warm, rustic, rural Pennsylvania. Not kitschy farmhouse, not a generic hospitality template. Something that looks like a property you'd actually want to stay at rather than a lead generation page.
The result:
- Primary palette: Forest green (
#4a8c32as the base) + warm amber (#fbbf24) + cream - Typography: Playfair Display for headings (classic serif with character), Inter for body (clean, readable)
- Photography-forward: Full-bleed hero images, property photos as the primary design element
- Brand name: "Hooper's Vacation Rentals" (Terry's last name)
The CSS design tokens in globals.css tell the story. Rather than the dark/OKLCH approach I use on this blog, the vacation rental site uses a warm light theme with a custom color scale built specifically around forest greens and amber tones. Tailwind v4 made this easy, since all configuration lives in CSS via @theme blocks with no separate config file.
/* Forest green scale */
--color-forest-50: #f2f7ee;
--color-forest-500: #4a8c32;
--color-forest-800: #2d5016;
--color-forest-900: #1e3510;
/* Warm amber scale */
--color-amber-400: #fbbf24;
--color-amber-500: #f59e0b;
/* Fonts */
--font-sans: var(--font-inter);
--font-serif: var(--font-playfair);
The font-heading utility maps to Playfair Display, which shows up on every section heading across the site. The combination reads "welcoming bed and breakfast" rather than "corporate booking portal," which was exactly the point.
The 6-Agent Team#
This is where the build got interesting.
I approached the project using an agentic team structure within Claude Code. Rather than one AI assistant doing everything sequentially, I used a team of specialized agents working in parallel on different concerns. The agents were:
| Agent | Role | Primary Focus |
|---|---|---|
web-designer-lead | Project architect | Overall site structure, UX, component hierarchy |
web-developer | Implementation | Next.js code, data fetching, API routes |
ux-ui-designer | User experience | Guest booking flow, form design, mobile UX |
graphic-designer | Visual design | Color palette, typography, brand tokens |
security-engineer | Security review | Auth flows, rate limiting, input validation |
property-owner | Domain expert | Vacation rental UX, host dashboard features |
The property-owner agent was the most unusual one. Its job was to simulate Terry's perspective: what does a non-technical host actually need in an admin dashboard? What information matters when you're checking the site on your phone between jobs? What would be confusing?
The Agent Team Pattern
This is the "Agent Captain" pattern in action: one main orchestrating Claude Code session managing a team of specialized subagents. Each agent focuses on its domain and their outputs get integrated back into the main build. I've written about this pattern before in the context of building games and analytics dashboards. Vacation rental websites are a surprisingly good use case for it.

The 7 Build Phases#
The project was structured in phases, which gave it a natural checkpointing rhythm and made it easier to delegate work to different agents.
Phase 0: Project Scaffolding#
Standard Next.js setup:
npx create-next-app@latest terry-website \
--typescript --tailwind --app --src-dir --eslint
Then adding the rest of the stack:
pnpm add @supabase/supabase-js @supabase/ssr
pnpm add resend
pnpm add @upstash/redis @upstash/ratelimit
pnpm add react-hook-form @hookform/resolvers zod
pnpm add date-fns lucide-react
pnpm add @vercel/analytics @vercel/speed-insights
The project uses pnpm rather than npm. For a project with a lot of dependencies (Supabase, Resend, Upstash, React Hook Form, shadcn components), pnpm's disk efficiency and lockfile reliability are worth the slight unfamiliarity.
The Supabase local dev setup used their CLI to manage schema migrations. Every schema change got a numbered migration file in supabase/migrations/, which means the entire database schema is version-controlled and reproducible.
Phase 1: Design System + Database Schema#
The graphic designer and web designer agents worked in parallel here. One was building the CSS design tokens (the color scales, typography, spacing) while the other was designing the database schema.
The schema covered seven core tables:
-- Properties (the vacation rentals themselves)
CREATE TABLE properties (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
slug text UNIQUE NOT NULL,
name text NOT NULL,
description text,
address text,
city text,
state text,
zip text,
bedrooms integer,
bathrooms numeric,
max_guests integer,
sqft integer,
base_price_per_night numeric,
cleaning_fee numeric,
amenities text[],
house_rules text[],
...
);
-- Availability (date ranges, blocked by season or existing booking)
CREATE TABLE property_availability (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
property_id uuid REFERENCES properties(id),
start_date date NOT NULL,
end_date date NOT NULL,
status text NOT NULL CHECK (status IN ('available', 'blocked', 'booked')),
EXCLUDE USING gist (property_id WITH =, daterange(start_date, end_date) WITH &&)
);
That EXCLUDE USING gist constraint is the interesting one. It uses PostgreSQL's btree_gist extension to enforce that no two availability records for the same property can have overlapping date ranges. It's a database-level constraint that prevents double-bookings, not an application-level check. The application can still have bugs. The database will not let overlapping bookings through.
Other tables: booking_inquiries (guest contact form submissions), guests (guest profiles for returning visitors), reviews (post-stay ratings), site_content (editable copy for the admin), and subscribers (email list for the newsletter CTA on the homepage).
Phase 2: Layout Shell#
The layout shell was straightforward: root layout with the site header and footer, route groups to separate guest-facing pages from admin pages, and the base page structure for each section.
The route group organization mattered:
src/app/
├── (guest)/ # Public routes, no auth required
│ ├── page.tsx # Redirects to / root
│ ├── properties/
│ │ └── [slug]/ # Individual property pages
│ ├── contact/
│ ├── about/
│ ├── auth/ # Login page
│ └── policies/ # ToS, privacy, cancellation
├── admin/ # Protected routes, admin auth required
│ ├── page.tsx # Dashboard
│ ├── properties/
│ ├── availability/
│ ├── inquiries/
│ ├── reviews/
│ └── content/
└── api/ # API routes
├── inquiries/
└── subscribe/
The (guest) route group is a Next.js App Router pattern where the folder name in parentheses doesn't appear in the URL. /properties/railroad-retreat is the actual URL, not /guest/properties/railroad-retreat. The grouping is purely for code organization.
Phase 3: Guest-Facing Pages#
The ux-ui designer and web developer agents built this phase together, with the property-owner agent doing review passes on the UX decisions.
The Homepage has six sections:
- Hero with full-bleed property photo background and two CTAs ("View Railroad Retreat" and "Check Availability")
- Featured Property card with property stats
- "Why Book Direct" section (four cards explaining the value prop)
- Testimonials (seeded with placeholder reviews)
- Area Highlights for Trevorton, PA and the surrounding region
- Email signup CTA for the newsletter list
The "Why Book Direct" section was the property-owner agent's suggestion. For a direct-booking site, the entire conversion job is explaining to someone who found this through Airbnb why they should bother going outside the platform. That requires a clear, honest pitch. The section addresses the fees directly, which is honest and converts better than vague "direct benefits" language.
The Property Page (/properties/railroad-retreat) is the main product page. It has:
- Photo gallery with lightbox viewer (this came in a later commit, after the initial launch)
- Property stats bar (bedrooms, bathrooms, max guests, square footage)
- Full description with expandable sections
- Amenities grid organized by category
- Location and area highlights
- House rules
- The inquiry form for booking requests
- Structured data (JSON-LD schema for
LodgingBusinessandVacationRental)
The structured data was something the security engineer agent flagged as important: without VacationRental schema markup, the property won't appear in Google's vacation rental rich results. The web developer implemented it as a server-rendered <script type="application/ld+json"> tag, which is the correct approach for SEO.
{
"@context": "https://schema.org",
"@type": ["LodgingBusiness", "VacationRental"],
"name": "Railroad Retreat",
"address": {
"@type": "PostalAddress",
"streetAddress": "530 Railroad St",
"addressLocality": "Trevorton",
"addressRegion": "PA",
"postalCode": "17881"
},
"amenityFeature": [...],
"numberOfRooms": "2",
"floorSize": { "@type": "QuantitativeValue", "value": 1796, "unitCode": "FTK" }
}
The Contact Page has a general inquiry form for guests who aren't ready to book but want to ask about dates, pets, or specific needs. This routes to the same /api/inquiries endpoint as the property page inquiry form.
Inquiry Form vs. Booking Engine
This site does inquiry-based booking, not instant booking. Guests submit a request, Terry responds, they confirm payment via direct transfer or preferred payment method. This is intentional: for a single-property operation with a hands-on host, instant booking through a payment processor adds complexity (Stripe integration, refund flows, payment disputes) that the business model doesn't need yet.
Phase 4: Auth + Admin Dashboard#
This was the phase the security engineer spent the most time on.
Authentication uses Supabase Auth with the @supabase/ssr package for proper server-side session handling in Next.js App Router. The admin login is at /auth/login. There is no guest registration flow on purpose: guests submit inquiry forms, they don't create accounts. The only account in the system is Terry's admin account.
The middleware protects all /admin routes:
// src/middleware.ts
export async function middleware(request: NextRequest) {
const { supabase, response } = createServerClient(request);
const { data: { session } } = await supabase.auth.getSession();
const isAdminRoute = request.nextUrl.pathname.startsWith("/admin");
if (isAdminRoute && !session) {
return NextResponse.redirect(new URL("/auth/login", request.url));
}
return response;
}
The security engineer flagged that the admin dashboard needed several protections that aren't obvious:
- Row-level security (RLS) on the Supabase tables, so even if the service role key leaked, direct database queries couldn't return data without auth context
- Rate limiting on the API routes using Upstash Redis, so inquiry form spam doesn't flood Terry's email
- Input validation with Zod schemas on both client and server (the
@hookform/resolverspackage bridges React Hook Form with Zod for client-side validation, while the API routes run the same schemas again server-side) - CSRF protection via Supabase's built-in session management (the SSR package handles this correctly)
The Admin Dashboard (/admin) is the piece I'm most proud of from a product perspective. The web developer and property-owner agents built it iteratively, with the property-owner agent constantly asking "but what does Terry actually need here?"
The dashboard landing page shows:
- Upcoming inquiries with guest name, dates, property, and action buttons
- Recent reviews pending moderation
- Quick stats (total inquiries, confirmed bookings, occupancy this month)
- A quick-access nav to the main management sections
The individual management pages:
/admin/inquiries: Full inquiry list with status filtering (new, in-progress, confirmed, declined), guest details, and message history/admin/properties/[id]: Property editor where Terry can update description, amenities, house rules, pricing, and photos/admin/availability: Calendar view to mark dates as blocked or available/admin/reviews: Moderation queue for submitted reviews with approve/hide controls/admin/content: Editable site copy for the About page, cancellation policy, and privacy policy
The photo management in the property editor deserves a mention. Uploading property photos is a multi-step process: the admin page accepts file uploads, sends them to /api/admin/photos, which validates the files and uploads them to the Supabase Storage bucket (property-images), organized by property slug. The property page then reads photos from that bucket dynamically. No hardcoded image paths, no manual CDN configuration.

Phase 5: SEO + Polish#
The web designer lead handled this phase, with some help from the web developer for the technical SEO pieces.
Sitemap and robots.txt via next-sitemap:
// next-sitemap.config.js
module.exports = {
siteUrl: process.env.NEXT_PUBLIC_SITE_URL,
generateRobotsTxt: true,
exclude: ["/admin/*", "/auth/*", "/api/*"],
};
Open Graph tags on every page, with a property-specific OG image for Railroad Retreat (a 1200x630px version of the hero photo, which shows up when the URL is shared on social media).
Performance: Next.js Image component with sizes attributes for responsive loading, priority loading on the hero image, lazy loading everywhere else. The property page loads the photo gallery lazily because it's below the fold. The security engineer also added Content Security Policy headers via next.config.ts.
Phase 6: Testing + Deployment#
Seven test files, 83 tests, 97% statement coverage. The test structure:
src/test/
├── unit/
│ ├── email-templates.test.ts # Email HTML generation
│ ├── utils.test.ts # Utility functions
│ ├── rate-limit.test.ts # Rate limiter logic
│ └── validation-schemas.test.ts # Zod schema validation
├── integration/
│ ├── api-inquiries.test.ts # POST /api/inquiries
│ └── api-subscribe.test.ts # POST /api/subscribe
└── smoke.test.ts # Sanity checks
e2e/
├── homepage.spec.ts
├── property-page.spec.ts
├── inquiry-form.spec.ts
├── admin.spec.ts
├── accessibility.spec.ts
└── security.spec.ts
The integration tests mock Supabase and Resend so they run in CI without live credentials. The security engineer wrote the security.spec.ts E2E tests, which check that the admin routes return 302 redirects without authentication, that the inquiry form rejects malformed input, and that the rate limiter kicks in after the configured request threshold.
The coverage report came out at 97.43% statements, 93.42% branches, 92.3% functions, 98.66% lines. The uncovered branches are mostly error paths in the email template renderer that would only trigger on truly malformed data.
Coverage That Actually Means Something
The 97% coverage here isn't just number chasing. The API routes, validation schemas, rate limiter, and email templates are the pieces where bugs would show up as missed emails or failed bookings. Those are the things Terry would notice. Having solid test coverage on them means I can deploy changes without worrying about breaking the guest communication flow.
The Property Itself#
The photos came from the Redfin listing for 530 Railroad St. Since this is a real property with a public real estate listing, the photos are readily available and high quality. Terry went through them and picked the ones that showed off the space best: the exterior with the 3-bay garage and mountain backdrop, the living room with the fireplace, the kitchen, and the view from the back porch.
The property details from the CLAUDE.md file I kept throughout the build:
- 2 bedrooms, 2 bathrooms
- 1,796 square feet
- Raised ranch style
- Built in 1934
- 3-bay garage (unusual and popular with guests who have boats, ATVs, or motorcycles)
- Mountain views of the Appalachian ridgeline
- Located on Railroad St in Trevorton, Northumberland County, Pennsylvania
The area highlights section covers the D&H Rail Trail (~15 min), Knoebels Amusement Resort (~20 min), Shikellamy State Park (~20 min), and fishing on the Susquehanna. This content matters for SEO: someone searching for "vacation rental near Knoebels" should find Hooper's Vacation Rentals.
What the Deployment Looks Like#
The deployment guide (DEPLOY.md in the repo) documents six steps:
- Supabase Cloud setup: Create project, push schema with
supabase db push, run the seed file for Railroad Retreat data, enable thebtree_gistextension, create theproperty-imagesstorage bucket - Upstash Redis: Create database, copy the REST URL and token
- Vercel: Import the GitHub repo, Next.js auto-detected, set environment variables, deploy
- Environment variables: Nine of them, covering the Supabase URL and keys, Resend API key, host email addresses, site URL, and Upstash Redis credentials
- Verify the live site: Eight-point checklist covering the homepage, property page, contact form, admin login, admin dashboard, and SEO endpoints
- Pre-launch tasks: Real photos upload, OG image, confirming check-in process with Terry, reviewing placeholder copy
The deployment guide was written for Terry to follow himself. It assumes no technical background and explains every step. Every service it references has a direct link to where you find the credential. The goal was that Terry (or anyone helping Terry) could take this from "code is done" to "site is live" without me being on a call.
The DEPLOY.md Pattern
I've started writing deployment guides like this for every project I hand off. The assumption is always: what happens if I'm unavailable when the site goes live? A good deployment guide answers that question. It costs an hour to write and saves enormous pain later.
What Claude Code Actually Did#
Let me be specific about the role Claude Code played, because "I used AI to build a website" undersells the actuality of it.
Claude Code, operating as the six-agent team, wrote:
- All TypeScript/React component code
- The database schema and seed file
- The Supabase Row Level Security policies
- The Zod validation schemas
- The Resend email templates (HTML and plain text)
- The Upstash rate limiting integration
- The middleware for auth protection
- The structured data (JSON-LD) for SEO
- All unit and integration tests
- The Playwright E2E test suite
- The deployment guide
My job was:
- Defining the requirements (what does Terry need?)
- Making design decisions (color palette, layout choices)
- Reviewing code as it was written
- Flagging things that didn't match the business requirements
- Providing context that the agents couldn't have (what Trevorton is like, what guests typically ask about, what Terry is and isn't technical enough to manage)
- Testing the UI manually and noting what felt off
The collaboration felt like having a team of developers who could execute very fast but needed to be told what mattered. The agents knew how to build a Next.js app with Supabase. They needed me to tell them that the admin dashboard should be simple enough for Terry to use on his phone between jobs, that the "Why Book Direct" section needed to address fees directly rather than vaguely, and that the area highlights should mention Knoebels because that's a regional anchor that a lot of guests would recognize.
What AI Agents Are Good At
Code that follows established patterns: auth flows, form validation, API routes, test structure, database schema design. These are areas where there are known-good approaches and the agent team can execute them correctly. The places where I had to step in were always about domain knowledge (vacation rental UX, what Terry's business actually needs) and judgment calls (what to prioritize, what complexity to skip).

The Admin Dashboard as the Key Deliverable#
If I had to name one thing that makes this project successful as a business tool rather than a technical exercise, it's the admin dashboard.
Terry runs a real estate business. He's not going to learn to edit a database. He's not going to update a JSON file to change the nightly rate. He needs a web interface that works on a phone, shows him what he needs to know at a glance, and lets him take action without confusion.
The dashboard shows him today's inquiries first. If someone submitted a booking request overnight, that's the first thing he sees. The inquiry detail page shows the guest's message, their requested dates, the property they're asking about, and action buttons to respond (which opens a pre-filled email) or update the status. He doesn't need to remember to check an email inbox and then go find which property they were asking about and then go find the calendar. It's all in one place.
The availability calendar is the other piece he'll use constantly. He can block dates (visiting family, personal trips, maintenance windows) and see the full calendar in one view. This is data that feeds directly into the property page's availability display.
The content management section lets him update the About page (which is genuinely about him, his business, and why he got into vacation rentals) and the cancellation policy. These are pieces that will need to change over time and shouldn't require a code deploy.
Building for a Non-Technical Owner
The single biggest design principle for the admin dashboard was: Terry should be able to use this without any documentation. If a feature requires explanation, it's not designed well. The property-owner agent kept applying this filter throughout Phase 4. "Would Terry know what this button does?" Surprisingly often, the answer was "probably not" and we simplified.
Lessons Learned#
Supabase RowLevel Security Is Not Optional
Set up RLS policies at the same time you set up the tables, not afterward. Adding them retroactively means checking every query to make sure it still works with the policies in place. Doing it at schema time means RLS is part of how you think about data access from the beginning.
Tailwind v4 Has No Config File
Coming from Tailwind v3, the absence of tailwind.config.ts is jarring. Everything lives in globals.css via @theme blocks. Custom colors, fonts, radius values, all of it. Once you understand the pattern it's actually cleaner, but expect confusion on first contact.
pnpm Workspaces for Future Growth
The project uses a pnpm-workspace.yaml file even though it's currently a single app. The workspace setup makes it easy to add a separate admin package or a shared utilities package later without restructuring. It's a low-cost future-proofing decision.
The btree_gist Extension Must Be Explicitly Enabled
PostgreSQL's EXCLUDE USING gist for date range overlap prevention requires the btree_gist extension. Supabase doesn't enable it by default. You'll get a migration error that says operator class "gist_date_ops" does not exist until you enable it in the Supabase Dashboard under Database > Extensions. This one took a debugging pass to find.
Photos From Real Estate Listings
For a new property with no professional photography yet, Redfin and Zillow listings are a useful source for development-phase placeholder photos. They're high quality and give the agents real visual context to work with. Replace them before launch with actual property photos that you have rights to.
Middleware Auth Is Not Enough
The Next.js middleware route protection prevents UI access to admin routes without authentication. But API routes called directly (not through the UI) don't go through the same middleware path. The admin action functions (/admin/actions.ts) need to re-verify the session independently. The security engineer caught this and added server-side session checks to every admin action.
The Project in Numbers#
- 32 tasks tracked through the 7 phases
- 83 tests passing (7 test files: 4 unit, 2 integration, 1 smoke)
- 97% statement coverage on the tested code
- 6 Playwright E2E test suites covering guest flows, admin flows, accessibility, and security
- 9 environment variables required for deployment
- 2 database migrations (initial schema + one patch for nullable dates in inquiries)
- 1 property (Railroad Retreat, 530 Railroad St, Trevorton, PA)
- 6 agents working across 7 phases
The git history has 11 commits, which is fewer than you'd expect for a project this size. That's because the early phases were mostly developed in a single large commit (feat: complete Hooper's Vacation Rentals website (Phases 0-6)), with subsequent commits adding the photo gallery, connecting real Supabase photos, and some deployment fixes. Not my cleanest commit history, but a functioning website.
What's Next#
The site is built and deployed to Vercel. It's ready for Terry to create his Supabase project, push the schema, configure the environment variables, and go live. The deployment guide walks him through every step.
After launch, there are a few natural extensions:
- Direct payment integration: Adding Stripe or Square so guests can pay a deposit directly through the site rather than working out payment separately
- Booking calendar as an embed: Adding an iCal-compatible calendar so Terry can share availability via a URL that other tools can read
- Channel manager sync: If Terry keeps Airbnb active alongside the direct site, a channel manager integration would prevent double-booking
- More properties: If Terry adds more vacation rentals to his portfolio, the property management system is already designed to handle multiple properties
For now, the goal is simpler: give repeat Airbnb guests a way to book directly, skip the fees, and get a better experience on both sides. The site does that. The admin dashboard gives Terry full control without requiring technical help for day-to-day operations.
That felt like the right place to stop and ship.
The terry-website repo is private (it includes Terry's business content and infrastructure details), but the tech stack, patterns, and approach described here are all reproducible. The same pattern applies to any small business owner who wants a direct booking website without platform dependency.
Written by Chris Johnson and built with Claude Code (Opus 4.6) and a 6-agent team. Next up: I'm starting to experiment with booking payment flows, so expect a post on Stripe integration for vacation rental deposits sometime this spring.
Weekly Digest
Get a weekly email with what I learned, summaries of new posts, and direct links. No spam, unsubscribe anytime.
Related Posts
One cybersecurity nerd, one AI coding assistant, one week, 117 commits, 24 pull requests, 17 blog posts, 410 tests at 98% coverage. This is the story of building a production website from scratch with Claude Code, including every mistake, meltdown, and meme along the way.
A detailed, hour-by-hour account of my first day with Claude Code - starting with Ollama frustrations, discovering Opus 4.6, building a complete website, and deploying to production. 30 commits, 4 repositories, and one very long day.
A detailed, step-by-step guide to vibe coding a production website from the ground up using Claude Code, from someone whose last website ran on Apache with hand-written HTML. Every service, every config, every command.
Comments
Subscribers only — enter your subscriber email to comment
