Build an AI Receptionist for Local Businesses with Claude Code

Build an AI phone receptionist that answers calls, checks real availability and books appointments into a protected dashboard.

Roadmap & resources

Requirements

Claude Code

Builds the booking app in Claude Desktop's Code tab, so you never need a terminal. Claude Code needs a paid Claude plan, such as Pro. Also install Node.js 20.19 or newer so the app can run on your computer.

Download Claude Desktop

https://claude.com/download

Download Node.js

https://nodejs.org/en/download

ElevenLabs

Runs the AI voice receptionist. You can build and test on the free plan; calls are billed by the minute, so check ElevenLabs' current pricing for the call volume you expect.

Open ElevenLabs Agents

https://elevenlabs.io/app/agents

Twilio

Gives your receptionist a real phone number. The number has a monthly fee and calls are charged per minute. Which numbers you can buy, and the identity or address checks required, depend on your country. On a trial account only numbers you verify can call it, and every call starts with a Twilio trial message.

Open Twilio Console

https://console.twilio.com

Supabase

Stores services, opening hours and bookings, and signs in the business owner. The free plan is enough to build and launch.

Open Supabase

https://supabase.link/r4gdwhi

Hostinger Node.js Web App hosting

A compatible plan is required: the booking app and the API your receptionist calls run on Node.js. Get it now or when you reach Step 4.

Hostinger Node.js Web App Hosting

Your receptionist's booking app needs hosting that runs its Node.js backend.

Prepare Your AI Receptionist

See How Your AI Receptionist Works

By the end of this guide, a real phone call can end with a real booking in your dashboard:

A customer calls your Twilio phone number Your ElevenLabs agent answers and says it is an AI assistant It answers questions from your business profile It asks your booking app for open times, then books the one the caller picks Your app saves the booking in Supabase, and only then does the agent confirm it The booking appears in your protected dashboard

This guide uses a fictional car detailing business. Keep it for your first run, or enter your own business in the next tasks.

Glossline Car Detailing, a fictional demo business

Four services, from a 60-minute Exterior Wash & Wax to a 180-minute Full Detail

Open Monday to Friday 8:00–18:00 and Saturday 9:00–15:00

Timezone America/New_York, one detailing bay, one appointment at a time

120 Harbor Road, Springfield

Open a New Project in Claude Code

Create an empty folder named ai-receptionist, then open it in Claude Desktop's Code tab:

Claude Desktop Code Local Select the project folder Open Trust Workspace

Create Your Business Profile

Send this prompt. Claude writes business-profile.md, the one file your app and your receptionist both learn the business from. Leave the fields empty to use the demo business.

Create the Business Profile

Click Customize Prompt, add your business details, then copy the prompt.

Create business-profile.md in this folder. It is the single source of truth for an AI phone receptionist: the booking app, the voice agent's knowledge base and the owner dashboard will all use it. Business name: {{BUSINESS_NAME}} Services, with duration and price: {{SERVICES}} Opening hours: {{OPENING_HOURS}} Timezone: {{TIMEZONE}} Location: {{LOCATION}} Receptionist tone: {{TONE}} Write these sections, short and easy to read aloud on a phone call: 1. Business — name, location and a one-sentence description. 2. Services — one line each: name, duration in minutes, price and what is included. 3. Opening hours — every weekday, and the timezone as an IANA name such as America/New_York. 4. Booking policies — one appointment at a time, at least 2 hours' notice, bookings up to 30 days ahead, and changes or cancellations handled by a callback from the team. 5. Frequently asked questions — 6 to 8 questions callers really ask a business like this, answered only from the facts above. Where the answer is not in the facts, write [Confirm: …] instead of inventing it. 6. Receptionist greeting — one sentence that names the business and says the caller is speaking with an AI assistant. 7. Receptionist tone — the tone above, in one sentence. RULES - Use only the details above. Never invent reviews, ratings, awards, years in business, guarantees, staff names or customer numbers. - If the business is described as a fictional demo business, add this line at the top: Demo business for a tutorial. All details are fictional. - Do not create any other files yet. When you finish, list every [Confirm: …] line so I can fill it in.

Business name

e.g. Glossline Car Detailing

Services, with duration and price

