A production-ready, multi-tenant voice AI system for automated lead qualification and appointment booking using VAPI.
- Multi-Tenant Architecture - Support unlimited clients with isolated configurations
- Automated Lead Qualification - Smart scoring based on customizable rules
- Google Sheets Integration - Automatic lead logging
- Google Calendar Integration - Automated appointment booking
- Comprehensive Testing - 60+ unit and integration tests
- Modular Configuration - Edit prompts, rules, and settings without code changes
- Production Ready - Built-in error handling, logging, and monitoring
- Node.js 18+
- VAPI account (vapi.ai)
- Google Cloud service account (for Sheets/Calendar)
- ngrok account (for local development)
# 1. Clone and install
npm install
# 2. Configure environment
cp .env.example .env
# Edit .env with your credentials
# 3. Add Google credentials
# Place google-credentials.json in project root
# 4. Start server
npm start
# 5. (Development) Start with ngrok tunnel
npm run dev- Create VAPI Assistant:
node scripts/setup/setup-assistant.js- 
Configure client in clients/demo-landscaping/config.json:- Add assistant ID from step 1
- Update spreadsheet ID
- Customize prompt in prompt.txt
 
- 
Test webhook: 
curl -X POST http://localhost:8547/webhook \
  -H "Content-Type: application/json" \
  -d @scripts/testing/test-webhook.jsvapi/
βββ server.js                 # Main Express server
βββ .env                      # Environment variables
βββ .gitignore               # Git ignore rules
β
βββ clients/                  # π― Multi-tenant configs
β   βββ demo-landscaping/
β       βββ config.json       # Client settings
β       βββ prompt.txt        # Conversation prompt
β       βββ qualification.json # Lead scoring rules
β       βββ tools.json        # Function definitions (NEW!)
β
βββ routes/                   # Express routes (NEW!)
β   βββ webhook.js            # VAPI webhook handler
β
βββ services/                 # Core business logic
β   βββ clientManager.js      # Multi-tenant routing
β   βββ leadProcessor.js      # Lead processing pipeline
β   βββ qualifier.js          # Lead qualification
β   βββ calendar.js           # Google Calendar API
β   βββ vapiAssistant.js      # VAPI API client
β
βββ storage/                  # Storage adapters
β   βββ index.js              # Storage factory
β   βββ json.js               # JSON file storage
β   βββ googleSheets.js       # Google Sheets adapter
β
βββ tests/                    # Test suites (60+ tests)
β   βββ unit/                 # Unit tests
β   βββ integration/          # Integration tests
β
βββ scripts/                  # Utility scripts
β   βββ setup/                # Setup & configuration
β   β   βββ setup-assistant.js       # Create new assistant
β   β   βββ update-assistant.js      # Update assistant config
β   βββ testing/              # Test utilities
β
βββ docs/                     # Documentation
β   βββ ARCHITECTURE-REFACTOR.md
β   βββ FIELD-MAPPING.md
β   βββ ROADMAP.md
β
βββ config/                   # Global configuration
βββ utils/                    # Utilities
βββ data/                     # Local storage (JSON mode)
Main endpoint for VAPI callbacks. Automatically routes to correct client based on assistant ID.
Lists all configured clients with their settings.
Health check showing loaded clients and system status.
View all leads (development only).
Each client has a dedicated folder in clients/ with four configuration files:
clients/your-client-name/
βββ config.json           # Main configuration
βββ prompt.txt            # Conversation prompt
βββ qualification.json    # Lead scoring rules
βββ tools.json           # VAPI function definitions
Create .env file in project root:
# Server
PORT=8547
NODE_ENV=development
# VAPI
VAPI_PRIVATE_KEY=your_private_key
VAPI_PUBLIC_KEY=your_public_key
# Google (optional, can be configured per-client)
GOOGLE_CREDENTIALS_PATH=./google-credentials.json{
  "clientId": "your-client-id",
  "name": "Your Business Name",
  "businessType": "service",
  "assistant": {
    "id": "vapi-assistant-id",
    "phoneNumber": null,
    "promptFile": "./prompt.txt",
    "firstMessage": "Hi! Thanks for calling..."
  },
  "services": ["service1", "service2"],
  "qualification": {
    "rulesFile": "./qualification.json",
    "minScore": 8
  },
  "storage": {
    "type": "googleSheets",
    "spreadsheetId": "your_sheet_id",
    "sheetName": "Sheet1",
    "credentialsPath": "google-credentials.json"
  },
  "calendar": {
    "enabled": true,
    "calendarId": "your@email.com",
    "credentialsPath": "../../google-credentials.json",
    "defaultDuration": 60,
    "timezone": "America/New_York",
    "businessHours": {
      "monday": ["09:00-17:00"],
      "tuesday": ["09:00-17:00"],
      "wednesday": ["09:00-17:00"],
      "thursday": ["09:00-17:00"],
      "friday": ["09:00-17:00"],
      "saturday": [],
      "sunday": []
    }
  }
}JSON (Local):
"storage": {
  "type": "json"
}Google Sheets:
"storage": {
  "type": "googleSheets",
  "spreadsheetId": "your_sheet_id",
  "sheetName": "Sheet1",
  "credentialsPath": "google-credentials.json"
}Airtable (Coming Soon):
"storage": {
  "type": "airtable",
  "apiKey": "your_key",
  "baseId": "your_base"
}- Create client folder:
mkdir -p clients/new-client- Copy template files:
cp clients/demo-landscaping/config.json clients/new-client/
cp clients/demo-landscaping/prompt.txt clients/new-client/
cp clients/demo-landscaping/qualification.json clients/new-client/
cp clients/demo-landscaping/tools.json clients/new-client/- Create VAPI assistant:
node scripts/setup/setup-assistant.js- 
Update config files with assistant ID, spreadsheet ID, and business details 
- 
Restart server to load new client 
npm run add-client --name="New Client" --type="service"npm test# Unit tests only
npm test tests/unit
# Integration tests only
npm test tests/integration
# Specific file
npm test tests/unit/qualifier.test.jsnpm test -- --coverage# Test webhook with sample data
curl -X POST http://localhost:8547/webhook \
  -H "Content-Type: application/json" \
  -d @scripts/testing/test-webhook-data.json
