Architecture Overview
KaiVox AI is a monolithic Laravel 11 application with multi-tenancy, two guard auth, a hybrid billing model, and a modular phase-based feature set. This page documents the core architectural decisions.
Tech Stack
| Layer | Technology | Notes |
|---|---|---|
| Backend Framework | Laravel 11 | PHP 8.2 — use D:\xampp\php82\php.exe artisan locally |
| Database | MySQL 8 | DB name: kaivox_ai, root/root locally |
| Frontend | Bootstrap 4.6 + Blade + Vite | No React/Vue — server-rendered Blade templates |
| Build Tool | Vite | Run npm run build after CSS/JS changes |
| AI Engine | OpenAI GPT-4o | Platform-level key, all tenants share one key |
| Telephony (India) | Vobiz | Webhook-based, no SDK dependency |
| Telephony (Global) | Twilio | twilio/sdk Composer package |
| HTTP Client | Guzzle (via Laravel) | Used for OpenAI, Google Calendar, Vobiz API, Open-Meteo |
| Queues | Database driver | QUEUE_CONNECTION=database, email jobs queued |
| Scheduler | Laravel Cron | Appointment reminders (hourly), missed-call follow-up (15 min), phone billing (daily 02:00) |
| Payments | Stripe + Razorpay | Stripe for global, Razorpay for India |
| Local Dev | Laragon on Windows | URL: http://kaivox-ai.test/ |
Multi-Tenancy Design
KaiVox uses a single-database, shared-schema multi-tenancy model. Every table that holds tenant-specific data has a tenant_id foreign key referencing the tenants table.
auth()->user()->tenant or the URL slug.Tenant Resolution
There are two contexts where a tenant is identified:
- Authenticated dashboard requests — tenant comes from
auth()->user()->tenant_id(eager-loaded viaUser::$with = ['tenant']) - Public/webhook requests — tenant resolved from the
{slug}URL segment viaTenant::where('slug', $slug)->firstOrFail()
Tables with tenant_id
All core tables include tenant_id: users, appointments, customers, call_logs, staff, services, locations, messages, knowledge_items, automation_rules, phone_numbers, business_profiles, reviews, visitors, audit_logs, and more.
Two Login Types
| Type | Guard | Login URL | Dashboard | Description |
|---|---|---|---|---|
| World User | web (default) |
/login |
/dashboard |
Business owners and their staff. Every world user belongs to exactly one tenant. |
| Super Admin | super_admin |
/super-admin/login |
/super-admin/dashboard |
Hardik (platform owner). Separate guard, separate session. Full platform visibility. |
User Roles within a Tenant
World users have a role column (owner / admin / staff). The OwnerOnly middleware restricts billing and security routes to role = owner. Helpers available on the User model: isOwner(), isAdmin().
Billing Model
KaiVox uses a hybrid subscription + wallet model — inspired by Vobiz prepaid billing.
| Component | Detail |
|---|---|
| Plans | Starter / Pro / Enterprise — each includes a monthly call-minute quota |
| Quota | Free minutes per month (e.g. Starter = 50 min). After quota exhausted → wallet is charged |
| Wallet | Prepaid balance topped up via Stripe or Razorpay. Deducted per minute of call time |
| Charge point | BillingService::chargeForCall() called at hangup — deducts from quota first, then wallet |
| Per-minute rate | Set by Super Admin per plan. Example: $0.01/min after quota |
| Low balance alert | Automation trigger low_wallet fires when balance drops below threshold |
Directory Structure — Key Files
Controllers
app/Http/Controllers/
├── Auth/ # Login, register, password reset
├── DashboardController.php # Main dashboard KPIs
├── AppointmentsController.php # Booking CRUD + calendar
├── CustomersController.php # CRM profiles + segmentation
├── StaffController.php # Staff management + availability
├── ServicesController.php # Services catalog
├── CallLogsController.php # Call transcripts + sentiment
├── MessagesController.php # Unified inbox
├── AnalyticsController.php # Charts, heatmaps, KPIs
├── BillingController.php # Wallet, invoices, plan upgrade
├── KnowledgeController.php # Knowledge Brain CRUD
├── AutomationController.php # Automation rules builder
├── FrontDeskController.php # Queue management
├── KioskController.php # Kiosk mode (no auth)
├── DisplayController.php # Digital display screen
├── PhoneNumbersController.php # DID marketplace + lifecycle
├── AgencyController.php # Agency dashboard + invites
├── BusinessProfileController.php # Profile settings
├── PublicProfileController.php # GET /p/{slug} — public
├── MarketingController.php # Homepage, pricing, industries
├── SettingsController.php # All settings tabs
└── SuperAdmin/ # Super admin panel controllers
Services
app/Services/
├── ConversationEngine.php # OpenAI GPT-4o call handler
├── VobizService.php # Vobiz webhook responder
├── TwilioService.php # Twilio voice + SMS + WhatsApp
├── SmsService.php # SMS sending abstraction
├── WhatsAppService.php # WhatsApp sending
├── BillingService.php # chargeForCall(), wallet deduction
├── CustomerService.php # syncStats(), VIP auto-promote
├── KnowledgeService.php # buildAiContext() — injects FAQs into AI
├── LocalIntelligenceService.php # Holidays, language, weather for AI
├── AutomationService.php # Trigger checker + action dispatcher
├── IndustryPackService.php # Applies vertical pack to tenant
├── PhoneNumberService.php # DID purchase, billing, release
├── AgencyService.php # Invite → accept → leave flow
├── NotificationService.php # Booking confirmations, reminders
├── WebhookService.php # HMAC-signed outbound webhooks
├── IntentRouter.php # Keyword-based intent detection
├── FailoverService.php # OpenAI failover + fallback responses
├── AuditService.php # Audit log writer
└── GoogleCalendarService.php # OAuth2 + event sync (Guzzle REST)
Models
app/Models/
├── Tenant.php # Core tenant — slug, plan, wallet, relations
├── User.php # World user — role, isOwner(), tenant relation
├── SuperAdmin.php # Separate model for super admin guard
├── Appointment.php # Bookings + status + Google Calendar sync
├── Customer.php # CRM profile — segment, pipeline_stage, LTV
├── CallLog.php # Call transcript, sentiment, duration, score
├── Staff.php # Team member + availability hours
├── Service.php # Service catalog — duration, price
├── Location.php # Multi-location branches
├── Message.php # SMS/email/WhatsApp message thread
├── KnowledgeItem.php # FAQ/SOP/pricing/policy items
├── AutomationRule.php # Trigger → condition → action definition
├── AutomationRun.php # Execution log per automation rule
├── EscalationQueue.php # Human handoff requests
├── PhoneNumber.php # DID — provider, status, billing dates
├── PhoneNumberBilling.php # Per-number billing history
├── BusinessProfile.php # Public profile metadata
├── Review.php # Customer reviews on public profile
├── AgencyClient.php # Pivot — agency_tenant_id, client_tenant_id, status
├── Visitor.php # Kiosk check-in + queue token
├── HolidayTemplate.php # Global holiday DB (98 holidays, 6 countries)
├── WebhookEndpoint.php # Tenant-configured webhook URLs
├── WebhookDelivery.php # Delivery log + retry tracking
└── AuditLog.php # Security audit trail
Key Views
resources/views/
├── layouts/
│ ├── app.blade.php # Main authenticated app layout
│ └── marketing.blade.php # Public marketing site layout
├── dashboard/index.blade.php # World user dashboard
├── super-admin/dashboard.blade.php# Super admin dashboard
├── appointments/ # Calendar + CRUD views
├── customers/ # CRM profiles
├── settings/ # All settings tabs (tabbed UI)
├── public/profile.blade.php # GET /p/{slug} — public profile
├── kiosk/ # Kiosk mode + display screen
├── docs/ # This documentation
└── marketing/ # Homepage, pricing, industries pages
Routes
routes/
├── web.php # World user + public routes
├── api.php # Public REST API v1 (/api/v1/*) + webhooks
└── (super-admin routes in web.php under /super-admin/* prefix)
Request Lifecycle (Authenticated)
Browser request
↓
routes/web.php → middleware: auth, verified
↓
Controller resolves tenant from auth()->user()->tenant
↓
Controller scopes all DB queries to tenant_id
↓
Blade view rendered with tenant-scoped data
↓
Response returned
Request Lifecycle (Webhook / API)
POST /api/vobiz/{slug}/answer
↓
routes/api.php → no auth middleware (CSRF exempt)
↓
VobizController resolves tenant by slug
↓
VobizService::handleAnswer()
↓
ConversationEngine::respond() → OpenAI GPT-4o
↓
XML response returned to Vobiz