Project Report

LHP AI Telephony Platform

Full-stack voice communication platform for HR/talent recruiting. AI-powered inbound handling, live monitoring, call recording, transcription & grading.

Report Date: March 27, 2026 Stack: React + Express + MySQL + Twilio + OpenAI Branch: main
24
Features Done
2
In Progress / Partial
1
Planned Next
30+
API Endpoints

System Architecture

Caller (PSTN)
Twilio Voice
Express API
:3000
OpenAI
GPT-4o / Whisper
React SPA
Vite + Tailwind
Express API
MySQL 8
:3306
 
MinIO / S3
Recordings
Browser Agent
Twilio Voice SDK
WebSocket /ws
Live Calls UI

Tech Stack

Frontend

React 18 + TypeScript
Vite (build & dev server)
Tailwind CSS + shadcn/ui
React Router v6
Twilio Voice SDK (browser)
React Query / date-fns / sonner

Backend

Node.js + Express
MySQL 8 (mysql2/promise)
Twilio REST API + TwiML
OpenAI (GPT-4o-mini + Whisper)
WebSocket (ws library)
AWS S3 SDK (MinIO compat)

Infrastructure

Docker Compose (4 services)
Nginx (frontend proxy)
MinIO (dev object storage)
Let's Encrypt / Certbot SSL
Domain: twml.lhpbo.com
Frontend: frontend-dev.lhpbo.com

Completed Features

Feature Description Status
Authentication & Roles JWT-based register/login, admin & agent roles, session restore via /auth/me, protected routes Done
Outbound Calls Browser-based dialing via Twilio Voice SDK. FloatingDialer keypad with country selector. OutboundCallModal with call states (connecting, ringing, in-call, ended). Auto call-log via /twilio/call-status Done
AI Inbound Handling Inbound calls on +15046366772 answered by AI agent. OpenAI GPT-4o-mini generates responses. Multi-turn conversation via /twilio/ai-respond loop with speech recognition Done
Business Hours & Voicemail Per-day schedule with timezone support. Emergency closed toggle. Calls outside hours redirect to voicemail recording. Voicemail saved as call_log with type "voicemail" Done
Dual-Phase Recording Separate recordings for AI phase and human phase. AI recording started async after TwiML response. Human recording starts when agent takes over. Both stored in MinIO/S3 Done
Dual-Phase Transcription Whisper STT + GPT-3.5 diarization for both phases. AI conversation also saved directly from chat history. Separate transcript and transcript_human columns Done
Live Call Monitoring WebSocket real-time updates. Live transcript feed with auto-scroll. "Listen In" (muted conference) and "Take Over" (unmuted, stops AI recording, starts human recording) Done
Call Logs & Filters Full CRUD. 7 filters: All, Inbound, Outbound, Voicemail, Failed, Busy/No Answer, Resolved. Count badges. Type column with icons. Recording availability indicator Done
Call Detail Page Dual transcript viewer (AI + Human with speaker bubbles). Dual audio player. Phase indicator banner for transferred calls. Metadata display Done
Call Grading Thumbs up/down grading on call detail page. Toggleable (click again to ungrade). Updates grading_score and review_status Done
Training Feedback Free-text correction notes per call. Saved to correction_notes column. Intended for AI persona improvement loop Done
Contact Management CRUD contacts with name + phone. Alphabetical grouping. Search. Last call info display. Click-to-call outbound Done
Dashboard Stats Total calls, inbound count, missed count, voicemail count. Recent 5 calls list Done
Settings Page AI greeting & system prompt config. Business hours UI with timezone picker (grouped by region, shows UTC offset). Emergency closed toggle. Local time preview for cross-region users Done
Auto Webhook Config configureInboundNumber() at startup auto-sets Twilio voice URL and status callback for inbound number Done
Docker Full Stack 4-service compose: MySQL, MinIO, Backend, Frontend (Nginx). Auto-init DB schema from init.sql. Build-time env injection for frontend Done
FloatingDialer — Recent Tab Recent calls tab with layout, styling, and data display from call history Done
FloatingDialer — Contacts Tab Contacts tab with search and click-to-call integrated into FloatingDialer Done