# Check server health
curl http://localhost:8547/health
# List all clients
curl http://localhost:8547/clients# Start server
npm start
# Start with ngrok tunnel (for VAPI testing)
npm run devUpdate conversation prompt:
- Edit clients/your-client/prompt.txt
- Run: node scripts/setup/update-assistant.js your-client
Modify qualification rules:
Edit clients/your-client/qualification.json - changes take effect immediately (no restart needed).
Add/update VAPI functions:
- Edit clients/your-client/tools.json
- Run: node scripts/setup/update-assistant.js your-client
Quick Update Workflow:
# 1. Edit your files
vi clients/demo-landscaping/prompt.txt
vi clients/demo-landscaping/tools.json
# 2. Push changes to VAPI
node scripts/setup/update-assistant.js demo-landscaping
# 3. Test immediately!# Enable verbose logging
NODE_ENV=development npm start
# Check logs for errors
tail -f logs/server.log# Start ngrok tunnel
ngrok http 8547
# Copy the HTTPS URL and update your VAPI assistant webhook URLRailway / Render / Heroku:
- Connect GitHub repository
- Set environment variables
- Add google-credentials.jsonvia file upload or secret
- Deploy
Docker:
# Build image
docker build -t vapi-lead-gen .
# Run container
docker run -p 8547:8547 --env-file .env vapi-lead-genVPS (Ubuntu):
# Install dependencies
sudo apt update
sudo apt install nodejs npm
# Clone and setup
git clone your-repo
cd vapi
npm install
# Run with PM2
npm install -g pm2
pm2 start server.js --name vapi-lead-gen
pm2 startup
pm2 save-  Set NODE_ENV=production
- Configure HTTPS (SSL certificate)
- Secure Google credentials
- Set up monitoring/logging
- Configure webhook URL in VAPI
- Test webhook with sample call
- Set up backup strategy
- Never commit google-credentials.jsonor.env
- Use environment variables for production
- Rotate API keys regularly
- Implement webhook signature verification (see webhookSecretin config)
- PII is sanitized in leadProcessor.js:167
- Implement data retention policies
- Enable Google Sheets access logging
- Consider encryption for sensitive fields
Add rate limiting to prevent abuse:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/webhook', limiter);- Setup Fee: $2,000 - $5,000 (one-time)
- Monthly: $300 - $500 (per client)
- VAPI Costs: Pass through (~$0.05-0.10/min)
- Never miss calls - 47% of small businesses miss calls regularly
- 24/7 availability - No more voicemail
- Instant qualification - AI qualifies leads in real-time
- Automated booking - Direct to calendar
- Time savings - 15-20 min per call saved
Business receiving 50 calls/month:
- Without system: 47% miss rate = 23.5 missed calls
- Lost revenue: 23.5 Γ $350 avg = $8,225/month
- Your cost: $500/month
- ROI: 16x return on investment
- Discovery call - understand their business
- Setup VAPI phone number
- Configure client folder with custom prompt
- Test calls and refine
- Go live and monitor
- Monthly optimization reviews
# Check server is running
curl http://localhost:8547/health
# Verify VAPI assistant webhook URL is correct
# Ensure server is publicly accessible (use ngrok for testing)
# Check logs
tail -f logs/server.log- Verify calendar is shared with service account email
- Check calendar.calendarIdmatches your calendar
- Ensure google-credentials.jsonpath is correct
- Verify Google Calendar API is enabled in Cloud Console
- Share spreadsheet with service account email (found in google-credentials.json)
- Verify storage.spreadsheetIdis correct
- Check sheet name matches storage.sheetName
- Ensure Google Sheets API is enabled
# Clear Jest cache
npm test -- --clearCache
# Run with verbose output
npm test -- --verbose
# Run single test file
npm test tests/unit/qualifier.test.js# Check server logs on startup
npm start
# Verify client folder structure
ls -la clients/your-client/
# Validate JSON syntax
node -e "console.log(JSON.parse(require('fs').readFileSync('clients/your-client/config.json')))"- Architecture: See docs/ARCHITECTURE-REFACTOR.md
- Field Mapping: See docs/FIELD-MAPPING.md
- Roadmap: See docs/ROADMAP.md
- VAPI Docs: docs.vapi.ai
- Fork the repository
- Create feature branch (git checkout -b feature/amazing-feature)
- Run tests (npm test)
- Commit changes (git commit -m 'Add amazing feature')
- Push to branch (git push origin feature/amazing-feature)
- Open Pull Request
MIT License - feel free to use this for your business or clients.