CJS.
Back to projects

Case Study · Voice + N8N

AI Voice Receptionist

Self-serve voice demo for HVAC operators. A prospect fills a 5-field form, gets a magic-link email, clicks it, and within seconds an AI receptionist named Casey calls their phone and handles a real booking conversation. After hangup they receive a recording, transcript, and a custom ROI PDF, followed by a Day-3 nudge and Day-10 breakup if they don't reply.

2 workflows · 5-min hard cap · ~$0.50/callVapiClaude Sonnet 4.6Next.jsSupabasen8nResend

The problem

HVAC operators miss roughly 35 calls a week. Each one is a $250 ticket walking out the door, call it $11K/month in recovered revenue at a 30% close rate. They know it. They just don't buy "AI receptionist" pitches because they've heard them, and a deck doesn't prove anything.

I needed a demo that answered the only real question: "does this thing actually sound like a receptionist on the phone?" A 90-second live call with the AI on the prospect's own phone, followed by a recording they can replay and a custom ROI estimate based on their numbers, that's a sales conversation that starts with conviction instead of skepticism.

Architecture

Seven stages spanning a Next.js form, HMAC magic links, Vapi outbound dial, two parallel n8n workflows (post-call + follow-up), and a daily cleanup cron. Numbers in code, judgment in the model. HTTP routes are the state machine.

1

Form Intake + Trust Scoring

A 5-field form on /voice-demo runs Turnstile, Zod validation, MX/disposable-domain checks, and per-IP/email/domain rate limits. Borderline trust scores get triaged by a Haiku call before any phone number is dialed.

Next.jsZodCloudflare TurnstileTrust ScoringClaude Haiku
2

Magic-Link Delivery

An HMAC-signed magic link (independent secret from the audit lane) lands in the prospect's inbox. The token is single-use; clicking is what consumes it, with an atomic UPDATE on consumed_at that rolls back automatically if the Vapi dial fails.

HMAC-SHA256ResendSingle-Use Token
3

Outbound Dial → Live AI Call

Click the link → Vapi dials the prospect's phone. Casey, an HVAC-tuned AI receptionist, opens with a two-party-consent disclosure (legal in CA/FL/MA/PA/IL/WA), then handles a real booking conversation. 5-min hard cap and a $50/mo account spend cap keep cost predictable.

VapiCasey (HVAC Assistant)ElevenLabsTwo-Party Consent
4

End-of-Call Webhook

Vapi's end-of-call webhook hits /api/voice/webhook with constant-time signature verification. The handler does an idempotent insert (unique vapi_call_id) and only fans out to n8n if this request was the inserter; over-emission is harmless because the routes own state transitions.

x-vapi-secret verifyIdempotent Insertn8n Fan-Out
5

Post-Call Pipeline

n8n's Post-Call workflow extracts the transcript, runs two Claude calls with forced tool_choice (no fence-stripping), then computes ROI deterministically in a Code node. Claude can't be trusted with structured math even when the prose is right. The synthesized HTML POSTs to a Vercel function that returns a hosted PDF URL.

Claude AnalysisTool-Choice ForcedDeterministic ROI MathPDF Render
6

Dual Email Delivery

~5 minutes after hangup the prospect gets a recording + transcript + custom ROI PDF + an NPS feedback row. I get a tier-prefixed internal email (hot/qualified/marginal/unqualified) with conversation quotes and signal extraction so I know who's worth following up with.

ResendROI PDFTier-Prefixed InternalNPS Tokens
7

Day-3 + Day-10 Follow-Up

An hourly Schedule Trigger fans to two parallel branches (not sequential, since sequencing would let an empty Day-3 block Day-10). Each branch nominates candidates; the HTTP route re-checks unsubscribed_at + replied_at + ai_tier before sending and advances state via guarded UPDATE. Daily 03:00 UTC cron auto-purges Vapi recordings older than 30 days.

n8n Schedule Triggerfollow_up_status State MachineGuarded Sends

The actual workflows

Both n8n graphs are running in production. The first ingests Vapi's end-of-call webhook and emits the conversion email pair; the second handles Day-3 + Day-10 nudges.

Voice Demo Post-Call workflow in n8n: webhook receiver, Claude analysis, ROI math, PDF render, dual email send.

Voice Demo · Post-Call

17 nodes. Verify Vapi secret → idempotent insert → Claude Analysis (forced tool_choice) → deterministic ROI math → PDF render → 2-email send.

Voice Demo Follow-Up workflow in n8n: hourly schedule trigger, parallel Day-3 and Day-10 branches with guarded HTTP route sends.

Voice Demo · Follow-Up

9 nodes. Hourly trigger fans into 2 parallel branches (Day-3 + Day-10) so an empty Day-3 never blocks Day-10. The HTTP route owns all state transitions.

Key decisions

The choices that separate this from a generic Vapi demo.

Why a self-serve voice demo instead of a sales call

Service operators don't need to be sold AI; they need to feel it. A 90-second live call where the AI asks for their address, books a slot, and confirms the appointment beats any pitch deck. The prospect arrives at the sales conversation already convinced, which compresses the cycle from weeks to days.

Why HTTP routes own state, not n8n

n8n is a scheduler that nominates candidates; every route re-checks every precondition (unsubscribed, replied, status, age, tier) before acting. Over-emission from n8n is harmless: duplicate nominations get rejected by the route. This makes the system crash-safe and easy to reason about: state lives in Postgres with guarded UPDATEs, not in workflow execution memory.

Why ROI math runs in code, narrative runs in Claude

Claude can write a beautiful paragraph and emit a number that doesn't add up, even when forced into structured output. So Claude does the qualitative read of the transcript (signals, quotes, tier) and a deterministic Code node does the arithmetic from extracted inputs. Numbers in code, judgment in the model.

Why hard caps are non-negotiable

Vapi $50/mo account ceiling + 5-min per-call cap + 1 demo per email/7d + 1 per IP/24h + 1 per domain/7d. The unit economics survive abuse: at 50 demos/month the bill is ~$25, and a malicious actor can't burn more than $50 in a month. This is what makes the demo public-facing without supervision.

Precautions baked in

This dials real phones, records real conversations, and emails real prospects. The guardrails are the product.

  • HMAC-signed magic links with secret independent from the audit lane: token leak in one product can't compromise the other
  • Two-party-consent disclosure spoken at call start so the recording is admissible in CA/FL/MA/PA/IL/WA
  • 30-day recording retention with automated daily cleanup cron: privacy-by-default
  • Constant-time verification on Vapi webhook signatures: timing attacks on the shared secret fail silently
  • Atomic single-use UPDATE on magic-link consumption: race conditions can't double-dial a prospect
  • Trust scoring + Haiku triage on borderline submissions before any paid call is placed

Outcome

  • Form fill → live AI phone call in under 60 seconds, fully self-serve
  • Recording + transcript + custom ROI PDF in the prospect's inbox ~5 min after hangup
  • Hard cap of $50/mo on Vapi spend; expected operating cost ~$25/mo at 50 demos
  • Both n8n workflows active in production with synthetic + live-call validation
  • Day-3 nudge and Day-10 breakup automated based on tier, with unsubscribe + reply guards
  • Daily 03:00 UTC cron auto-deletes recordings older than 30 days: zero manual hygiene

Hear what an AI receptionist sounds like on your phone

Drop your number. The AI calls you in seconds. Recording + custom ROI estimate hits your inbox after.