Full Detail — 180 minutes — $229

Opening hours

e.g. Mon–Fri 8:00–18:00, Sat 9:00–15:00, Sun closed

Business timezone

e.g. America/New_York or Europe/London

Location

Street address, city

Receptionist tone

Read the file. Replace each [Confirm: …] line with a real answer, or delete it. Your receptionist will only say what this file says.

Build the Booking Application

Create a Supabase Project

Create the project that will hold your services, opening hours and bookings.

Open Supabase

https://supabase.link/r4gdwhi

Create a new project and save its database password somewhere safe. Wait until the project is ready. Open the project's Connect panel and keep the Project URL and the Publishable key at hand. Claude will ask for them.

The Secret key from Project Settings › API Keys is server-only. Give it only to the server's .env file when Claude asks — never put it in frontend code.

Create Your Owner Account

Only the business owner signs in to the dashboard. Create that account yourself:

Supabase Authentication Users

Click Add user, then Create new user Enter your email address and a strong password Keep Auto Confirm User on, then create the user

Then turn off public sign-ups

Nobody else needs an account. Turn off Allow new users to sign up and save.

Supabase Authentication Sign In / Providers

Build the Booking App

Send this prompt. Claude builds the database, the secure API your receptionist will call and the owner dashboard, then tests them.

Build the AI Receptionist Booking App

Supabase schema with double-booking protection, secure voice-agent API routes, optional verified call summaries and a protected owner dashboard — tested before it reports back.

Build the booking app for an AI phone receptionist in this folder. An ElevenLabs voice agent will call its API during phone calls, and the business owner will manage bookings in a protected dashboard. Read business-profile.md first: it is the source of truth for the business name, services, opening hours, timezone and policies. FIXED ARCHITECTURE (do not suggest alternatives) - One project: a React + Vite frontend and a Node.js + Express backend. Express serves the built frontend and every /api route, and listens on process.env.PORT. - package.json: npm run dev for local work, npm run build to build the frontend, npm start to start the Express server. - Supabase for the database and the owner's sign-in. - It will run as a Hostinger Node.js Web App. No other server, queue or hosting service. SUPABASE - Ask me for the Project URL, the Publishable key and the Secret key. Store them in .env as SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY and SUPABASE_SECRET_KEY. - The Secret key is server-only: never send it to the browser, never put it in a VITE_ variable or in the built files. - The browser gets the Project URL and the Publishable key from GET /api/public-config at runtime, so the production build needs no build-time variables. - Make sure .env is in .gitignore, and create .env.example with every variable name and no values. DATABASE (one SQL migration, with seed data taken from business-profile.md) - business_settings: one row with the business name, the IANA timezone, a 30-minute slot interval, 2 hours' minimum notice and a 30-day booking window. - business_hours: one row per weekday with open and close times, or closed. - closed_dates: dates the business is closed. - services: name, short description, duration in minutes, price text, active. - appointments: service, customer name, customer phone, start and end time (timestamptz), status (booked, completed, cancelled, no_show), source (phone or dashboard), a short booking reference and the created time. - callback_requests: customer name, phone, reason, status (new or done) and the created time. - call_logs: ElevenLabs conversation id (unique), start time, duration, success flag and a short summary. No transcripts and no audio. - Prevent double bookings in the database itself: enable btree_gist and add an exclusion constraint so two appointments with status booked can never overlap. The business has one bay, so one appointment at a time. - Enable Row Level Security on every table, with no policies for anon or authenticated users. Only the Express server, using the Secret key, reads or writes data. - Give me the SQL as a migration file and tell me exactly how to run it in the Supabase SQL Editor, or run it for me if you can. VOICE AGENT API (called by ElevenLabs webhook tools) - Every /api/voice route requires the header X-Receptionist-Key. Generate a long random RECEPTIONIST_TOOL_SECRET, store it in .env, compare the header with it in constant time, and return 401 for anything else. - Validate every request body with zod and return 400 with a short, plain-English message for invalid input. Rate-limit the voice routes. - Dates are YYYY-MM-DD and times are HH:MM (24-hour), always in the business timezone. Do the timezone maths with Luxon so daylight-saving changes are handled. - POST /api/voice/check-availability — body: service, date. Match the service by name, ignoring case; if nothing matches, return the active services. Return business_today, the service with its duration, and every start time on that date where the whole appointment fits inside opening hours, respects the minimum notice and booking window, and overlaps no booked appointment. If there are none, also return the next three dates that have times. - POST /api/voice/create-booking — body: service, date, start_time, customer_name, customer_phone. Check everything again on the server: service, opening hours, closed dates, minimum notice, booking window and overlaps. Insert with the database constraint as the final guard. On success return booked: true, the booking reference and the details to read back. If the time is taken or invalid, return booked: false, the reason and alternative times. If the same phone number books the same service at the same time again, return the existing booking instead of creating a second one. - POST /api/voice/request-callback — body: customer_name, customer_phone, reason. Save it and return saved: true. - Every response includes a one-sentence message the agent can say. Never return success unless the row is saved. - Store only the name, phone number and booking details. Never write customer names or phone numbers to the server logs. CALL SUMMARIES (stays off until I add its secret) - POST /api/elevenlabs/post-call receives ElevenLabs post-call webhooks. Before anything else, verify the ElevenLabs-Signature header against the raw request body with the official ElevenLabs JavaScript SDK and ELEVENLABS_WEBHOOK_SECRET. Reject anything that fails. - For post_call_transcription events, store the conversation id, start time, duration, call_successful and transcript_summary in call_logs. Do not store the transcript or audio. - If ELEVENLABS_WEBHOOK_SECRET is not set, return 503 and store nothing. OWNER DASHBOARD - A sign-in page with email and password (Supabase Auth). No sign-up page. - Ask me for my owner email and store it as OWNER_EMAIL. Every dashboard /api route verifies the Supabase session on the server and checks that its email equals OWNER_EMAIL. Anyone else gets 403 and no data. - Appointments: a week view with a column per day, and a list of upcoming appointments, each showing time, service, customer name, phone and status. Open an appointment to see its details and mark it completed, no-show or cancelled. Cancelling frees the time. - Callback requests: newest first, each with Mark done. - Call activity: the latest call summaries, when they exist. - Settings: edit opening hours, closed dates and services (name, duration, price, active). - Clean, calm and readable on desktop and phones, with clear empty states. Everything must still be there after a page refresh. HEALTH - GET /api/health returns ok without exposing any secret. When you finish, run the app and test the API with curl: check availability, create a booking, book the same time again, call without the key, and send an invalid date. Then tell me to sign in to the dashboard and check that the test booking is there. Report each test and its result.

Check the Dashboard

Open the local address Claude gave you and sign in with your owner account.

The test booking appears in the week view

The booking is still there after refreshing the page

Cancelling the test booking frees its time

Create the ElevenLabs AI Receptionist

Create the Agent

Sign in to ElevenLabs and create a new agent from the Blank template. Name it after your business, such as Glossline Receptionist.

Open ElevenLabs Agents

https://elevenlabs.io/app/agents

Choose the Voice and Language

Set how your receptionist sounds:

Open the Voice tab and choose a clear, natural voice that suits your business Set the agent's language to the language your callers speak If the agent lets you choose its LLM, pick one of the stronger models — they call tools more reliably

Higher-quality voices and models can add a little delay to each reply. Try a few voices in the test call later.

Add Your Business Knowledge

Your receptionist answers questions only from your business profile. In the agent's Knowledge Base, add business-profile.md from your project folder as a document.

business-profile.md

Changed the file later? Replace the document in the Knowledge Base, so the receptionist always uses the latest version.

Add the Receptionist Instructions

In the agent's Agent tab, fill in the First message and the System prompt:

First message: paste the Receptionist greeting from business-profile.md System prompt: paste the prompt below In the System prompt editor, set the timezone to your business timezone Save the agent

AI Receptionist System Prompt

A phone-ready receptionist that admits it is an AI, answers only from your business profile, and never confirms a booking your app did not save.

