RB Restoration Builders: AI Receptionist Demo
Independent AI technology demonstration created by Rainier Labs for RB Restoration Builders. Not an official RB Restoration Builders production system.
A working demo of an AI receptionist for a Charlotte, NC roofing and restoration company: the original business website with an AI chat overlay, a Retell voice agent, and a staff dashboard. All leads, calls and appointments in the demo dashboard are synthetic demonstration data unless created live during a demo.
Live demo
- Website: https://rbrestorationbuilders.rainierlabs.io/
- Technical overview (this document): https://rbrestorationbuilders.rainierlabs.io/readme
- Password-protected dashboard: https://rbrestorationbuilders.rainierlabs.io/admin
- AI phone demo: (704) 755-6119
- Health: https://rbrestorationbuilders.rainierlabs.io/healthz
Purpose and business value
RB Restoration Builders provides roofing and restoration services in the Charlotte area. This independent demonstration shows how a receptionist could answer routine questions around the clock, gather service and property details, qualify inspection requests and record callbacks in English or Spanish.
Manual path: customer visits the website, submits a form or calls, then waits for the team to collect the details.
Demo-assisted path: customer starts a chat or calls the AI demo number, receives a source-grounded answer, provides contact and property details, and creates a structured lead or a demonstration inspection request for review in the dashboard.
Staff can review the service needed, location, storm damage, insurance claim status, urgency, consent, conversation history and follow-up information. This demo makes no measured conversion, response-time or revenue claims. No real calendar, CRM or customer notification service is connected.
Website and dashboard
The homepage displays https://www.rbrestorationbuilders.com/ in a full-page, sandboxed frame, using the same approach as Fantastica. The original site's layout, images, navigation and content remain on the original site; they are not copied or re-created. Rainier Labs' orange-to-pink Ask RB Assistant button sits in the bottom-right corner. The chat follows Fantastica's welcome screen, suggested questions, callback/phone bar, trash-icon conversation reset and expand control. Drag the top-left grip (or use arrow keys while it is focused) to resize the desktop dialog; drag the text area's bottom-right handle to resize the composer vertically. Enter submits, Shift+Enter adds a line. On phones the dialog opens full-screen.
The dismissible demonstration ribbon identifies the overlay. Forms and phone links inside the framed website belong to the real RB website and contact RB directly. The AI assistant and the separate /inspection.html form use this demo's backend.
/admin now follows Fantastica's actual dashboard layout and workflows: a gradient header with Dashboard and Knowledge sources, cream background, insights above eight persistent metric tiles, a selected-view results table, date ranges, filters, record details and staff actions. The older RB-specific tools are preserved separately at /admin/operations.
HTML responses include content-hashed stylesheet and script URLs. Frontend assets revalidate rather than remaining fresh for ten minutes, preventing an existing browser from combining a new page with an old layout or chat script after deployment.
Status (honest summary)
| Area | Status |
|---|---|
| Website, chat (EN/ES), lead form, admin dashboard | Built and tested |
| Website conversation engine | Azure OpenAI GPT-4o generates contextual English/Spanish answers from approved RB knowledge and recent conversation history. Deterministic server workflows still control consent, intake and demo booking. |
| Retell voice | GPT-4.1 managed by Retell, not Azure OpenAI. RB agent is published, signed webhook and 8 tool endpoints are deployed. The inbound phone-to-agent binding is verified against Retell's API; an audible end-to-end phone call has not been performed. |
| Phone number | (704) 755-6119 (+17047556119) is bound to the RB agent and displayed in the website and chat. |
| CRM | Demo CRM provider is active. A Kommo adapter (API v4) exists and is unit-tested against a mocked HTTP layer only. It has not been run against a real Kommo account; pipeline status IDs come from KOMMO_STATUS_MAP. |
| Scheduling | Demo scheduler (Mon–Fri 8–5 ET slots, capacity 2). The SchedulingProvider interface is ready for Calendly, Google Calendar, Jobber or similar; no real calendar is connected. |
| SMS / email | Mock only; confirmations are sent only with consent and are off by default. |
| Deployment | Live on Railway (Postgres): https://rbrestorationbuilders.rainierlabs.io (admin at /admin; password is a Railway variable). See "Deployment". |
| Custom domain | GoDaddy DNS is configured and HTTPS is active. Retell's published webhook and all eight custom tools use rbrestorationbuilders.rainierlabs.io. |
No screenshots are committed yet; run the app locally to view it.
Architecture
See docs/architecture.md (Mermaid diagrams: system, inbound call, outbound consented follow-up, scheduling, CRM sync).
Original RB website (full-page frame) + Rainier AI chat overlay
|
Express / TypeScript
/api chat ---- Azure OpenAI GPT-4o
approved facts + recent chat history
|
Retell GPT-4.1 -------- signed webhook + eight voice tools
|
Shared knowledge, lead, call and appointment services
|
Railway Postgres <----> password-protected /admin
|
Demo CRM, scheduling, SMS and email providers
server/: Express 5 API, services (leads, appointments, CRM, calls, transfer, conversation engine), providers (scheduling, CRM, messaging), routes (/api,/retell,/admin/api).web/+public/: vanilla TypeScript bundled with esbuild (customer chat, dashboard selection controls, legacy RB operations SPA). The Fantastica-equivalent dashboard is server-rendered byserver/routes/dashboard.ts.retell/agent.json+docs/retell/system-prompt.md: voice agent definition.- Database: Postgres via
DATABASE_URL; embedded PGlite in./.pglitewhen unset.
| Path | What it is |
|---|---|
/ |
Original RB website with the demonstration ribbon and bottom-right chat |
/inspection.html |
Independent demo intake form and demonstration inspection slots |
/admin |
Password-protected Fantastica-equivalent dashboard |
/admin/sources |
Knowledge sources, retrieval timestamps, change review and sync history |
/admin/operations |
Preserved RB tools: appointments, CRM, analytics, settings, health and voice simulator |
/healthz or /health |
Public service and database health |
/readme |
This technical overview, publicly readable without the admin password |
npm run build:web renders this README into the public documentation page using the same Markdown-to-HTML approach as Fantastica. The page uses the shared dashboard stylesheet and its gradient header, cream background, serif headings, tables and code blocks. Relative repository links point to GitHub, not missing routes on the demo site.
Knowledge and answers
Website chat now uses Azure OpenAI GPT-4o, with the Azure connection reused from Fantastica and configured privately on RB's Railway service. Each model request includes the approved RB catalog, structured intake state and up to the 20 most recent user/assistant messages from that conversation, enabling contextual questions and comparisons. Answers cite validated catalog sources; unsupported source IDs, pricing/percentage claims and workflow confirmations are rejected.
The model can suggest answering, inspection intake or a callback, but cannot execute actions itself. Consent, opt-out, emergency handling, lead writes and booking remain server-controlled. It can answer a side question during intake without treating the question as a name or consent.
Valid name, phone, city/ZIP, consent and slot answers are processed before Azure routing, so a model response cannot restart intake and a provider outage does not block these steps. Already collected details are retained when resuming a callback. A declined contact request creates no callback; repeated submissions reuse the contact's open request. Inspection booking requires an explicit slot number or label, not just "yes."
Request callback opens an inline form (following AFHCouncil's workflow, with RB's existing colors and controls): name, phone or email, optional request details, Cancel and Send request. Submitting explicitly authorizes contact about that request; email-only submissions record separate email consent and do not grant call permission. Requests appear in the admin callback view and retain the linked chat. Validation and do-not-contact restrictions also apply server-side. This independent demo saves requests but does not automatically place calls or send emails.
Use the Español / English button in the chat header to switch languages. The entire interface switches: welcome text, suggestions, controls, accessible labels, phone links, callback form, validation errors and demo notices. New assistant replies use the selected language, even if you write in the other language. Switching preserves conversation context, intake progress and form drafts; the choice stays when closing/reopening or clearing the chat. Existing conversation messages remain as originally written. You can also type Español, Hablar en español or English. Before explicitly selecting a language, the chat detects Spanish automatically. The phone agent is configured for both languages; speak Spanish during the call.
The database stores messages and intake state. Context is scoped to the current chat: closing/reopening keeps it; refreshing the page or clearing the conversation starts a new one. Chat and phone do not automatically share conversational memory. Retell maintains its own GPT-4.1 context during each call.
Azure requires AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY and AZURE_OPENAI_DEPLOYMENT, with AZURE_OPENAI_API_VERSION selecting the API version. Without a complete configuration, local/test chat uses the original rule-based retrieval workflow. If configured Azure fails, the app reports an error; it does not silently pretend a rule-based answer came from the model. Credentials remain server-side and never appear in frontend assets or this document.
The admin's website check records retrieval timestamps and live-content hashes, flags changes for review and keeps the reviewed catalog intact. It does not automatically replace approved answers with newly scraped text. RB has no embeddings or pgvector retrieval: its small approved catalog is supplied directly to Azure; voice knowledge tools use keyword retrieval.
Admin dashboard
The dashboard uses the same page organization, visual styling and interaction pattern as Fantastica:
- Date range: From/To calendar dates, inclusive in Eastern time for RB's Charlotte operation. Month to date is the default; presets are Today, Last 7 days, Last 30 days, Last month and Year to date. The range carries through tiles, filters, details and mutation forms.
- Insights: top concerns, services/products and ZIP codes for that range, above the metric tiles.
- Eight tiles: Conversations, Chats, Phone calls, Total leads, Qualified leads, Consultation requests, Callback requests and Failed conversations. Consultation requests map to RB's inspection requests/appointments. Clicking a tile highlights it and shows its rows; tiles remain on detail pages.
- Phone calls: When / From / Duration / Summary & transcript, linked lead chips, collapsible transcripts and recording players when a recording exists. Filters search caller number, lead name, summary or transcript; duration buckets and Has transcript match Fantastica. Audio uses an authenticated same-origin proxy, validates provider hosts/redirects, supports byte ranges and refreshes expired Retell URLs.
- Lead details: contact/property and qualification fields, summary, linked call summaries, full conversation, independent staff statuses (Open / Contacted / Qualified / Closed), append-only notes and single-record deletion. Staff status does not change AI qualification or remove a do-not-call restriction.
- Bulk deletion: row selection, select all, selected-row highlighting, live selection count and confirmation. Deleting a conversation/call removes related transcripts/calls but keeps leads. Deleting a lead keeps its calls/conversations and retains phone opt-out suppression.
- Knowledge sources: reviewed pages, content previews, timestamps, change-review controls, durable sync-run history and refresh progress. RB's refresh button is enabled; Fantastica's reference currently disables its button. RB has no water-product catalog or snapshot-push integration because its source catalog and business differ.
- RB operations: existing appointments, CRM retry, analytics, settings, system health and simulator remain at
/admin/operationswithout crowding Fantastica's two-link header.
Workflows
- Customer: browse the original RB website, then open the bottom-right AI chat. For the independent demo form, visit
/inspection.html: explicit call-consent checkbox, then pick a demo free-inspection slot. - Chat: "Ask RB Assistant" answers from approved, source-linked knowledge, collects intake step by step and offers demo slots. The callback button opens an inline request form; conversational callback intake also works.
- Voice: Retell calls our tools (
lookup_business_info,create_or_update_lead,check_available_appointments,book_appointment,request_callback,transfer_call,mark_do_not_call,create_crm_note). Webhooks are signature-verified and idempotent. - Transfer: voice transfers are offered only during business hours and when
HUMAN_TRANSFER_NUMBERis set; otherwise the agent offers consented callback intake. Website chat records callback requests rather than claiming a live phone transfer. - English/Spanish: chat and voice detect or accept a language switch.
Security and consent
- Admin: HTTP Basic with
ADMIN_PASSWORD(disabled if unset); server-rendered mutation forms require a signed CSRF token and reject cross-origin requests. Existing JSON API mutations requireX-Requested-With(CSRF guard). Staff changes, notes and deletions are audited and transactional. - Retell webhooks and tools:
x-retell-signatureHMAC verified (rejects missing, stale or tampered); idempotency viawebhook_events. - Zod validation, rate limiting, honeypot field, strict CSP and security headers, PII-safe logging, central error handler, audit trail.
- Consent:
consent_to_callis only set on an explicit yes, with source and timestamp. Opt-out phrases ("stop", "do not call me", "no me llamen") take effect immediately: consent revoked, callbacks cancelled, lead marked do-not-contact. Outbound eligibility requires consent and no DNC (LeadService.canCallOutbound). - Secrets live only in environment variables;
.envis gitignored;npm run secret-scanchecks the tree and history.
Local development
npm install
copy .env.example .env # set ADMIN_PASSWORD at minimum
npm run build:web
npm run dev # http://localhost:3100 (admin: /admin, any user + ADMIN_PASSWORD)
In /admin/operations, Voice Simulator runs 10 scenarios (storm booking, roof leak, Spanish, after hours, human transfer, opt-out, existing customer, pricing question, out of area, CRM outage) against the real business logic.
Environment variables
See .env.example for the full list. Key ones: ADMIN_PASSWORD, DATABASE_URL, PUBLIC_BASE_URL, DEMO_MODE, RETELL_API_KEY, RETELL_WEBHOOK_KEY, RETELL_PHONE_NUMBER, HUMAN_TRANSFER_NUMBER, KOMMO_*, AZURE_OPENAI_*.
Retell
npm run retell:sync -- --dry-run # print payloads, no key needed
npm run retell:sync -- --list-voices
npm run retell:sync # needs RETELL_API_KEY + https PUBLIC_BASE_URL
A phone number is bound only if RETELL_PHONE_NUMBER is set; no existing number is touched otherwise. A built-in warm-transfer tool is added only when HUMAN_TRANSFER_NUMBER is set.
Deployment
The original brief mentioned Azure; this build targets Railway. Create a Postgres plugin, set DATABASE_URL (reference), ADMIN_PASSWORD, DEMO_MODE=true, NODE_ENV=production, PUBLIC_BASE_URL, and the Retell variables, then deploy. Health check: /healthz.
GoDaddy custom domain
The live public URL is https://rbrestorationbuilders.rainierlabs.io. The following records are configured in GoDaddy's DNS management for rainierlabs.io:
| Type | Name | Value | TTL |
|---|---|---|---|
| CNAME | rbrestorationbuilders |
qok1esdz.up.railway.app |
1 hour |
| TXT | _railway-verify.rbrestorationbuilders |
The Railway verification value supplied during setup (also visible in Railway's domain settings) | 1 hour |
GoDaddy appends rainierlabs.io to the Name fields. Change only records for this subdomain, not the root domain or Fantastica's records. Railway has provisioned HTTPS. Retell agent version 1 is published with the webhook and tool URLs on this domain; the purchased number remains bound to that agent. The original Railway URL remains usable as an alternate.
Testing
npm run check && npm test && npm run build && npm run secret-scan
Tests (node:test) cover qualification, dedupe, consent and opt-out (including suppression after deletion), booking and double-booking, CRM failure and retry, the Kommo request shape, Retell signatures and webhook idempotency, voice tools, business hours, transfer decisions, the 10 simulator scenarios, admin auth/CSRF, inclusive date ranges, matching metrics/filters, staff status/notes, atomic deletion, recording ranges/refresh/host validation, knowledge refresh history, Azure request shape/history/isolation/grounding/failures, and public-site checks.
Desktop/mobile browser checks:
npx playwright install chromium
npm run build:web
npm run test:browser
These run against an isolated, in-memory demo database. They verify the actual RB frame, chat controls/resizing, README, Fantastica-equivalent dashboard layout, range navigation, call filters/transcripts/selection, staff status/notes/deletion and preserved RB operations. Destructive browser checks are skipped against remote deployments. The original-site check requires internet access. Screenshots and failure traces are written to gitignored test-results. To check a deployment, set TEST_BASE_URL and TEST_ADMIN_PASSWORD (never commit credentials). Unit/browser tests do not need Azure credentials; the deployed Azure connection and contextual answers are checked separately.
Demo mode
DEMO_MODE=true seeds clearly labeled synthetic data (18 leads, ~22 calls, 10 appointments, a CRM failure and a do-not-call lead), enables the simulator and uses mock providers. POST /admin/api/demo/reset re-seeds.
Known limitations
- Kommo, scheduling, SMS and email are not connected to real systems.
- The voice number is bound, but an audible phone-call test has not been performed.
- Azure web-chat answers are grounded in curated knowledge, not the entire live website. The model can still make mistakes; unknown details should be confirmed by the team. Its context window is bounded to the latest 20 messages.
- Knowledge is a curated snapshot of public website facts, not a live crawl; no testimonials are reproduced.
- Admin auth is a single shared password suitable for a demo, not multi-user RBAC.
- The embedded business website depends on RB's availability and framing policy. The ribbon links to the original website if it cannot be displayed; no proxy bypasses its protections.
- Insurance and financing answers are general; nothing here promises coverage, pricing or rates.