# AI Customer Feedback Calling SaaS
## Complete Deployment Guide — Laravel 12 + Vapi AI + Telnyx

---

## TECH STACK

| Layer | Technology |
|---|---|
| Backend | Laravel 12 |
| Database | MySQL 8.0 |
| Frontend | Bootstrap 5, DataTables |
| AI Voice | Vapi AI (GPT-4o powered) |
| Telephony | Telnyx (via Vapi phone number) |
| Queue | Laravel Queue (Redis recommended) |
| Server | Nginx + PHP 8.2 FPM |
| Process Manager | Supervisor |

---

## HOW THE CALL FLOW WORKS

```
Admin clicks "Start Campaign"
        ↓
Laravel dispatches PlaceCallJob for each selected customer
        ↓
PlaceCallJob → Vapi API → POST /call (with customer_name, product_name vars)
        ↓
Vapi dials customer using Telnyx phone number
        ↓
Vapi AI Agent runs GPT-4o conversation
        ↓
Call ends → Vapi sends webhook to: https://yourdomain.com/api/webhooks/vapi
        ↓
Laravel VapiWebhookController stores:
  - calls table (status, duration, summary, sentiment)
  - messages table (each speaker turn)
        ↓
Telnyx also sends CDR webhook to: https://yourdomain.com/api/webhooks/telnyx
        ↓
Laravel TelnyxWebhookController stores raw event in telnyx_events table
```

---

## PROJECT STRUCTURE

```
feedback-saas/
├── app/
│   ├── Http/
│   │   ├── Controllers/
│   │   │   ├── AuthController.php
│   │   │   ├── DashboardController.php
│   │   │   ├── CustomerController.php
│   │   │   ├── CallController.php
│   │   │   ├── CampaignController.php
│   │   │   ├── ReportController.php
│   │   │   └── Webhooks/
│   │   │       ├── VapiWebhookController.php
│   │   │       └── TelnyxWebhookController.php
│   │   └── Middleware/
│   │       └── AdminAuth.php
│   ├── Models/
│   │   ├── User.php
│   │   ├── Customer.php
│   │   ├── Call.php
│   │   ├── Message.php
│   │   └── TelnyxEvent.php
│   ├── Services/
│   │   ├── VapiService.php
│   │   └── SentimentService.php
│   └── Jobs/
│       └── PlaceCallJob.php
├── database/
│   └── migrations/
├── resources/views/
│   ├── layouts/app.blade.php
│   ├── auth/login.blade.php
│   ├── dashboard/index.blade.php
│   ├── customers/index.blade.php
│   ├── calls/index.blade.php
│   └── reports/index.blade.php
├── routes/
│   ├── web.php
│   └── api.php
├── config/
│   └── vapi.php
└── .env.example
```

---

## STEP 1 — SERVER REQUIREMENTS

```bash
# Ubuntu 22.04 LTS
sudo apt update && sudo apt upgrade -y

# PHP 8.2
sudo apt install -y software-properties-common
sudo add-apt-repository ppa:ondrej/php
sudo apt update
sudo apt install -y php8.2 php8.2-fpm php8.2-mysql php8.2-mbstring \
  php8.2-xml php8.2-curl php8.2-zip php8.2-redis php8.2-gd php8.2-cli

# MySQL 8.0
sudo apt install -y mysql-server
sudo mysql_secure_installation

# Nginx
sudo apt install -y nginx

# Redis
sudo apt install -y redis-server
sudo systemctl enable redis-server

# Composer
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer

# Node (for assets if needed)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# Supervisor
sudo apt install -y supervisor
```

---

## STEP 2 — MYSQL SETUP

```sql
CREATE DATABASE feedback_saas CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'feedback_user'@'localhost' IDENTIFIED BY 'StrongPassword123!';
GRANT ALL PRIVILEGES ON feedback_saas.* TO 'feedback_user'@'localhost';
FLUSH PRIVILEGES;
```

---

## STEP 3 — LARAVEL PROJECT SETUP

```bash
# Upload all project files to /var/www/feedback-saas

cd /var/www/feedback-saas

# Install dependencies
composer install --no-dev --optimize-autoloader

# Copy env
cp .env.example .env
php artisan key:generate

# Set permissions
sudo chown -R www-data:www-data /var/www/feedback-saas
sudo chmod -R 755 /var/www/feedback-saas
sudo chmod -R 775 /var/www/feedback-saas/storage
sudo chmod -R 775 /var/www/feedback-saas/bootstrap/cache

# Run migrations
php artisan migrate --force

# Seed admin user
php artisan db:seed --class=AdminSeeder

# Cache config
php artisan config:cache
php artisan route:cache
php artisan view:cache
```

---

## STEP 4 — ENV CONFIGURATION

Edit `/var/www/feedback-saas/.env`:

```env
APP_NAME="Feedback Saas"
APP_ENV=production
APP_KEY=base64:GENERATED_BY_ARTISAN
APP_DEBUG=false
APP_URL=https://yourdomain.com

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=feedback_saas
DB_USERNAME=feedback_user
DB_PASSWORD=StrongPassword123!

QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379

CACHE_DRIVER=redis
SESSION_DRIVER=redis

# Vapi AI
VAPI_API_KEY=your_vapi_private_key_here
VAPI_ASSISTANT_ID=your_vapi_assistant_id_here
VAPI_PHONE_NUMBER_ID=your_vapi_phone_number_id_here

# Telnyx
TELNYX_API_KEY=KEY0_your_telnyx_key
TELNYX_PHONE_NUMBER=+1XXXXXXXXXX
TELNYX_CONNECTION_ID=your_connection_id

MAIL_MAILER=smtp
```