# Role You are the AI phone receptionist for the business described in the Business Profile in your knowledge base. You answer calls, answer questions about the business and book appointments. # Be honest that you are an AI - If anyone asks whether you are a person, say you are an AI assistant. - Never pretend to be a member of staff. # How you speak on the phone - Use the tone the Business Profile describes: calm, clear and helpful. - Keep answers short: one or two sentences, then let the caller speak. - Ask one question at a time. - Say dates as a weekday and date, and times in 12-hour format, for example Tuesday, March 4th at 2:30 PM. - Read phone numbers back in small groups of digits. # Business facts - Use only the Business Profile for services, prices, opening hours, location, policies and answers to questions. - If the answer is not there, or is marked [Confirm: …], do not guess. Say you don't have that information and offer a callback from the team. # Booking an appointment Follow these steps in order: 1. Find out which service the caller wants. If they are unsure, briefly describe the services. 2. Ask which day they would like. Turn days such as tomorrow or next Friday into a date in YYYY-MM-DD format, in the business's timezone. 3. Call check_availability with the service and the date. Use business_today from its answer if you need to correct the date. 4. Offer two or three of the times it returned. Never suggest a time it did not return. If none suit the caller, offer the next dates it returned. 5. When the caller chooses a time, ask for their full name and the best phone number to reach them. 6. Read back the service, day, time, name and phone number, and ask the caller to confirm. 7. Only after they confirm, call create_booking with service, date, start_time (HH:MM, 24-hour), customer_name and customer_phone. 8. If booked is true, say the appointment is booked and give the booking reference. If booked is false, explain briefly and offer the alternative times it returned. # Rules you never break - Never say an appointment is booked, confirmed or reserved unless create_booking returned booked: true during this call. - Never invent availability, prices, services, discounts or policies. - If a tool fails or does not answer, say you could not complete it right now and offer a callback. Try a tool again at most once. - Collect only the caller's name, phone number and appointment details. Never ask for payment details, home addresses, dates of birth or other personal information. If a caller offers sensitive information, politely say you don't need it. # Callbacks When the caller wants a person, asks something you cannot answer, or wants to change or cancel an appointment, offer a callback. Ask for their name, phone number and a short reason, read them back, then call request_callback. Say the team will call them back, and never promise a specific time. # Ending the call Before ending, ask whether there is anything else. Thank the caller by name and say goodbye.

Test a Conversation

Click Test AI agent and talk to your receptionist in the browser. It has no booking tools yet, so it must not offer any times.

It greets callers and says it is an AI assistant

It answers questions about services, hours and location correctly

Asked for an appointment, it does not invent a time

Deploy the Booking App on Hostinger

Get Hostinger Node.js Web App Hosting

ElevenLabs can only reach your booking app once it is live over HTTPS. It needs hosting that runs its Node.js backend.

Get Hostinger Node.js Web App Hosting

Recommended plan: Unlimited, or a Cloud plan. Both run Node.js Web Apps.

USED IN TUTORIAL

Already have one of these plans? Continue below.

Connect Hostinger to Claude

Already connected? Skip to the next task. Otherwise, open the plugin directory:

Claude Desktop Customize Plugins Discover

Find Hostinger and install the official Hostinger plugin. Open the plugin's Connectors tab and connect Hostinger. Sign in to Hostinger in the browser window that opens and approve the access.

Then return to your project's Code session.

Publish the Booking App

Send this prompt. Claude prepares the production app, sets the server variables on Hostinger and deploys it.

Deploy the Booking App to Hostinger

Prepare the React + Express app for production, set its environment variables, deploy it as a Hostinger Node.js Web App and return the live tool URLs.

