Overview
The TelemanAI Web Dialer is a browser-based calling interface powered by Twilio Client SDK. Make and receive calls without any hardware, track call history automatically, and manage conversations with integrated contact information.
Browser-Based No phone hardware required - call from any device
WebRTC Technology Crystal-clear VoIP calls using WebRTC
Call Recording Automatic recording of all conversations
SIP Trunk Support Connect to external SIP trunks for advanced routing
Getting Started
Prerequisites
Before using the Web Dialer, ensure:
Provider Configuration
Set up at least one Twilio provider with your account credentials
Phone Number
Associate a Twilio phone number with your provider
Browser Permissions
Allow microphone access in your browser
TwiML App
Configure a TwiML application (capability token) in your provider settings
Accessing the Dialer
Navigate to the dialer interface from your dashboard:
// From DialerController.php:24-29 - Dialer Index
public function index ( $department = null )
{
generate_user_slug ( auth () -> id ());
return view ( 'backend.dialer.index' , compact ( 'department' ));
}
Making Calls
Outbound Calling
The Web Dialer uses Twilio Client SDK for outbound calls:
// From DialerController.php:34-50 - Generate Dialer Token
public function dialer_token ()
{
$accountSid = user_active_provider_info () -> account_sid ;
$authToken = user_active_provider_info () -> auth_token ;
$appSid = user_active_provider_info () -> capability_token ;
// Generate token for the client
$capability = new ClientToken ( $accountSid , $authToken );
$capability -> allowClientOutgoing ( $appSid );
$capability -> allowClientIncoming ( get_user_identity ( auth () -> id ()));
$token = $capability -> generateToken ( 3600 * 12 ); // 12-hour token
$data [ 'identity' ] = get_user_identity ( auth () -> id ());
$data [ 'token' ] = $token ;
echo json_encode ( $data );
}
How Token Authentication Works
The Web Dialer uses short-lived tokens for security:
Your browser requests a token from the server
Server generates a 12-hour token using your Twilio credentials
Browser uses the token to establish WebRTC connection
Token expires automatically after 12 hours for security
Call Handling
When you make a call, TelemanAI handles the routing:
// From DialerController.php:53-131 - Handle Outbound Call
public function handle_call ( Request $request )
{
$data = $request -> all ();
$response = new VoiceResponse ();
// OUTBOUND CALL
if ( isset ( $data [ 'ApplicationSid' ])) {
$applicationSid = $data [ 'ApplicationSid' ];
$provider = Provider :: where ( 'capability_token' , $applicationSid ) -> first ();
$twilio_number = $provider -> phone ;
$response -> dial ( $data [ 'To' ], [
'callerId' => $twilio_number ,
'record' => 'record-from-answer-dual' , // Record both sides
'action' => '' ,
'timeout' => 20 ,
'method' => 'POST' ,
'statusCallback' => route ( 'dialer.handle_status' ),
'statusCallbackMethod' => 'POST' ,
'statusCallbackEvent' => 'initiated ringing answered completed'
]);
}
return response ( $response ) -> header ( 'Content-Type' , 'text/xml' );
}
All calls are automatically recorded in dual-channel format, capturing both sides of the conversation.
Receiving Calls
Inbound Call Flow
The Web Dialer can receive incoming calls to your Twilio numbers:
// From DialerController.php:88-126 - Handle Inbound Call
else {
// INBOUND CALL
$user_id = Provider :: where ( 'phone' , $data [ 'Called' ]) -> first () -> user_id ;
$identity = Identity :: where ( 'user_id' , $user_id ) -> first () -> identity ;
$response -> pause ([ 'length' => 2 ]);
$agentAvailable = checkAgentAvailability ( $user_id );
if ( $agentAvailable == 1 ) {
// Remove from queue if agent is available
removeFromQueue ( $data [ 'Caller' ], $data [ 'Called' ]);
$dial = $response -> dial ( '' , [
'record' => 'record-from-answer-dual' ,
'action' => '' ,
'timeout' => 20 ,
'method' => 'POST' ,
'statusCallback' => route ( 'dialer.handle_status' ),
'statusCallbackMethod' => 'POST' ,
'statusCallbackEvent' => 'initiated ringing answered completed'
]);
$dial -> client ( $identity );
} else {
// Add to queue if no agents available
moveToQueue ( $data [ 'Caller' ], $data [ 'Called' ], 1 , $user_id );
$response -> say ( 'All of our agents are currently busy...' );
$response -> pause ([ 'length' => 3 ]);
$response -> say ( 'Your call is number ' . getCallerQueueSerialNumber ( $data [ 'Called' ], $data [ 'Caller' ]) . ' in the queue.' );
$response -> redirect ( route ( 'dialer.handle_call' ), [ 'method' => 'POST' ]);
}
}
When all agents are busy, incoming calls are automatically queued:
Caller hears a message about their queue position
System checks for available agents every few seconds
Calls are routed in order when agents become available
Queue position is announced to keep callers informed
Call History
Automatic Tracking
Every call is automatically logged with detailed information:
// From DialerController.php:160-198 - Create Call History
public function createCallHistory ( Request $request )
{
$history = CallHistory :: where ([
'user_id' => auth () -> id (),
'identity_id' => get_user_identity_id ( auth () -> id ()),
'caller_uuid' => $request -> get_caller_uuid_session ,
]) -> first ();
if ( $history ) {
if ( $history -> pick_up_time == null ) {
$history -> pick_up_time = now ();
}
if ( $history -> hang_up_time == null ) {
$history -> hang_up_time = now ();
}
$history -> status = $request -> status ;
$history -> save ();
} else {
$history = CallHistory :: create ([
'user_id' => auth () -> id (),
'identity_id' => get_user_identity_id ( auth () -> id ()),
'my_number' => $request -> my_number ,
'caller_number' => $request -> caller_number ,
'caller_uuid' => $request -> get_caller_uuid_session ,
'pick_up_time' => now (),
'hang_up_time' => $request -> hang_up_time ?? null ,
'status' => $request -> status ,
]);
}
return response () -> json ( $history , 200 );
}
Call History Data Structure
-- From migrations/2023_04_16_231454_create_call_histories_table.php
CREATE TABLE call_histories (
id BIGINT UNSIGNED PRIMARY KEY ,
caller_uuid TEXT , -- Unique call identifier
user_id BIGINT UNSIGNED, -- Agent who handled the call
identity_id BIGINT UNSIGNED, -- Twilio identity
my_number TEXT , -- Your Twilio number
caller_number TEXT , -- Customer's number
pick_up_time TEXT , -- When call was answered
hang_up_time TEXT , -- When call ended
dial_call_sid TEXT , -- Twilio call SID
record_file TEXT , -- Recording URL
status TEXT , -- missed, picked, hanged
created_at TIMESTAMP ,
updated_at TIMESTAMP
);
Call Recording
Automatic Recording
All calls are recorded in dual-channel format:
// From DialerController.php:201-228 - Recording Handler
public function recording ( Request $request )
{
$recordingUrl = $request -> RecordingUrl ?? null ;
$DialCallSid = $request -> DialCallSid ?? null ;
$my_number = $request -> Called ?? null ;
$caller_number = $request -> To ?? null ;
if ( ! $recordingUrl || ! $my_number || ! $caller_number ) {
return response () -> json ([ 'error' => 'Missing required parameters' ], 400 );
}
$add_record_to_call_history = CallHistory :: where ( 'my_number' , $my_number )
-> where ( 'caller_number' , $caller_number )
-> latest ()
-> first ();
if ( $add_record_to_call_history ) {
$add_record_to_call_history -> dial_call_sid = $DialCallSid ?? null ;
$add_record_to_call_history -> record_file = $recordingUrl . '.mp3' ;
$add_record_to_call_history -> save ();
}
return response () -> json ([ 'status' => 'success' ], 200 );
}
Call recording may be subject to legal requirements in your jurisdiction. Always inform callers that the call is being recorded.
AI-Powered Analysis
Analyze recorded calls using OpenAI:
// From DialerController.php:230-246 - Analyze Call Recording
public function analyze_the_call_record ( $file_name )
{
if ( env ( 'OPENAI_API_KEY' ) == null ) {
smilify ( 'error' , translate ( 'OPENAI key is not configured.' ));
return back ();
}
try {
$transcribed_text = analyze_call_record ( $file_name )[ 'transcribed_text' ];
$analyze_call_record = analyze_call_record ( $file_name )[ 'analysis_result' ];
return view ( 'backend.dialer.record_analyze_open_ai' ,
compact ( 'transcribed_text' , 'analyze_call_record' , 'file_name' ));
} catch ( \ Throwable $th ) {
smilify ( 'error' , $th -> getMessage ());
return back ();
}
}
What AI Analysis Provides
When you analyze a call recording, you get:
Transcription - Full text transcript of the conversation
Sentiment Analysis - Overall tone and sentiment
Key Topics - Main discussion points
Action Items - Follow-up tasks identified
Summary - Concise overview of the call
SIP Trunk Integration
Enhanced Calling with SIP Trunks
Connect external SIP trunks for advanced routing:
// From DialerController.php:280-303 - Get SIP Trunk Configuration
public function getSipTrunkConfig ( Request $request )
{
$user = Auth :: user ();
// Get user's active SIP Trunks
$sipTrunks = \App\Models\ SipTrunk :: where ( 'user_id' , $user -> id )
-> where ( 'is_active' , true )
-> get ();
$config = [
'sip_enabled' => config ( 'services.twilio.sip.enabled' , false ),
'sip_trunks' => $sipTrunks -> map ( function ( $trunk ) {
return [
'id' => $trunk -> id ,
'name' => $trunk -> name ,
'domain' => $trunk -> sip_domain ,
'uri' => $trunk -> sip_uri ,
'department' => $trunk -> department ? $trunk -> department -> name : null
];
})
];
return response () -> json ( $config );
}
SIP Trunk Data Structure
-- From migrations/2025_06_20_000001_create_sip_trunks_table.php
CREATE TABLE sip_trunks (
id BIGINT UNSIGNED PRIMARY KEY ,
user_id BIGINT UNSIGNED,
department_id BIGINT UNSIGNED NULL ,
name VARCHAR ( 255 ),
sip_domain VARCHAR ( 255 ),
friendly_name VARCHAR ( 255 ) NULL ,
sip_uri VARCHAR ( 255 ),
is_active BOOLEAN DEFAULT TRUE,
secure BOOLEAN DEFAULT TRUE,
credential_type ENUM( 'IP_ACL' , 'CREDENTIAL_LIST' ) DEFAULT 'IP_ACL' ,
credentials JSON NULL ,
ip_access_control_list JSON NULL ,
voice_url TEXT NULL ,
voice_method ENUM( 'GET' , 'POST' ) DEFAULT 'POST' ,
configuration JSON NULL ,
created_at TIMESTAMP ,
updated_at TIMESTAMP
);
Making SIP Trunk Calls
Route calls through specific SIP trunks:
// From DialerController.php:348-389 - Handle SIP Call
public function handleSipCall ( Request $request )
{
$data = $request -> all ();
$response = new VoiceResponse ();
$sipTrunkId = $request -> input ( 'sip_trunk_id' );
if ( $sipTrunkId ) {
$sipTrunk = \App\Models\ SipTrunk :: find ( $sipTrunkId );
if ( $sipTrunk && $sipTrunk -> user_id === auth () -> id () && $sipTrunk -> is_active ) {
// Route call through SIP Trunk
$response -> dial ( $data [ 'To' ], [
'callerId' => $sipTrunk -> sip_uri ,
'record' => 'record-from-answer-dual' ,
'action' => route ( 'dialer.handle_status' ),
'timeout' => 20 ,
'method' => 'POST' ,
'statusCallback' => route ( 'dialer.handle_status' ),
'statusCallbackMethod' => 'POST' ,
'statusCallbackEvent' => 'initiated ringing answered completed'
]);
Log :: info ( 'SIP Trunk call initiated' , [
'sip_trunk_id' => $sipTrunk -> id ,
'domain' => $sipTrunk -> sip_domain ,
'to' => $data [ 'To' ],
'user_id' => auth () -> id ()
]);
return response ( $response ) -> header ( 'Content-Type' , 'text/xml' );
}
}
}
SIP trunks are useful for integrating with existing phone systems or routing calls through specific carriers.
Call Status Tracking
Real-Time Status Updates
Track call status in real-time:
// From DialerController.php:248-263 - Call Status Callback
public function callStatus ( Request $request )
{
$callSid = $request -> input ( 'CallSid' );
$callStatus = $request -> input ( 'CallStatus' );
// Log the event
\ Log :: info ( 'Twilio Call Status Callback' , [
'CallSid' => $callSid ,
'CallStatus' => $callStatus ,
]);
// Cache status for 5 minutes
\ Cache :: put ( 'call_status_' . $callSid , $callStatus , now () -> addMinutes ( 5 ));
return response ( 'OK' , 200 );
}
Call statuses include:
initiated - Call is being set up
ringing - Phone is ringing
answered - Call was picked up
completed - Call has ended
Agent Availability
The dialer pad automatically sets agent availability:
// From DialerController.php:143-147 - Dialerpad
public function dialerpad ( $my_number = null )
{
updateAgentAvailability ( auth () -> id (), true ); // Mark agent as available
return view ( 'backend.dialer.dialerpad' , compact ( 'my_number' ));
}
Opening the dialer automatically marks you as available
Incoming calls route to available agents only
Unavailable agents don’t receive calls
Close the dialer or log out to become unavailable
Best Practices
Test Audio Test your microphone and speakers before making important calls.
Stable Connection Use a reliable internet connection for best call quality.
Chrome Browser Use Chrome or Edge for best WebRTC compatibility.
Review Recordings Listen to call recordings to improve your communication.
Troubleshooting
Check browser microphone permissions
Verify your audio devices are working
Try refreshing the page to regenerate the token
Verify your provider configuration
Check that you have a valid capability token (TwiML app)
Ensure your Twilio account is funded
Check your internet connection speed
Close other bandwidth-intensive applications
Use a wired connection instead of WiFi if possible
Voice Campaigns Launch automated calling campaigns
Contact Management View contact info during calls
Analytics Analyze call performance and metrics