In Progress / Partial

Feature Description & Status Status
Persona Feedback Loop Training feedback (correction_notes) is stored per call. Next: aggregate grading + corrections into a persona summary. Auto-inject into AI system prompt for continuous improvement Ongoing
Conference Controls Hold, Transfer, and Keypad buttons exist in Live Calls UI but are currently disabled. Core listen & take-over work Partial

Recent Bug Fixes (March 2026)

Fixed  Recording Error 21220
Twilio "not eligible for recording" — recording was await-ed before TwiML response. Moved to setImmediate with 2s delay so Twilio receives TwiML first
Fixed  JSON Double-Parse
mysql2 auto-parses JSON columns into JS objects. Code called JSON.parse() on already-parsed objects → SyntaxError. Added safeParseJson() helper throughout
Fixed  transcript_turns=0
Consequence of the JSON double-parse bug — conversation history silently returned []. Fixed by safeParseJson on all conversation_history reads
Fixed  Missing Inbound Logs
inbound-status silently bailed when active_calls row was missing. Added fallback from Twilio params + pre-create call_log at call start
Fixed  Recording Race Condition
recording-status fired before call_logs row existed. Fixed: cache in active_calls first, retry UPDATE up to 8 times with 3s delay
Fixed  AI Transcript Skip Logic
hasAiTranscript compared parsed array to string "[]" — always truthy. Fixed with safeParseJson + array.length > 0 check

Persona & Feedback System (Design)

How the AI Persona Improves Over Time

Every inbound call generates grading data (good / needs improvement) and optional correction notes from the reviewer. This feedback is stored per-call in call_logs.grading_score and call_logs.correction_notes. The system will aggregate this data into a living Persona Summary that feeds back into the AI agent's system prompt.

1
Call Happens

AI handles inbound call. Transcript & recording saved

2
Review & Grade

Admin reviews call detail. Gives thumbs up/down + correction text

3
Store Feedback

grading_score + correction_notes saved to DB per call

4
Summarize

Periodic aggregation: collect all corrections → summarize patterns → update persona

5
Inject to Persona

Summary appended to agent_settings.system_prompt. AI behavior improves on next call

Component Status Details
Call Grading UI Done Thumbs up/down on CallDetailPage, toggleable, saves to DB
Correction Notes UI Done Textarea on CallDetailPage, saves to correction_notes
Data Storage Done call_logs.grading_score and call_logs.correction_notes columns exist
Feedback Dashboard Planned Visual report: % good vs needs improvement, common correction themes, persona version history

API Endpoints (30+)

Method Endpoint Purpose Auth
POST/auth/registerCreate user account
POST/auth/loginLogin → JWT
GET/auth/meCurrent user profile
POST/twilio/tokenGenerate Voice SDK access token
POST/twilio/voiceTwiML webhook (outbound + conference join)
POST/twilio/call-statusOutbound call completion callback
POST/twilio/inboundAI answers inbound call (business hours check)
POST/twilio/ai-respondAI processes speech & responds (loop)
POST/twilio/inbound-statusInbound call ended → finalize call_log
POST/twilio/voicemailVoicemail TwiML prompt
POST/twilio/voicemail-doneVoicemail recording complete
POST/twilio/conference-statusConference lifecycle events
POST/twilio/recording-statusRecording ready → download, store, transcribe
POST/twilio/transcription-statusTwilio transcription completion
GET/twilio/recording/:sidServe recording audio (MinIO/Twilio proxy)
POST/twilio/transcribe/:idRetroactive Whisper+GPT transcription
GET/active-callsList live AI-handled calls
POST/active-calls/:callSid/monitorListen in or take over call
POST/active-calls/:callSid/agent-responseAgent accept/reject transfer
GET/active-usersOnline user list
GET/call-logsList all call logs
GET/call-logs/:idSingle call detail
POST/call-logsCreate call log
PUT/call-logs/:idUpdate call log (grade, feedback)
DELETE/call-logs/:idDelete call log
GET/agent-settingsGet AI agent config
POST/agent-settingsCreate agent settings
PUT/agent-settings/:idUpdate agent settings
GET/dashboard/statsCall stats & recent activity
GET/contactsList contacts with last call info
POST/contactsCreate contact
DELETE/contacts/:idDelete contact
GET/healthHealth check
WS/wsReal-time admin updates (call events, transcripts)msg