Deploy this booking app to Hostinger as a Node.js Web App with the Hostinger tools. BEFORE DEPLOYING - Use the Hostinger tools from the Hostinger plugin. If they are not available, or I am not signed in, stop and tell me how to connect. Never ask for my password, and never write a token or key into any file. - Check that my Hostinger plan runs Node.js Web Apps. If it does not, stop and tell me. - If .hostinger/site.json exists, deploy to the website it names. Otherwise list my websites and ask which one to use, or offer a new website on a free temporary domain. Never guess, and confirm with me before replacing any existing website. PRODUCTION APP - package.json has npm run build (builds the React frontend) and npm start (starts the Express server). Express serves the built frontend and the /api routes, and listens on process.env.PORT. - Package the source without node_modules, the build folder or any .env file. Hostinger installs and builds it. ENVIRONMENT VARIABLES - Set every server variable on the Hostinger app in one go, using the values from my local .env: SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, SUPABASE_SECRET_KEY, OWNER_EMAIL and RECEPTIONIST_TOOL_SECRET. Setting them replaces the whole list, so include all of them. - Leave ELEVENLABS_WEBHOOK_SECRET out for now. DEPLOY AND CHECK - Deploy, watch the build, and fix only real build errors. - Open the live address over HTTPS and check that /api/health answers, that the dashboard sign-in page loads, and that a voice route without the key returns 401. - Save the website's domain in .hostinger/site.json. Save only the domain. Give me the live address and these full URLs, one per line: - Check Availability: …/api/voice/check-availability - Create Booking: …/api/voice/create-booking - Request Callback: …/api/voice/request-callback - Post-call webhook: …/api/elevenlabs/post-call

Deploying replaces what is on the Hostinger website you choose. Use a new website or a temporary domain, or one you want to replace.

Sign In to the Live Dashboard

Open the live address Claude gave you and sign in with your owner account.

The live address opens over HTTPS

You can sign in, and your services and opening hours appear in Settings

Claude gave you the four live URLs

Connect Phone Calls and Bookings

Add the Check Availability Tool

Open your agent's Agent section, click Add Tool and choose Webhook. Fill it in with these values and set the URL to the Check Availability URL Claude gave you.

Check Availability

check_availability POST Checks real open appointment times for one service on one date. Call it before offering any time to the caller. Returns available_times in HH:MM (24-hour, business timezone), business_today, and the next available dates when the day is full. X-Receptionist-Key

check_availability

Name

POST

Method

Checks real open appointment times for one service on one date. Call it before offering any time to the caller. Returns available_times in HH:MM (24-hour, business timezone), business_today, and the next available dates when the day is full.

Description

X-Receptionist-Key

Header name

Set the header's type to Secret and click Create New Secret. Name it receptionist_tool_key and paste the RECEPTIONIST_TOOL_SECRET value from the .env file in your project folder.

Body parameters

service date

service

The service name, exactly as listed in the Business Profile

date

The appointment date in YYYY-MM-DD format

Add each body parameter as a required string with the value type LLM Prompt, then save the tool.

Add the Create Booking Tool

Add a second Webhook tool the same way, with the Create Booking URL. For the header, choose the receptionist_tool_key secret you already created.

Create Booking

create_booking POST Books an appointment. Call it only after the caller has confirmed the service, date, time, name and phone number. The appointment exists only if the response has booked: true. X-Receptionist-Key

create_booking

Name

POST

Method

Books an appointment. Call it only after the caller has confirmed the service, date, time, name and phone number. The appointment exists only if the response has booked: true.

Description

X-Receptionist-Key

Header name

Body parameters

service date start_time customer_name customer_phone

service

The service name, exactly as listed in the Business Profile

date

The appointment date in YYYY-MM-DD format

start_time

The chosen start time in HH:MM, 24-hour format, as returned by check_availability

customer_name

The caller's full name

customer_phone

The caller's phone number, with the country code if they give one

Again, every parameter is a required string with the value type LLM Prompt.

Add the Request Callback Tool

Add a third Webhook tool with the Request Callback URL and the same secret header. Your receptionist uses it whenever a caller needs a person.

Request Callback

request_callback POST Saves a callback request for the team when the caller wants a person, asks something you cannot answer, or wants to change or cancel an appointment. X-Receptionist-Key

request_callback

Name

POST

Method

Saves a callback request for the team when the caller wants a person, asks something you cannot answer, or wants to change or cancel an appointment.

Description

X-Receptionist-Key

Header name

Body parameters

customer_name customer_phone reason

customer_name

The caller's full name

customer_phone

The caller's phone number, with the country code if they give one

reason

A short summary of what the caller needs

Test a Booking in the Browser

Save the agent, click Test AI agent and book an appointment by voice. Then open your live dashboard.

The receptionist offers only times your app returned

It confirms the booking and gives a booking reference

The booking appears in your live dashboard

Get a Twilio Phone Number

Buy a number in the Twilio Console that supports voice calls in your country. Use an upgraded account for a real business line: on a trial, only numbers you verify can call it.