---

## STEP 5 — VAPI DASHBOARD SETUP

1. Go to https://vapi.ai → Dashboard
2. **Create a Phone Number** → Import Telnyx number
   - Or buy a number directly in Vapi
3. **Create an Assistant**:
   - Model: GPT-4o
   - First Message: `Hello {{customer_name}}, this is a customer feedback call regarding your {{product_name}} purchase.`
   - System Prompt (see config/vapi.php for full prompt)
   - Voice: ElevenLabs or Azure Neural (your choice)
4. **Set Webhook URL** in Vapi Dashboard:
   - Server URL: `https://yourdomain.com/api/webhooks/vapi`
5. Copy:
   - `VAPI_API_KEY` → from API Keys section
   - `VAPI_ASSISTANT_ID` → from Assistant settings
   - `VAPI_PHONE_NUMBER_ID` → from Phone Numbers section

---

## STEP 6 — TELNYX DASHBOARD SETUP

1. Go to https://telnyx.com → Portal
2. **Add your number** or buy a new one
3. **Create a Messaging Profile** → set webhook:
   - `https://yourdomain.com/api/webhooks/telnyx`
4. Copy:
   - `TELNYX_API_KEY` → from API Keys
   - `TELNYX_PHONE_NUMBER` → your number in E.164 format
   - `TELNYX_CONNECTION_ID` → from Connections

> **Note**: If using Vapi's built-in phone number, Telnyx webhook is optional. The primary flow uses Vapi for both AI + calling.

---

## STEP 7 — NGINX CONFIGURATION

Save as `/etc/nginx/sites-available/feedback-saas`:

```nginx
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;

    root /var/www/feedback-saas/public;
    index index.php index.html;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
    ssl_prefer_server_ciphers on;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
    add_header X-XSS-Protection "1; mode=block";

    client_max_body_size 50M;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_read_timeout 300;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    error_log /var/log/nginx/feedback-saas-error.log;
    access_log /var/log/nginx/feedback-saas-access.log;
}
```

```bash
sudo ln -s /etc/nginx/sites-available/feedback-saas /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

# SSL with Certbot
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
```

---

## STEP 8 — SUPERVISOR CONFIGURATION

Save as `/etc/supervisor/conf.d/feedback-saas-worker.conf`:

```ini
[program:feedback-saas-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/feedback-saas/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600 --queue=calls,default
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/supervisor/feedback-saas-worker.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
stopwaitsecs=3600
```

```bash
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start feedback-saas-worker:*
sudo supervisorctl status
```

---

## STEP 9 — SSL + FINAL CHECKS

```bash
# Verify app
php artisan about

# Test queue
php artisan queue:monitor redis:calls

# Check logs
tail -f /var/www/feedback-saas/storage/logs/laravel.log

# Restart all services
sudo systemctl restart php8.2-fpm nginx redis-server
sudo supervisorctl restart all
```

---

## DATABASE TABLES OVERVIEW

| Table | Purpose |
|---|---|
| users | Admin accounts |
| customers | Imported customer list |
| calls | Call records with status/summary/sentiment |
| messages | Full conversation transcript per call |
| telnyx_events | Raw Telnyx CDR webhook events |
| campaigns | Campaign grouping (optional) |

---

## DEFAULT ADMIN CREDENTIALS

After seeding:
- **Email**: admin@feedbacksaas.com
- **Password**: Admin@12345

> Change immediately after first login.

---

## EXCEL IMPORT FORMAT

The Excel/CSV must have these exact column headers:

```
customer_name | phone_number | product_name
Ramesh        | +919876543210 | Smart Watch
Kumar         | +919876543211 | Bluetooth Headset
Priya         | +919876543212 | Power Bank
```

---

## WEBHOOK ENDPOINTS

| Endpoint | Method | Purpose |
|---|---|---|
| `/api/webhooks/vapi` | POST | Vapi call events (transcript, summary, status) |
| `/api/webhooks/telnyx` | POST | Telnyx CDR events |

Both are **CSRF-exempt** (added to VerifyCsrfToken middleware).

---

## SECURITY CHECKLIST

- [ ] APP_DEBUG=false in production
- [ ] Change default admin password
- [ ] Restrict MySQL to localhost
- [ ] Enable UFW firewall (allow 80, 443, 22)
- [ ] Set up log rotation
- [ ] Monitor disk space for call logs

```bash
# UFW firewall
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enable
```

---

## TROUBLESHOOTING

**Calls not being placed:**
```bash
# Check queue worker is running
sudo supervisorctl status

# Check failed jobs
php artisan queue:failed

# Retry failed jobs
php artisan queue:retry all
```

**Webhook not receiving:**
```bash
# Verify URL is publicly accessible
curl -X POST https://yourdomain.com/api/webhooks/vapi

# Check nginx logs
tail -f /var/log/nginx/feedback-saas-error.log
```

**Import failing:**
```bash
# Ensure storage is writable
sudo chmod -R 775 /var/www/feedback-saas/storage
sudo chown -R www-data:www-data /var/www/feedback-saas/storage
```