Database Schema

users

id, email (unique), password_hash
full_name, role (admin/agent)
created_at, updated_at

call_logs

id, call_sid (unique)
caller_name, caller_number, call_type
disposition, disposition_label, duration
transcript (JSON), recording_url
transcript_human (JSON), recording_url_human
grading_score, correction_notes
review_status, created_at

active_calls

id, call_sid (unique)
caller_number, caller_name, status
conversation_history (JSON)
recording_sid, recording_sid_human
conference_name, started_at, recording_url

agent_settings

id, timezone
system_prompt, greeting_message
business_hours (JSON)
is_emergency_closed, voice_id

contacts

id, name, phone (unique)
created_at

profiles

id, user_id (unique)
full_name, avatar_url

Planned / Next Up

Feature Description Priority
Conference Hold & Transfer Implement Hold and Transfer buttons in Live Calls intervention panel Medium

Environment & Config

Variable Purpose Scope
VITE_API_URLBackend URL baked into frontend JS at build timeFrontend (build)
CORS_ORIGINAllowed origin for CORSBackend
JWT_SECRETJWT signing keyBackend
TWILIO_ACCOUNT_SIDTwilio account identifierBackend
TWILIO_API_KEY / SECRETTwilio API credentialsBackend
TWILIO_TWIML_APP_SIDTwiML App for Voice URL routingBackend
TWILIO_PHONE_NUMBEROutbound caller ID (+12253503828)Backend
TWILIO_INBOUND_PHONE_NUMBERAI inbound number (+15046366772)Backend
OPENAI_API_KEYOpenAI for GPT responses + Whisper transcriptionBackend
MINIO_*Object storage credentials & bucketBackend
DB_HOST / DB_USER / DB_PASSWORDMySQL connectionBackend
$

Deployment Cost Estimation (Monthly)

Staging Environment

Staging
EC2 — App Server
t3.medium · 2 vCPU · 4 GB RAM · us-east-1
~$30.37
RDS MySQL
db.t3.small · 2 vCPU · 2 GB · 20 GB gp3 storage
~$24.82
S3 Storage
~5 GB recordings · Standard tier
~$0.12
EBS Volume
30 GB gp3 for EC2
~$2.40
Data Transfer
~10 GB outbound (light staging use)
~$0.90
Elastic IP
1 static IP (attached to instance)
$0.00
AWS Subtotal (Staging)
~$58.61/mo

Production Environment

Production
EC2 — App Server
t3.medium · 2 vCPU · 4 GB RAM · us-east-1
~$30.37
RDS MySQL
db.t3.small · 2 vCPU · 2 GB · 50 GB gp3 · Multi-AZ
~$49.64
S3 Storage
~50 GB recordings · Standard tier
~$1.15
EBS Volume
30 GB gp3 for EC2
~$2.40
Data Transfer
~50 GB outbound (recordings + API)
~$4.50
CloudWatch Logs
5 GB ingestion + basic monitoring
~$2.50
AWS Subtotal (Production)
~$90.56/mo

Third-Party Services (Shared Across Environments — Expectation)