Twilio Phone Numbers Manage Buy a number

Some countries ask for identity or address documents before a number is activated. Then copy your Account SID and Auth Token from the Account Info panel on the Twilio Console home page.

The Auth Token gives full access to your Twilio account and its billing. Paste it only into ElevenLabs in the next task — never into a prompt, a file or a chat.

Connect the Number to Your Receptionist

ElevenLabs connects to Twilio for you. Open its phone numbers:

ElevenLabs ElevenAgents Phone Numbers

Start importing a number from Twilio Fill in Label (any name, such as Main line), Phone Number (your Twilio number with its country code), Twilio SID (your Account SID) and Twilio Token (your Auth Token), then import it In the number's agent dropdown, select your receptionist

A verified caller ID is not enough: it can make outbound calls but cannot answer incoming ones. Use the number you bought in Twilio.

Turn On Call Summaries

Optional. Shows a short summary of each call in your dashboard's Call activity. Only the summary is stored — never the recording or the transcript.

Open ElevenAgents Settings

https://elevenlabs.io/app/agents/settings

Add a post-call webhook with the Post-call webhook URL Claude gave you Keep Send audio data turned off Copy the webhook secret ElevenLabs shows

Set the Call Summary Secret

Claude stores the webhook secret on the Hostinger app only, redeploys, and checks that unsigned requests are refused.

I created an ElevenLabs post-call webhook for this app. Ask me for its secret, add it to the Hostinger app's environment variables as ELEVENLABS_WEBHOOK_SECRET together with all the existing variables, and redeploy. Never write it into any file. Then confirm that an unsigned request to /api/elevenlabs/post-call is refused.

Test Your Real AI Receptionist

Call Your AI Receptionist

Cancel any test bookings in your dashboard first. Then call your Twilio number from your own phone:

Call the Twilio number The AI receptionist answers and says it is an AI assistant Ask what the Full Detail includes Ask when you are open on Saturday Ask to book a Full Detail next week The receptionist checks real availability and offers times Choose one of the times it offers Give your name and number, then confirm the booking Open your live dashboard Find the new appointment in the week view

The receptionist answered the call and said it is an AI assistant

It confirmed the booking only after checking availability

The appointment appears in the dashboard with the right service, time, name and phone number

Test the Safeguards

Send this prompt. Claude tests the live API's protections without placing a call.

Test the Live Safeguards

Seven checks against the live API — wrong key, bad input, closed days, double booking at the same moment and repeats — ending with READY or NEEDS ATTENTION.

Test the live booking API's safeguards on the Hostinger app. Use the domain in .hostinger/site.json and the key from my local .env. Report each check as pass or fail. 1. A request without X-Receptionist-Key, or with a wrong one, returns 401 and saves nothing. 2. An invalid body (a missing field, the date 2026-13-45, a time like 25:00) returns 400 with a clear message and saves nothing. 3. A date in the past, on a closed day or outside opening hours is refused with alternatives. 4. Two create-booking requests for the same free time, sent at the same moment with different names, create exactly one booking. 5. Repeating a successful booking with the same phone number returns the same booking, not a second one. 6. An unknown service returns the list of real services. 7. No secret appears in the browser, the built files or the API responses. Use a test customer named Safeguard Test, then cancel every booking you created so the calendar stays clean. Fix only real problems, redeploy if you changed anything, and run the failed checks again. End with exactly one line: READY or NEEDS ATTENTION

Then call the number again and try these yourself:

Asked for a time when you are closed, it offers real alternatives

Asked to book the time you just booked, it says the time is taken

Asked something not in your profile, it offers a callback instead of guessing

The callback request appears in the dashboard

It asked only for your name and phone number

Before Real Customers Call

Your receptionist handles real people's details. Check these before you publish the number:

Every call starts with the greeting that says callers are speaking with an AI assistant

Your business profile has no [Confirm: …] lines left

You know how long ElevenLabs keeps conversation history in your account, and its retention settings match what you tell callers

You have set a spending limit or alert in ElevenLabs and Twilio

Laws on AI disclosure, call recording consent and storing customer data differ by country and state. Check the rules where your business and your callers are, and tell callers how their details are used, before you go live.