2nd Place
Agent Jones – AI Front Desk for Local Service Businesses
An AI front desk that chats with customers, books jobs, and hands off tricky cases to humans with context.
Core trick
Claude drafts a personalized opener from lead details, then emits a structured action (BOOKED or ESCALATE) that updates the lead record.
Built with
anthropicclaudehtmljavascriptnext.jsnode.jsreactrest-apitailwind-csstypescriptvercel
Description
Inspiration
Most local service businesses (like mobile detailers) don’t have a real front desk.
It’s usually one owner juggling:
Calls and texts from customers
Web form submissions
Actually doing the work in the field
Missed calls = missed revenue, and even when a form exists, it doesn’t talk back or update the owner’s tools. We wanted an AI “front desk” that:
Feels like a real person, not a generic chatbot
Can actually update a lead’s status and revenue
Knows when to
escalate
instead of over-promising
Gives the owner a clean summary instead of raw logs
What it does
Judge Mode home
Landing page built for judges and owners:
“Start customer flow” → opens the AI chat demo
“Open owner dashboard” → jumps to the business view
Customer chat → lead → booking
Customer fills a short form (name, phone, “What do you need done?”).
We send that context into Claude and generate a personalized first message.
The AI then drives the conversation:
Confirms ZIP / service area
Explains pricing ranges
Offers concrete time slots (e.g., “Saturday 10am or 1pm”)
When the customer says “1pm works”, we:
Infer
status = BOOKED
, service type, estimated revenue
Persist it on the
Lead
so the dashboard updates automatically
Service-area & pricing guardrails
If the ZIP looks borderline (“outside our main service area”) or
the customer is upset about price, the AI:
Stops
short of confirming a job
Sets
status = ESCALATE
via our action contract or fallback logic
The owner can see exactly why it escalated from the transcript.
Owner dashboard
Summary strip:
Leads today
Booked revenue
Escalations needing attention
Filterable/searchable leads table:
Status pills (NEW / QUALIFIED / BOOKED / ESCALATE)
Estimated revenue per lead
CSV export of the filtered view
Clicking a row opens a drawer with:
Full message history
Service details, status, chosen slot, revenue
AI Daily Summary for owners
Owner clicks
“Ask AI for today’s summary”
.
Backend sends Claude a structured snapshot:
Counts by status, total revenue, recent leads, sample messages
Claude replies with a short briefing:
What happened today
Patterns (e.g. “lots of demand from 77005 for interior-only jobs”)
Concrete action items (who to call back, what offers to test)
AI Follow-up Coach for escalations
For
ESCALATE
leads, the drawer shows an
“AI Follow-up Coach”
panel.
Owner clicks
“Draft follow-up message”
:
Claude reads the actual conversation and drafts a ready-to-send SMS / email.
The draft is editable, with
Copy SMS
and
Regenerate
buttons.
For non-escalated leads, the panel is visible but disabled, explaining it’s for pricing / location / angry-customer cases — a gentle onboarding for the feature.
How we built it
Stack
Next.js 15 (App Router) + React + TypeScript
Tailwind CSS for the UI
Vercel for hosting
Anthropic Claude for all AI behavior
AI orchestration
A single
conductor.ts
module builds the
system prompt
, lead context, and user messages.
We use a typed
ActionContract
object to capture:
status
updates
chosen time slot
estimated revenue
escalation flags and updated lead fields
Fallback inference
Sometimes the model forgets to fill the ActionContract.
We run
inferUpdatedLeadFieldsFromReply()
which parses the reply text to infer:
booking confirmations
revenue estimates
whether language like “outside our service area” should force
status = ESCALATE
.
Next.js 15 compatibility
/demo
and
/owner
are server components that render
<Suspense><DemoPageClient/></Suspense>
and
<Suspense><OwnerPageClient/></Suspense>
, keeping all hooks and
useSearchParams
in client components.
Challenges we ran into
Keeping AI and dashboard in sync
Early versions: Claude would say “You’re all set for Saturday at 2pm for $150” but forget to set
status: BOOKED
or revenue.
Fix: added the structured
ActionContract
plus a fallback that infers missing fields from reply text so the UI always reflects reality.
Next.js 15 +
useSearchParams
Our first UI used hooks directly in the app router pages, which broke static generation and Vercel builds.
Fix: refactored
/demo
and
/owner
into thin server components that wrap
<DemoPageClient/>
and
<OwnerPageClient/>
in
<Suspense>
.
Designing good escalation behavior
It’s easy for an AI to over-promise (“You’re all set!”) even when the ZIP is far away or the customer is angry about price.
Fix: added a dedicated
“Service Area & Escalations”
section in the system prompt and a guardrail in our fallback logic so phrases like “outside our main service area” force
status = ESCALATE
instead of booking.
Avoiding repetitive questions
The model occasionally re-asked for phone numbers we already had.
Fix: added a
Phone Number Handling
section in the prompt and made the lead context explicit (
lead.phone
). Claude is told to reference it instead of asking again.
Accomplishments that we’re proud of
Turned a static form into a
fully AI-driven intake + booking flow
.
Built a dashboard that stays in sync with the conversation without manual data entry.
Designed escalation flows where the AI
knows when to hand off
instead of forcing a sale.
Added two owner-facing AI tools:
Daily summary of the business
Follow-up Coach for tricky leads
Got everything working end-to-end on production (with lint + type-safe builds) within the hackathon window.
What we learned
AI is great at conversations but needs
strong structure
(contracts + fallbacks) to be reliable for operations.
Small prompt details (like “don’t re-ask for phone if
lead.phone
exists”) dramatically improve perceived intelligence.
Judging UX matters: a dedicated
Judge Mode
and keyboard shortcuts make it much easier to demo under time pressure.
What’s next for Agent Jones
Mobile owner app + notifications
iOS / Android companion app so owners get instant push notifications for:
New leads
Escalations
“No-response” reminders when a lead goes cold
One-tap actions: call, text, or mark as done.
Google Calendar integration
Sync booked jobs directly into Google Calendar.
Prevent double-booking by checking availability before confirming a time slot.
Send automatic reminders to customers (and optional follow-ups after service).
Multi-business + vertical templates
Let any local business (plumbers, cleaners, med-spa, etc.) configure their own services, pricing, and service areas without touching code.
README excerpt
# AI Front Desk
An AI-powered front desk for local service businesses: it qualifies inbound leads through natural conversation, checks availability against a knowledge base, and books the appointment — no dashboard clicking required on the business side. Built as a hackathon project, prototyped against a Houston car-detailing business. The core idea proved out here — Claude-driven lead qualification and booking — is what grew into [DOBI](https://www.bookwithdobi.com), the production AI booking assistant now running across GrowZone's client businesses.
**See it live:** this prototype's concept is what's running in production today — [try the DOBI demo](https://www.bookwithdobi.com/chat/cv-sample). To run this original prototype itself, see below.
## Stack
Next.js 15 (App Router), TypeScript, Tailwind CSS, Anthropic Claude (Sonnet 4.5). In-memory data store — no external database.
## The hard part: getting an LLM to reliably drive a state machine
The core of this is `lib/conductor.ts` — a prompt + parser pair that turns a chat message into a strict `ActionContract`: `{ reply, action, status, parameters }`. `action` is one of `send_message | offer_slots | create_booking | flag_for_review`; `status` walks a lead through `new → qualifying → qualified → slots_offered → booked` (or `needs_review` at any point). The system prompt makes Claude reason in steps — what stage is this conversation in, what information is still missing, what should happen next — before it commits to t