~$25.50/mo
Twilio — Phone Numbers
2 numbers · +12253503828 (outbound) + +15046366772 (inbound)
~$2.00
Twilio — Voice Minutes
Estimated ~500 min inbound + 200 min outbound · $0.0085–$0.014/min
~$8.50
Twilio — Recording Storage
~500 recordings · $0.0025/min stored
~$1.75
OpenAI — GPT-4o-mini
AI responses · ~500 calls × ~8 turns avg · $0.15/$0.60 per 1M tokens
~$2.50
OpenAI — Whisper
Transcription · ~500 calls × avg 3 min · $0.006/min
~$9.00
OpenAI — GPT-3.5 Turbo
Diarization post-processing · ~500 transcripts
~$0.75
Domain & SSL
lhpbo.com · Let's Encrypt (free SSL)
~$1.00
Services Subtotal
~$25.50/mo
~$58.61
AWS Staging / mo
~$90.56
AWS Production / mo
~$25.50
Third-Party Services / mo
~$174.67
Total (Staging + Prod + Services)
Note — OpenAI Budget: Current OpenAI API key balance is $20.00. At estimated usage (~500 calls/mo), OpenAI costs run ~$12.25/mo (GPT-4o-mini + Whisper + GPT-3.5 diarization). The $20 balance covers approximately 1.5 months of operation at this volume. Monitor usage via platform.openai.com/usage and top up before depletion to avoid transcription/AI interruption.

Key Discussion Points

AI Training Strategy

  • Persona is the main source of AI behavior — the system prompt in agent_settings defines how the AI agent communicates
  • Feedback from call reviews is collected and stored per call (grading_score + correction_notes)
  • Feedback is summarized periodically, not directly injected into the persona
  • This prevents noise and ensures only validated, pattern-based improvements reach the AI

RAG (Future Approach)

  • Vector database (e.g. Pinecone, pgvector) for scalable knowledge retrieval
  • Enables AI to reference company policies, FAQs, product details without cramming everything into the system prompt
  • Not required for current scope — the persona-based approach handles present conversation complexity well
  • Will become necessary when knowledge base exceeds prompt context limits or requires dynamic updates

Persona Management

  • Persona updated periodically (not in real-time) to maintain stability
  • Avoid too many inputs to prevent hallucination — a bloated prompt leads to confused, inconsistent responses
  • Each update should be a curated summary of the most impactful feedback patterns
  • Keep persona focused: core role, tone, key rules, known edge cases — not a dump of every correction ever received
  • Version the persona so rollbacks are possible if quality degrades

Feedback & Grading

  • Every call can be graded: "Good" or "Needs Improvement"
  • Correction notes provide specific textual feedback on what the AI did wrong and how it should respond instead
  • Feedback is stored and reviewed before any persona update — human-in-the-loop ensures quality
  • Grading data enables evaluation metrics: % good over time, improvement trends, problem areas

AI Capabilities — Current vs Future

Current
  • Basic conversation — multi-turn speech-to-speech via GPT-4o-mini
  • Greeting, Q&A, and escalation to human agent
  • Business hours awareness (voicemail if closed)
  • Call recording & transcription (AI + human phases)
Future
  • Scheduling — AI books appointments directly via calendar API
  • Contact creation — AI adds new contacts from call context
  • Call summary generation — auto-generate post-call summaries for CRM
  • API integrations — connect to external systems (CRM, calendar, ticketing)
  • RAG knowledge retrieval — vector DB for dynamic company knowledge

Decisions Made

Use persona-based approach as the primary AI training method. The system prompt in agent_settings is the single source of truth for AI behavior. This keeps things simple, controllable, and sufficient for current call volume and complexity.
Do NOT inject raw feedback directly into the persona. Raw correction notes are often context-specific, contradictory, or too granular. Injecting them verbatim would bloat the prompt and cause hallucination or inconsistent behavior.
Use summarized feedback for persona updates. A periodic review process: collect all new corrections → identify recurring patterns → distill into concise behavioral rules → update persona. This ensures only high-signal, validated improvements reach the AI.
RAG is deferred to a future phase. The current persona-based approach handles present needs. RAG (vector database for knowledge retrieval) will be implemented when the knowledge base grows beyond what fits in a system prompt or when dynamic, frequently-updated content is needed.
Dual-environment AWS deployment: staging (t3.medium + db.t3.small) for testing, production (t3.medium + db.t3.small Multi-AZ) for live traffic. Both run Docker Compose with the same stack to maintain parity.
OpenAI $20 budget is sufficient for initial launch (~1.5 months at 500 calls/mo). Monitor usage proactively and allocate top-up budget before depletion to avoid service interruption.