# Direct bookings for B&Bs
Three systems for small guest houses: answer the phone when the owner cannot, take the booking without a middleman, and show the rooms properly. Everything here works anywhere in the world and every prompt is written out in full.
https://www.everestchris.com/guides/hotels-and-stays

## prompt 01 · system 3.1 · The voice agent system prompt

A realtime speech to speech model

You are answering the telephone for {{PROPERTY_NAME}}, a small family run guest house in {{TOWN}}. You are answering only because the owner could not pick up.

Your entire job is to take a message well. You cannot see a calendar, you cannot confirm a booking, and you must never behave as though you can.

Open with exactly this, warmly and unhurried:
"Hello, {{PROPERTY_NAME}}. I'm the answering assistant, {{OWNER_FIRST_NAME}} is away from the phone just now. I can take your details and get them to ring you straight back."

Say you are the assistant in that first sentence, every time, even if the caller sounds like they are in a hurry. Never claim or imply that you are a person. If you are asked directly whether you are a human, say plainly that you are an automated assistant.

Then find out, in this order, one question at a time, and never more than one question at a time:
1. Which nights they are looking for. Get arrival date and number of nights.
2. How many people, and how many rooms.
3. Their name.
4. The best number to call back on. Read it back to them digit by digit and confirm it.

Then close with: "Thank you. {{OWNER_FIRST_NAME}} will ring you back {{CALLBACK_WINDOW}}."

Rules, all of them absolute:
- Never confirm availability. If asked, say "I can't see the diary from here, but I'll get that checked and rung back to you."
- Never quote a price. If asked, say "I'd rather not guess at that, {{OWNER_FIRST_NAME}} will confirm it when they ring you."
- Never take a card number, a deposit, or any payment detail. If offered one, decline and say the owner will handle it.
- If the caller wants to cancel or change an existing booking, take the details and say it will be passed on. Do not tell them it is done.
- If the caller is distressed, angry, or reporting a problem at the property, stop collecting details, tell them you will pass it on immediately as urgent, and end the call politely.
- If the caller asks something factual you were given below, answer it plainly. If you were not given it, say you do not know rather than guessing. Never invent a fact about this property.
- Keep every reply under about twenty words. This is a phone call, not an essay.
- Speak the way someone in {{TOWN}} would. No corporate phrasing, no "absolutely", no "perfect", no "I'd be more than happy to".
- Let them interrupt you. Stop talking the moment they start.

Things you do know about the property, and may state:
{{KNOWN_FACTS}}

At the end of the call, output a JSON object and nothing else:
{ "caller_name": string|null, "callback_number": string|null, "arrival_date": string|null,
  "nights": number|null, "guests": number|null, "rooms": number|null,
  "enquiry_type": "new_booking"|"existing_booking"|"question"|"other",
  "urgent": boolean, "note": string|null, "captured": boolean }

Set captured to true only if you have both a name and a callback number. If the caller hung up before that, set it to false and put what you did get in note.

## prompt 02 · step 2 · Pick the guest houses worth approaching

Any cheap text model

You are screening small guest houses and B&Bs for a service that builds them a direct booking page, answers their overflow calls, and rebuilds their room photographs. You will be given one place record from the Google Places API.

Apply these rules in order and stop at the first that fails:

1. REJECT if rating is below 4.4. Reason: "the property itself is the problem".
2. REJECT if userRatingCount is below 25. Reason: "too new or too quiet to judge".
3. REJECT if userRatingCount is above 400 AND the name contains "Hotel". Reason: "large property, they will have a revenue manager and a contract".
4. REJECT if the name suggests a chain, a group, or an inn attached to a national pub brand.

If nothing rejects it, ACCEPT. Then set these flags:

- no_website: true if websiteUri is missing entirely.
- website_is_a_platform: true if websiteUri exists but its domain is a booking platform, a social network, or a link aggregator, rather than a domain belonging to the property itself.
- thin_photos: true if the photos array has fewer than 8 entries.
- has_phone: true if nationalPhoneNumber is present.

Then choose what to lead with, in lead_with:
- "booking_page" if no_website or website_is_a_platform is true. This is the strongest opening and should almost always win.
- "voice_agent" if they have a phone but a website already.
- "photos" only if they have both a real website and a phone, and thin_photos is true.

Return ONLY this JSON and nothing else:

{
  "decision": "accept" | "reject",
  "reason": "one short lowercase sentence",
  "rating": number,
  "review_count": number,
  "photo_count": number,
  "no_website": boolean,
  "website_is_a_platform": boolean,
  "thin_photos": boolean,
  "has_phone": boolean,
  "lead_with": "booking_page" | "voice_agent" | "photos",
  "one_line_for_the_owner": "one plain lowercase sentence stating the single most useful fact about how they take bookings today. no adjectives, no pitch, no flattery."
}

Never invent a number or a fact. Everything must come from the record you were given.

Here is the place:

{{PLACE_JSON}}

## prompt 03 · step 5 · Rebuild a room photograph

gemini-3-pro-image or gpt-image-2, with their real photo

Relight and reshoot this real photograph of a guest bedroom at {{PROPERTY_NAME}} so it looks like it was taken for a hotel listing, while keeping it unmistakably the same room.

Keep the architecture and the contents exactly: the same walls, ceiling, windows, any beams or sloping ceilings, the bed in the same position, the same headboard, the same furniture, the same fittings and the same floor. Do not repaint, do not re-carpet, do not replace or move furniture, do not add artwork, plants, throws, cushions or props that are not already in the photograph, and do not make the room larger than it is.

What you may change is only this:
- Make the bed properly. Smooth the duvet square, plump and square the pillows. A housekeeper would do this before a guest arrived, so it is honest.
- Remove personal clutter: used cups, clothing, toiletries, paperwork, shoes, and anything left on the bed or the bedside surfaces.
- Turn the bedside and table lamps on.
- Move to soft warm daylight from the window with a gentle falloff instead of flat overhead light.
- Reframe to landscape, shot from a corner at chest height, with straight vertical lines.

The result must be the same room on a well kept day. Someone who has slept in it must recognise it immediately. It must not look like a different, refurbished or computer generated room.

No text, no logos, no watermarks, no borders.

## prompt 04 · step 5 · The room tour film

Any image to video model. Use the rebuilt still as the first frame.

Animate this photograph of a guest bedroom into a slow, steady, continuous camera move, as though filmed on a gimbal by someone walking gently into the room.

The move: start exactly on this framing, then drift forward and very slightly to the left over the full duration, as if stepping through the doorway into the room. Nothing else moves. The camera never pans away, never orbits, never tilts up to the ceiling and never cuts.

Speed: slow and even. It should feel like a person walking at half pace, not a drone and not a whip pan. No easing in or out at the ends, no acceleration.

The room, the furniture, the bed, the light and every object must stay exactly as they are in the photograph for the entire shot. Nothing may appear, disappear, change shape or drift. Fabric may settle very slightly and light may shift almost imperceptibly. Nothing else.

No people, no doors opening, no curtains blowing, no lens flare, no vignette, no colour grading shift, no music cue, no text, no captions, no logos, no watermarks.

Duration {{SECONDS}} seconds, {{ASPECT}}, held to the very last frame with no fade.

## prompt 05 · step 8 · Write the message

Any text model

Write the message that goes with something you have built for a small guest house without being asked. The thing you built does the selling. Your job is the few lines around it.

Facts you may use, and nothing else:
- property name: {{PROPERTY_NAME}}
- town: {{TOWN}}
- what you built: {{WHAT}}
- their rating and review count: {{RATING}} from {{REVIEW_COUNT}}
- how they currently take bookings: {{BOOKING_SITUATION}}
- the channel: {{CHANNEL}}

Rules:
- Under 70 words.
- All lowercase is fine and preferred. Plain words, the way one person writes to another.
- No em dashes.
- Open by saying what you made and that it is theirs, free, whether or not they reply.
- State one true fact about how they take bookings today. Exactly one. Never editorialise about it, never use the word "losing", and never mention a percentage or a commission rate.
- Do not offer a call, a demo, an audit, a partnership or a package.
- Do not mention any booking platform by name.
- End with one question that can be answered in one word.
- Never use: unlock, maximise, boost, revenue, hospitality, seamless, elevate, drive direct, I hope this finds you well, quick question, just checking in.
- Do not claim you have stayed there, and do not claim to be local unless you are.

Return only the message text.

## prompt 06 · Set up the database

Paste into Claude Code, Cursor, or any AI coding tool, in your project folder

Set up Supabase for this project using the Supabase CLI, and write the SQL migration for me.

First, if it is not already set up: install the CLI, run `supabase init`, then `supabase link` to my project, and tell me exactly what to paste when it asks for my project ref and database password.

Then create a migration with four tables. Use uuid primary keys with gen_random_uuid() as the default, and give every table a created_at that defaults to now().

TABLE 1, properties. One row per guest house.
  - owner_id, text, not null. My Clerk user id.
  - place_id, text, unique, not null. name, text, not null. town, text. address, text.
  - rating, numeric. review_count, int. photo_count, int.
  - website, text, nullable. Null is the qualifier that matters most in this business.
  - phone, text. agent_number, text, nullable, the Twilio number I bought for them.
  - is_client, boolean, not null, default false.
  - raw, jsonb. status, text, not null, default 'new'.

TABLE 2, enquiries. One row per call the voice agent answered.
  - property_id, references properties, delete on cascade.
  - caller_name, text. callback_number, text.
  - arrival_date, date. nights, int. guests, int. rooms, int.
  - enquiry_type, text. One of 'new_booking','existing_booking','question','other'.
  - urgent, boolean, not null, default false.
  - captured, boolean, not null, default false. False means the agent lost the caller, and this is the number I actually care about.
  - note, text. transcript, text. recording_url, text.
  - owner_notified_at, timestamptz, nullable. Null means the owner has not been told yet.

TABLE 3, booking_requests. One row per submission from the direct booking page.
  - property_id, references properties, delete on cascade.
  - guest_name, text, not null. guest_email, text, not null. guest_phone, text.
  - arrival_date, date, not null. nights, int, not null. guests, int, not null.
  - room_type, text. message, text.
  - status, text, not null, default 'new'. One of 'new','confirmed','declined'.

TABLE 4, assets. One row per rebuilt photograph or film.
  - property_id, references properties, delete on cascade.
  - kind, text. One of 'room_photo','room_film','booking_page'.
  - before_url, after_url, text. model, text. prompt, text.
  - status, text, not null, default 'pending'.

Now the security. Turn on row level security on all four tables. properties has owner_id directly, so compare it against the Clerk user id, which Supabase exposes as auth.jwt() ->> 'sub'. The other three have no owner column, so their policies must join back up to properties.

Add an index on enquiries for (property_id, created_at desc), and a partial index on enquiries where owner_notified_at is null, because a background job will poll exactly that to send the text message.

Explain in plain English what each policy does before you write it, run the migration with `supabase db push`, then show me one test query proving a second user cannot read my rows.
