Skip to content

Repository files navigation

client-intake-agent

A deployable AI intake agent that runs the first pass of your discovery calls for you.

Instead of a dead contact form that dumps name / email / message into your inbox, this is a short, friendly chat that qualifies the lead — asks the right follow-ups, extracts structured answers, scores the opportunity, and drops a hot / warm / cold summary into your database and/or a webhook.

It's a Next.js template you clone, edit one config file, and deploy. The shipped example is an AI-consulting discovery intake (the author's own), but the whole flow is driven by a single typed config — retool it for any business.


Why this exists

Discovery calls are where consultants and agencies burn the most time on the worst-fit leads. The first 10 minutes of nearly every call are the same questions: what do you do, what's the problem, how soon, what's the budget, are you the decision-maker. A plain web form can't ask a good follow-up, so you end up on a call to find out the budget was $500.

This template does that first pass automatically:

Plain contact formclient-intake-agent
InteractionStatic fieldsA real conversation that adapts to answers
Missing / vague answersYou find out on the callThe agent asks a follow-up on the spot
OutputFree-text blobStructured JSON keyed by field
PrioritizationYou read every submissionAuto-scored hot / warm / cold with a reason breakdown
Where it goesYour inboxSupabase table + webhook (Zapier, n8n, CRM, …)

Example. A visitor chats through the intake; the agent extracts:

{
"name": "Dana Reyes",
"company": "Northwind Logistics",
"email": "dana@northwind.co",
"problem": "Manually re-keying supplier invoices into our ERP",
"team_size": 40,
"timeline": "immediately",
"budget": "$20k-$50k",
"decision_maker": true
}

…and scores it before it ever hits your inbox:

{
"score": 100,
"maxScore": 100,
"tier": "Hot",
"breakdown": [
{ "label": "Ready to start now", "points": 30 },
{ "label": "Strong budget", "points": 30 },
{ "label": "Is a decision maker", "points": 20 },
{ "label": "Team of 10+", "points": 10 },
{ "label": "Described a concrete problem", "points": 10 }
]
}

How it works

Visitor ──chat──▶ Next.js UI ──POST /api/chat──▶ Claude (tool use)
│
extracts fields via the `submit_intake` tool
│
┌────────────────┼────────────────┐
score lead store in Supabase POST webhook
(pure, typed) (optional) (optional, signed)

Each turn is one turn-based (non-streaming) call to Claude with a submit_intake tool whose JSON schema is generated from your configured fields. The agent chats normally until it has everything it needs, then calls the tool — that's the signal to score the lead and deliver it. Scoring is a pure, fully-tested function, so the qualification logic is deterministic and doesn't depend on the model.

  • Model: Claude Sonnet 5 by default (override with ANTHROPIC_MODEL).
  • Extraction: Anthropic tool use, schema derived from intake.config.ts.
  • Persistence: Supabase (optional).
  • Delivery: signed webhook (optional). Add your own destinations easily.

Quick start

# 1. Clone (or use this repo as a GitHub template) and install
git clone https://github.com/brutusdev0/client-intake-agent.git
cd client-intake-agent
pnpm install
# 2. Configure environment
cp .env.example .env
# → set ANTHROPIC_API_KEY (required)# → optionally set LEAD_WEBHOOK_URL / Supabase vars# 3. Run
pnpm dev
# open http://localhost:3000

With no webhook or database configured, completed leads are logged to the server console — so you can try the full flow with just an Anthropic key.


Configure your intake

Everything lives in intake.config.ts. You edit this file, not the code under lib/. It has four parts:

exportconstintakeConfig: IntakeConfig={business: {name: 'Rivet AI Consulting',agentName: 'Ava'},greeting: "Hi! I'm Ava… what should I call you, and what company are you with?",persona: 'Warm, concise, curious. Speaks like a consultant on a discovery call.',fields: [{key: 'name',label: 'Name',type: 'text',description: '…',required: true},{key: 'budget',label: 'Budget range',type: 'select',description: 'Rough budget they have in mind.',options: ['under $5k','$5k-$20k','$20k-$50k','$50k+','not sure yet'],required: true,},// …],scoring: {rules: [/* … */],tiers: [/* … */]},};

Fields support text, longtext, email, number, select, and boolean. The description is guidance for the model — be specific; it's what the agent uses to decide what to ask and how to interpret answers. required fields must be collected before the agent can submit.

Scoring

Scoring turns extracted answers into a tier. Each rule awards (or subtracts) points when a field matches a condition; the highest tier whose minScore is met wins.

scoring: {rules: [{field: 'timeline',equals: 'immediately',points: 30,label: 'Ready to start now'},{field: 'budget',oneOf: ['$50k+','$20k-$50k'],points: 30,label: 'Strong budget'},{field: 'decision_maker',equals: true,points: 20,label: 'Is a decision maker'},{field: 'team_size',gte: 10,points: 10,label: 'Team of 10+'},{field: 'problem',present: true,points: 10,label: 'Described a concrete problem'},{field: 'timeline',equals: 'just exploring',points: -10,label: 'Just exploring'},],tiers: [{name: 'Hot',minScore: 70},{name: 'Warm',minScore: 40},{name: 'Cold',minScore: 0},],}

Supported conditions: equals, oneOf, includes (case-insensitive substring), gte, lte, and present. String comparisons are case-insensitive. The breakdown in the output lists exactly which rules fired, so every score is explainable.


Lead destinations

Configure any combination — or none (leads log to the console in dev).

Webhook

Set LEAD_WEBHOOK_URL and every completed lead is POSTed there as JSON. Point it at Zapier, n8n, Make, a CRM, or your own endpoint.

If you also set LEAD_WEBHOOK_SECRET, each request is signed with an X-Signature: sha256=<hmac> header over the raw body. Verify it on your end:

import{createHmac,timingSafeEqual}from'node:crypto';functionverify(rawBody: string,signature: string,secret: string): boolean{constexpected=`sha256=${createHmac('sha256',secret).update(rawBody).digest('hex')}`;consta=Buffer.from(signature);constb=Buffer.from(expected);returna.length===b.length&&timingSafeEqual(a,b);}

The webhook payload is the full lead: { createdAt, fields, score, transcript }.

Supabase

Set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY, then apply the schema:

# Supabase SQL editor, or:
supabase db execute --file supabase/schema.sql

Leads are inserted into a leads table (fields, score, tier, total_score, transcript). The service-role key is used server-side only in the API route — it never reaches the browser, and Row Level Security stays on.

Add your own

Destinations live in lib/destinations/. Write a module beside webhook.ts (email via Resend, a Google Sheet append, a CRM call) and wire it into deliverLead() in lib/destinations/index.ts. Delivery is best-effort per destination — one failing won't block the others or the visitor.


Deploy

This is a standard Next.js app — deploy anywhere that runs Next 16. On Vercel: import the repo, set the environment variables from .env.example, and deploy. The /api/chat route runs on the Node.js runtime.


Project structure

intake.config.ts ← the file you edit (fields, persona, scoring)
app/
page.tsx server component → renders the chat
api/chat/route.ts one turn: run agent → score → deliver
components/Chat.tsx the chat UI (client component)
lib/
intake.ts builds the system prompt + submit_intake tool from config
llm.ts the Anthropic call for one turn
scoring.ts pure, tested lead scoring
db.ts Supabase persistence (optional)
destinations/ webhook + delivery orchestration
types.ts shared types
supabase/schema.sql the leads table
test/ Vitest unit tests for scoring, intake, webhook

Testing

pnpm test# run the suite
pnpm typecheck # tsc --noEmit
pnpm lint # ESLint

The core logic — scoring rules, tool-schema generation, prompt assembly, and webhook signing — is covered by unit tests that run in CI on every push.

License

MIT © Rick (brutusdev0). See LICENSE.

About

AI chat intake agent that runs the first pass of your discovery calls. Qualifies leads, extracts structured answers with Claude, scores them hot/warm/cold, and delivers to Supabase + webhook. Deployable Next.js template, config-driven.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages