CallMineAI Docs
Home
  • Introduction
  • Architecture
  • Installation
  • Web installer
  • Configuration
  • Deployment
  • Customer panel
  • Admin panel
  • Agents & Voices
  • Campaigns & Calls
  • Billing & Credits
  • Telephony (Twilio)
  • Voice & AI
  • SIP trunks
  • Messaging
  • REST API
Home
  • Introduction
  • Architecture
  • Installation
  • Web installer
  • Configuration
  • Deployment
  • Customer panel
  • Admin panel
  • Agents & Voices
  • Campaigns & Calls
  • Billing & Credits
  • Telephony (Twilio)
  • Voice & AI
  • SIP trunks
  • Messaging
  • REST API
  • Getting started

    • Introduction
    • Architecture
    • Installation
    • Web installer
    • Configuration
    • Deployment
  • Using CallMineAI

    • Customer Guide
    • Agents & Voices
    • Campaigns & Calls
    • Billing & Credits
  • Administration

    • Admin Guide
    • Roles & permissions
    • Localization
  • Integrations

    • Telephony (Twilio)
    • Voice & AI
    • SIP trunks
    • Messaging
    • REST API
  • Help

    • FAQ & troubleshooting

Architecture

CallMineAI is a multi-tenant AI voice-calling and lead-intelligence SaaS. This page explains how the pieces fit together: the tech stack, the two authentication panels, the tenancy model, the end-to-end calling pipeline, and the core data model.

Tech stack

LayerTechnology
BackendLaravel 12 (PHP 8.3+)
FrontendInertia.js 2 + React 19, server-driven pages (no separate SPA/API for the UI)
StylingTailwindCSS (Space Grotesk display · Inter body · JetBrains Mono metrics)
BuildVite
DatabaseMySQL (SQLite supported for local/testing)
Cache / Queue / SessionRedis optional — database driver is the default fallback
Voice stackTwilio (telephony) + ElevenLabs (voice synthesis) + OpenAI (gpt-4o-mini agent brain)

Redis is optional

The product runs entirely on the database drivers for cache, queue and session, so no Redis install is required to get started. Point the relevant *_DRIVER env vars at Redis in production if you want the throughput. See Installation.

Two panels, two guards

There are two completely separate authentication contexts. They never share sessions, users, or roles.

PanelGuardModelURL prefixLogin
Customer workspaceweb (default)User (role = client)/app/*/login
Super AdminadminAdminUser/admin/*/admin/login
  • The customer workspace is where organizations build agents, launch campaigns and place calls. Routes live in routes/client.php under the /app prefix with the middleware stack ['web','auth','role:client','client.scope','demo'].
  • The Super Admin panel operates the whole platform (plans, customers, credits, engines, locales, currencies). Routes live in routes/admin.php and are protected by the permission: middleware backed by the admin guard. See the Admin panel guide.

Role and permission details for both panels are in Roles & permissions.

Multi-tenancy model

Each customer organization is one row in the clients table. Every calling artifact — agents, campaigns, contacts, calls, phone numbers, and so on — carries a client_id foreign key and is scoped through the App\Models\Concerns\BelongsToClient trait, which provides a client() relation and a scopeForClient() query scope. The active tenant is resolved by the client.scope middleware, so a signed-in team member only ever sees their own organization's data.

Tenant isolation

Any new model that holds customer data must use the BelongsToClient trait and be queried through its tenant scope. Skipping it leaks data across organizations.

The calling pipeline

Outbound calling is an asynchronous, queue-driven pipeline. Launching a campaign fans work out into per-contact queue rows; a scheduled dispatcher feeds them to the dialer at a rate the ElevenLabs key pool can sustain; each placed call runs a speech loop driven by the agent's LLM; results are then charged, analyzed and pushed to the customer's webhooks.

1. Campaign launch
   └─ CampaignController@launch fans out one CallQueueJob row (status=pending) per contact

2. calling:dispatch  (scheduled command, everyMinute, withoutOverlapping)
   └─ Dialer::dispatchDue computes free slots = pool.capacity − pool.currentLoad
      (capped by dialer_concurrency), claims that many due queue rows, and
      dispatches a PlaceCallJob for each

3. PlaceCallJob
   ├─ acquires an ElevenLabs key from the pool (requeue if none free)
   ├─ verifies the customer has credits
   ├─ creates a Call row (status=ringing)
   └─ TwilioService::placeCall → Twilio dials, TwiML Url points back to the webhook

4. Live call — TwiML <Gather input="speech"> loop
   ├─ TwilioWebhookController streams caller speech in
   ├─ AgentConversationService builds the system prompt (agent + tone + goal +
   │  knowledge base + contact vars) and gets the next reply via the LLM
   │  (AIProviderRegistry → OpenAI), spoken back with <Say>
   └─ loop continues until [END_CALL] or max_turns is reached

5. ProcessCallResultJob  (fired by Twilio status webhook, idempotent)
   ├─ maps Twilio status → Call status, sets duration/ended_at
   ├─ releases the ElevenLabs key back to the pool
   ├─ charges credits for connected calls  (ceil(seconds / 60) ≈ 1 credit/min)
   ├─ dispatches AnalyzeCallJob
   └─ recomputes campaign stats; auto-completes the campaign when the queue drains

6. AnalyzeCallJob
   ├─ CallAnalysisService → LLM produces {summary, sentiment, qualification}
   │  (Whisper transcribe fallback if a recording exists; heuristic fallback with no LLM)
   └─ fires outbound webhooks: call.completed and lead.qualified (hot/warm)

Queue worker + scheduler are required

None of the above runs without a queue worker consuming jobs and the Laravel scheduler triggering calling:dispatch every minute. In production run a Supervisor-managed php artisan queue:work plus the standard scheduler cron entry:

* * * * * cd /path/to/app && php artisan schedule:run >> /dev/null 2>&1

Real calls additionally need valid Twilio/ElevenLabs/OpenAI credentials and a public HTTPS URL so Twilio can reach the TwiML webhooks. See Installation and Billing & Credits.

Data model (key tables)

The schema is centered on the clients tenant table; almost everything below is scoped by client_id.

DomainTables
Tenancy & usersclients, users
Plans & billingplans, subscriptions, credit_ledger, credit_packages
Agents & voiceagents, voices, elevenlabs_keys
Telephonyphone_numbers, sip_trunks
Campaigns & contactscampaigns, contacts
Calling enginecalls, call_queue_jobs
Compliancekyc_documents
Messaging add-onmessaging settings, message/WhatsApp templates, WhatsApp accounts, conversations, messages
Developer API add-onapi_keys (+ API audit logs)
Team RBAC add-onclient_roles, client_permissions (+ role/permission pivots)

credit_ledger is the single source of truth for a customer's balance — the latest row's balance_after is the current balance, and 1 credit is roughly one call minute. The four add-ons (Messaging, REST API, Team RBAC, SIP Trunk) are built-in modules that are plan-gated: each adds its own columns to plans and is toggled per customer by their plan's feature flags.

Last Updated: 7/9/26, 7:12 AM
Prev
Introduction
Next
Installation