<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Moisi Trungu</title>
    <description>The latest articles on DEV Community by Moisi Trungu (@moisi_trungu_31647b7ac300).</description>
    <link>https://dev.to/moisi_trungu_31647b7ac300</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2016431%2Fe664cdc4-b987-4a9a-a206-4875fba0b376.png</url>
      <title>DEV Community: Moisi Trungu</title>
      <link>https://dev.to/moisi_trungu_31647b7ac300</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/moisi_trungu_31647b7ac300"/>
    <language>en</language>
    <item>
      <title>ViciDial 'Agent Not Available' Error — Routing &amp; Login Troubleshooting</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Mon, 27 Jul 2026 03:30:04 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/vicidial-agent-not-available-error-routing-login-troubleshooting-5f8d</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/vicidial-agent-not-available-error-routing-login-troubleshooting-5f8d</guid>
      <description>&lt;h1&gt;
  
  
  ViciDial 'Agent Not Available' Error — Routing &amp;amp; Login Troubleshooting
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Learn how to diagnose and fix the "Agent Not Available" error in ViciDial by understanding call routing logic, agent status codes, database configuration, and Asterisk integration issues.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting this troubleshooting guide, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A running ViciDial installation (3.14 or later recommended)&lt;/li&gt;
&lt;li&gt;SSH access to your ViciDial server&lt;/li&gt;
&lt;li&gt;MySQL/MariaDB access to the &lt;code&gt;asterisk&lt;/code&gt; database&lt;/li&gt;
&lt;li&gt;Basic knowledge of Asterisk CLI commands&lt;/li&gt;
&lt;li&gt;Administrator credentials for the ViciDial web interface at &lt;code&gt;/vicidial/admin.php&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Familiarity with ViciDial campaign and agent configuration&lt;/li&gt;
&lt;li&gt;Access to Asterisk logs at &lt;code&gt;/var/log/asterisk/messages&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding the "Agent Not Available" Error
&lt;/h2&gt;

&lt;p&gt;The "Agent Not Available" error in ViciDial occurs when the system attempts to route an inbound call or outbound lead to an agent, but the agent cannot receive the call. This is distinct from an agent simply being offline—it means the agent's status prevents call delivery even though they may be logged in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Root Causes Overview
&lt;/h3&gt;

&lt;p&gt;The error manifests in five primary scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Agent status is not in a call-receiving state&lt;/strong&gt; (paused, not ready, on break)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent phone/device is not registered&lt;/strong&gt; with Asterisk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Campaign routing rules&lt;/strong&gt; exclude the agent from receiving calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database inconsistency&lt;/strong&gt; between agent status and Asterisk registration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Call center phone system configuration&lt;/strong&gt; prevents the agent's extension from accepting calls&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Section 1: Agent Status and Call States
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Understanding ViciDial Agent States
&lt;/h3&gt;

&lt;p&gt;ViciDial agents operate in distinct status codes that determine their availability. The system checks these statuses before routing any call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calls_today&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'AGENT001'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The critical status field contains values that directly impact call routing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Call Receivable&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;READY&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Agent is available for calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QUEUE&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Agent in call queue, can receive preview calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DISPO&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Agent processing after-call work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BREAK&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Agent on break&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LUNCH&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Agent on lunch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NOT READY&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Agent logged in but not accepting calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UNAVAILABLE&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;System-set unavailable status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INCALL&lt;/td&gt;
&lt;td&gt;Yes (current call)&lt;/td&gt;
&lt;td&gt;Agent on active call&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Checking Agent Status via Database
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status_date&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'AGENT001'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CURDATE&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When an agent reports being unable to receive calls, verify their actual database status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# SSH into ViciDial server&lt;/span&gt;
ssh root@vicidial.example.com

&lt;span class="c"&gt;# Query agent status directly&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT user, status, phone_login, last_login_ip FROM vicidial_users WHERE user='AGENT001';"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common Status Code Mismatches
&lt;/h3&gt;

&lt;p&gt;A frequent issue occurs when the agent's database status doesn't match their perceived status in the agent application. This happens when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent forcibly closes the agent screen without logging out properly&lt;/li&gt;
&lt;li&gt;Network interruption breaks the session synchronization&lt;/li&gt;
&lt;li&gt;Browser cache shows outdated status&lt;/li&gt;
&lt;li&gt;Manual database update (admin modification) isn't reflected in the agent UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix: Force agent logout and login&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Via Asterisk CLI&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show channels"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;AGENT001

&lt;span class="c"&gt;# Or force logout via database (last resort)&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"UPDATE vicidial_users SET status='READY' WHERE user='AGENT001';"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 2: Phone Registration and Asterisk Integration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Verifying SIP Registration
&lt;/h3&gt;

&lt;p&gt;The most common cause of "Agent Not Available" errors is the agent's phone or softphone not being registered with Asterisk. ViciDial cannot route calls to an unregistered endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check SIP peer registration&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peers"&lt;/span&gt;

&lt;span class="c"&gt;# Look for your agent's extension/phone&lt;/span&gt;
&lt;span class="c"&gt;# Output should show similar to:&lt;/span&gt;
&lt;span class="c"&gt;# AGENT001/101        192.168.1.50         D  0-127  OK (56 ms)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the agent's extension is missing or shows "UNREACHABLE", the phone is not registered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get detailed SIP peer information&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer 101"&lt;/span&gt;

&lt;span class="c"&gt;# Output includes:&lt;/span&gt;
&lt;span class="c"&gt;# Name       : 101&lt;/span&gt;
&lt;span class="c"&gt;# Qualified  : Yes&lt;/span&gt;
&lt;span class="c"&gt;# Status     : OK (56 ms)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking Extension Registration in ViciDial
&lt;/h3&gt;

&lt;p&gt;ViciDial maintains its own phone/extension mappings separate from raw Asterisk registration. Verify both layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'AGENT001'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;phone_login&lt;/code&gt; field contains the extension number. This must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Match the actual phone extension the agent is using&lt;/li&gt;
&lt;li&gt;Be registered with Asterisk (confirmed via &lt;code&gt;sip show peers&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Not be assigned to multiple agents simultaneously&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Diagnosing Registration Failures
&lt;/h3&gt;

&lt;p&gt;Check the Asterisk SIP log for registration errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View recent Asterisk messages&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"sip&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;register"&lt;/span&gt;

&lt;span class="c"&gt;# Look for entries like:&lt;/span&gt;
&lt;span class="c"&gt;# [Jun 15 14:32:01] REGISTER from sip:101@192.168.1.50 - No matching peer found&lt;/span&gt;
&lt;span class="c"&gt;# [Jun 15 14:32:02] Failed to authenticate user 101&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fixing SIP Registration Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Issue: Extension not registering at all&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Verify the SIP configuration in &lt;code&gt;/etc/asterisk/sip-vicidial.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="m"&gt;101&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;friend&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="n"&gt;dynamic&lt;/span&gt;
&lt;span class="n"&gt;secret&lt;/span&gt;=&lt;span class="n"&gt;yourpassword123&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;default&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;gsm&lt;/span&gt;
&lt;span class="n"&gt;directmedia&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;canreinvite&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;force_rport&lt;/span&gt;,&lt;span class="n"&gt;comedia&lt;/span&gt;
&lt;span class="n"&gt;qualify&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;qualifyfreq&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wrong password&lt;/strong&gt;: The phone must have the exact secret configured&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong context&lt;/strong&gt;: Verify the context matches the dialplan rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NAT issues&lt;/strong&gt;: If agent is behind NAT, ensure &lt;code&gt;nat=yes&lt;/code&gt; is set&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codec mismatch&lt;/strong&gt;: Agent phone and server must support common codecs
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Restart Asterisk to reload SIP configuration&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core reload"&lt;/span&gt;

&lt;span class="c"&gt;# Force re-registration&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip notify resync 101"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Issue: Intermittent registration drops&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This often indicates network issues or aggressive NAT timeouts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# In sip-vicidial.conf, increase keep-alive
&lt;/span&gt;[&lt;span class="m"&gt;101&lt;/span&gt;]
&lt;span class="n"&gt;keepalive&lt;/span&gt;=&lt;span class="m"&gt;20&lt;/span&gt;
&lt;span class="n"&gt;qualify&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;qualifyfreq&lt;/span&gt;=&lt;span class="m"&gt;30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 3: Campaign Routing Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Agent Availability in Campaigns
&lt;/h3&gt;

&lt;p&gt;Campaigns control which agents can receive calls. Even if an agent is READY, they won't receive calls if they're not assigned to the campaign.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_agents&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'CAMPAIGN01'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;agent_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'AGENT001'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this query returns no results, the agent is not assigned to this campaign.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add agent to campaign:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_agents&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'CAMPAIGN01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'AGENT001'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking Campaign Call Distribution Rules
&lt;/h3&gt;

&lt;p&gt;ViciDial campaigns have routing priorities that determine call distribution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;campaign_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent_choose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;lead_distribution_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;predictive_dialer&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'CAMPAIGN01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key fields affecting agent availability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;agent_choose&lt;/code&gt;: Whether agents can choose campaigns (Y/N)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lead_distribution_method&lt;/code&gt;: HOW agents are selected (EVEN, LOAD, RING, MOST_RECENT)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;predictive_dialer&lt;/code&gt;: Campaign mode (manual, preview, power dialer, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Agent Group Configuration
&lt;/h3&gt;

&lt;p&gt;Some deployments use agent groups for logical grouping and routing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_agent_group_members&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;agent_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'AGENT001'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If agents belong to specific groups, the campaign must be configured to route to those groups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_agent_groups&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'CAMPAIGN01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 4: Database Consistency and Synchronization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Detecting Database Inconsistencies
&lt;/h3&gt;

&lt;p&gt;Inconsistencies between the agent database table and running Asterisk state cause "Agent Not Available" errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check running Asterisk channels&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show channels brief"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; agent001

&lt;span class="c"&gt;# Compare with database&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT user, status, channel, bridge_channel FROM vicidial_log 
WHERE user='AGENT001' ORDER BY call_date DESC LIMIT 5;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an agent shows INCALL in the database but no active Asterisk channel exists, the database is stale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cleaning Up Stale Agent Sessions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Mark agents as READY if they've been INCALL for over 2 hours&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'READY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;status_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;calls_today&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;calls_today&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'AGENT001'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'INCALL'&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;status_date&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Verify the change&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status_date&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'AGENT001'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Agent Log Inconsistencies
&lt;/h3&gt;

&lt;p&gt;ViciDial maintains detailed call logs that can reveal where calls are failing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'AGENT001'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; 
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for calls with &lt;code&gt;agent_status&lt;/code&gt; showing as NOT READY or UNAVAILABLE when they should show as READY.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dialer Process Status
&lt;/h3&gt;

&lt;p&gt;The ViciDial daemon processes (&lt;code&gt;astguiclient&lt;/code&gt;, &lt;code&gt;SipServer&lt;/code&gt;, etc.) must be running for call routing to work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check critical ViciDial processes&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"astguiclient&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;SipServer&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;ViciAutoCall"&lt;/span&gt;

&lt;span class="c"&gt;# Typical output should show processes running:&lt;/span&gt;
&lt;span class="c"&gt;# root  12345  0.5  1.2  /usr/share/astguiclient/astguiclient.pl&lt;/span&gt;
&lt;span class="c"&gt;# root  12346  0.3  0.8  /usr/share/astguiclient/SipServer.pl&lt;/span&gt;
&lt;span class="c"&gt;# root  12347  0.2  0.6  /usr/share/astguiclient/ViciAutoCall.pl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If critical processes are missing, restart ViciDial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stop ViciDial&lt;/span&gt;
/etc/init.d/asterisk stop
&lt;span class="nb"&gt;sleep &lt;/span&gt;2

&lt;span class="c"&gt;# Start ViciDial&lt;/span&gt;
/etc/init.d/asterisk start

&lt;span class="c"&gt;# Verify processes started&lt;/span&gt;
&lt;span class="nb"&gt;sleep &lt;/span&gt;5
ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;astguiclient | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 5: Dialplan and Extension Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ViciDial Dialplan Structure
&lt;/h3&gt;

&lt;p&gt;ViciDial routes inbound calls through extension dialplans. Check &lt;code&gt;/etc/asterisk/extensions-vicidial.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="n"&gt;inbound&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;
[&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Answer&lt;/span&gt;()
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;AGENT&lt;/span&gt;=&lt;span class="m"&gt;101&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Dial&lt;/span&gt;(&lt;span class="n"&gt;SIP&lt;/span&gt;/&lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="m"&gt;20&lt;/span&gt;,&lt;span class="n"&gt;m&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;VoiceMail&lt;/span&gt;(&lt;span class="m"&gt;101&lt;/span&gt;@&lt;span class="n"&gt;default&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

; &lt;span class="n"&gt;Campaign&lt;/span&gt; &lt;span class="n"&gt;inbound&lt;/span&gt; &lt;span class="n"&gt;routing&lt;/span&gt;
[&lt;span class="n"&gt;vicidial_campaign&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;3000&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;AGI&lt;/span&gt;(&lt;span class="n"&gt;agi&lt;/span&gt;://&lt;span class="m"&gt;127&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;:&lt;span class="m"&gt;4573&lt;/span&gt;/&lt;span class="n"&gt;call_log&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;3000&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Goto&lt;/span&gt;(&lt;span class="n"&gt;agent&lt;/span&gt;-&lt;span class="n"&gt;available&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;3000&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Voicemail&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;@&lt;span class="n"&gt;campaign&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Agent Extension Dialplan
&lt;/h3&gt;

&lt;p&gt;Each agent extension must have a valid dialplan context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;; &lt;span class="n"&gt;Check&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;SIP&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;
[&lt;span class="n"&gt;agents&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Answer&lt;/span&gt;()
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Dial&lt;/span&gt;(&lt;span class="n"&gt;SIP&lt;/span&gt;/&lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="m"&gt;25&lt;/span&gt;,&lt;span class="n"&gt;m&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the agent's SIP peer context matches a defined context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check agent SIP configuration&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 10 &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;[101&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt; /etc/asterisk/sip-vicidial.conf | &lt;span class="nb"&gt;grep &lt;/span&gt;context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The context should match a defined context in the extensions configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reload Dialplan After Changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Reload dialplan without restarting Asterisk&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan reload"&lt;/span&gt;

&lt;span class="c"&gt;# Verify dialplan loaded&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan show agents"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 6: Practical Troubleshooting Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step-by-Step Diagnostic Process
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Verify Agent Exists and Has Correct Status&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT user, status, phone_login, last_login_ip, calls_today FROM vicidial_users WHERE user='AGENT001';"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected: Status = READY, phone_login populated, recent last_login_ip&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Confirm Phone Registration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT phone_login FROM vicidial_users WHERE user='AGENT001' LIMIT 1;"&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected: Status shows OK or UNKNOWN, Qualify: Yes&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Check Campaign Assignment&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT campaign_id FROM vicidial_campaign_agents WHERE agent_id='AGENT001' AND active='Y';"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected: Returns at least one campaign&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Review Recent Call Attempts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT call_date, user, status, agent_status FROM vicidial_log 
WHERE user='AGENT001' 
AND call_date &amp;gt;= DATE_SUB(NOW(), INTERVAL 1 HOUR) 
ORDER BY call_date DESC LIMIT 5;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected: Recent calls showing READY when attempted&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Check Asterisk Logs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"dial&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;agent&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;unavailable"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for any DIAL errors or unavailable messages related to the agent's extension.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Diagnostic Script
&lt;/h3&gt;

&lt;p&gt;Create a reusable troubleshooting script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# vicidial-agent-check.sh&lt;/span&gt;

&lt;span class="nv"&gt;AGENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;AGENT001&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;MYSQLPASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOURPASSWORD"&lt;/span&gt;
&lt;span class="nv"&gt;MYSQLUSER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cron"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== ViciDial Agent Availability Diagnostic ==="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Agent: &lt;/span&gt;&lt;span class="nv"&gt;$AGENT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"1. Database Agent Record:"&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nv"&gt;$MYSQLUSER&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt;$MYSQLPASS&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT user, status, phone_login, calls_today, last_login_ip FROM vicidial_users WHERE user='&lt;/span&gt;&lt;span class="nv"&gt;$AGENT&lt;/span&gt;&lt;span class="s2"&gt;';"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"2. Asterisk SIP Registration:"&lt;/span&gt;
&lt;span class="nv"&gt;EXTENSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nv"&gt;$MYSQLUSER&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt;$MYSQLPASS&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT phone_login FROM vicidial_users WHERE user='&lt;/span&gt;&lt;span class="nv"&gt;$AGENT&lt;/span&gt;&lt;span class="s2"&gt;';"&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXTENSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer &lt;/span&gt;&lt;span class="nv"&gt;$EXTENSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No extension found for agent"&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"3. Campaign Assignments:"&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nv"&gt;$MYSQLUSER&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt;$MYSQLPASS&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT campaign_id, active FROM vicidial_campaign_agents WHERE agent_id='&lt;/span&gt;&lt;span class="nv"&gt;$AGENT&lt;/span&gt;&lt;span class="s2"&gt;';"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"4. Recent Call Attempts:"&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nv"&gt;$MYSQLUSER&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt;$MYSQLPASS&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT call_date, status, agent_status FROM vicidial_log 
WHERE user='&lt;/span&gt;&lt;span class="nv"&gt;$AGENT&lt;/span&gt;&lt;span class="s2"&gt;' 
AND call_date &amp;gt;= DATE_SUB(NOW(), INTERVAL 1 HOUR) 
ORDER BY call_date DESC LIMIT 5;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"5. Asterisk Process Status:"&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"astguiclient|SipServer"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make it executable and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x vicidial-agent-check.sh
./vicidial-agent-check.sh AGENT001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting Section
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue: Agent Shows READY but Calls Don't Route
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent logged in with READY status&lt;/li&gt;
&lt;li&gt;Incoming calls available but agent doesn't receive them&lt;/li&gt;
&lt;li&gt;No error in agent screen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check if calls are even reaching ViciDial&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"inbound&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;incoming"&lt;/span&gt;

&lt;span class="c"&gt;# Verify campaign is accepting calls&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT campaign_id, active, pause_calls, lead_distribution_method FROM vicidial_campaigns WHERE campaign_id='CAMPAIGN01';"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify campaign is not paused (&lt;code&gt;pause_calls = 'N'&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Check if agent has minimum calls limit reached (&lt;code&gt;vicidial_campaign_agents.max_calls_today&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Ensure campaign's lead pool has available leads&lt;/li&gt;
&lt;li&gt;Verify inbound route points to correct campaign&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Issue: "Agent Not Available" Error After Login
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent successfully logs in&lt;/li&gt;
&lt;li&gt;Immediately receives error message&lt;/li&gt;
&lt;li&gt;Status shows as READY but calls won't come through&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_login_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_login_time&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'AGENT001'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'AGENT001'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;MINUTE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Force agent logout:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'LOGGED OUT'&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'AGENT001'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Clear any stale sessions:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show channels"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;101
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Wait 30 seconds and have agent log back in&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Issue: Phone Keeps Unregistering
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent phone registration drops frequently&lt;/li&gt;
&lt;li&gt;Pattern: Registered → Unreachable → Registered (repeat)&lt;/li&gt;
&lt;li&gt;Calls drop or fail to connect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check registration frequency&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"register.*101"&lt;/span&gt;

&lt;span class="c"&gt;# Check network connectivity from phone&lt;/span&gt;
ping &amp;lt;agent-phone-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Adjust SIP settings in &lt;code&gt;/etc/asterisk/sip-vicidial.conf&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;registerintervalseconds&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;
&lt;span class="n"&gt;qualify&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;qualifyfreq&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;
&lt;span class="n"&gt;notifyringing&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add keep-alive to specific peer:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="m"&gt;101&lt;/span&gt;]
&lt;span class="n"&gt;keepalive&lt;/span&gt;=&lt;span class="m"&gt;25&lt;/span&gt;
&lt;span class="n"&gt;qualify&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;qualifyfreq&lt;/span&gt;=&lt;span class="m"&gt;30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check for network connectivity issues between phone and server:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# From agent phone's perspective (if possible)&lt;/span&gt;
tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"host &amp;lt;vicidial-server-ip&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue: Multiple Agents Not Available Simultaneously
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple agents report unavailability at the same time&lt;/li&gt;
&lt;li&gt;Usually after system restart or database issue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check if astguiclient process is running&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;astguiclient | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;

&lt;span class="c"&gt;# Check for database connection issues&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"database&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;mysql&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;connection"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Restart ViciDial services:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/etc/init.d/asterisk stop
&lt;span class="nb"&gt;sleep &lt;/span&gt;3
/etc/init.d/asterisk start
&lt;span class="nb"&gt;sleep &lt;/span&gt;5

&lt;span class="c"&gt;# Verify processes&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;astguiclient | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="c"&gt;# Should return at least 3-4 processes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Reset all agent statuses (nuclear option - use sparingly):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'READY'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'NOT READY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'BREAK'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'LUNCH'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'UNAVAILABLE'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;last_login_time&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Verify&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'READY'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue: New Agent Cannot Get Calls
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Newly created agent account&lt;/li&gt;
&lt;li&gt;Phone registered successfully&lt;/li&gt;
&lt;li&gt;Campaign assigned but no calls received&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'AGENT_NEW'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_agents&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;agent_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'AGENT_NEW'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify both records exist and are active.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify user group permissions:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_user_groups&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;user_group&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'AGENT_NEW'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check if user group is assigned to campaign:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_user_groups&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'CAMPAIGN01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Ensure agent phone extension is unique:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_login&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;phone_login&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'101'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'AGENT_NEW'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Should return 0 rows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue: Agent Can Dial Out But Not Receive Calls
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptoms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent can make outbound calls successfully&lt;/li&gt;
&lt;li&gt;Inbound/campaign calls don't arrive&lt;/li&gt;
&lt;li&gt;No error messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check dialplan context for agent&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 5 &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;[101&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt; /etc/asterisk/sip-vicidial.conf | &lt;span class="nb"&gt;grep &lt;/span&gt;context

&lt;span class="c"&gt;# Verify context exists&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan show &amp;lt;context&amp;gt;"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify SIP peer context matches a valid context with agent extensions:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="m"&gt;101&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;friend&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;agents&lt;/span&gt;     &lt;span class="c"&gt;# This context must be defined
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Reload dialplan:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check campaign inbound context routing:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify campaign routing&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT campaign_id, answer_phone_number FROM vicidial_campaigns WHERE campaign_id='CAMPAIGN01';"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The "Agent Not Available" error in ViciDial typically stems from one of five areas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Agent Status Issues&lt;/strong&gt;: Agent in non-available status (NOT READY, BREAK, LUNCH). Fix by checking &lt;code&gt;vicidial_users.status&lt;/code&gt; and ensuring agent transitions to READY.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Phone Registration&lt;/strong&gt;: Agent's extension not registered with Asterisk. Verify via &lt;code&gt;sip show peers&lt;/code&gt; and check SIP configuration in &lt;code&gt;/etc/asterisk/sip-vicidial.conf&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Campaign Routing&lt;/strong&gt;: Agent not assigned to campaign or campaign paused. Verify &lt;code&gt;vicidial_campaign_agents&lt;/code&gt; and &lt;code&gt;vicidial_campaigns&lt;/code&gt; tables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Database Inconsistency&lt;/strong&gt;: Stale database records not matching live Asterisk state. Clean up via targeted UPDATE queries and restart ViciDial services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dialplan/Extension Issues&lt;/strong&gt;: Invalid dialplan context or missing extension configuration. Check &lt;code&gt;/etc/asterisk/extensions-vicidial.conf&lt;/code&gt; and reload with &lt;code&gt;dialplan reload&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Best Practices Going Forward
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monitor agent status regularly&lt;/strong&gt;: Create alerts for agents stuck in INCALL or NOT READY&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintain clean databases&lt;/strong&gt;: Run weekly cleanup scripts on old log entries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log SIP registration issues&lt;/strong&gt;: Enable detailed SIP logging for troubleshooting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test new agent setup&lt;/strong&gt;: Run through complete login and test call workflow before deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document your changes&lt;/strong&gt;: Keep audit trail of any manual database modifications&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Quick Reference Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Agent status check&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SELECT user, status, phone_login FROM vicidial_users WHERE user='AGENT001';"&lt;/span&gt;

&lt;span class="c"&gt;# SIP registration check&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peers"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; agent

&lt;span class="c"&gt;# Recent calls log&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SELECT call_date, user, status FROM vicidial_log WHERE user='AGENT001' AND call_date &amp;gt;= DATE_SUB(NOW(), INTERVAL 1 HOUR);"&lt;/span&gt;

&lt;span class="c"&gt;# Restart ViciDial&lt;/span&gt;
/etc/init.d/asterisk restart

&lt;span class="c"&gt;# Check critical processes&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;astguiclient | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this comprehensive approach to diagnosing "Agent Not Available" errors, you now have both the theoretical understanding and practical commands to resolve these issues quickly in production environments.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
    <item>
      <title>ViciDial DEAD Call Status — Why Agents Get Dead Air &amp; How to Fix It</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Sun, 26 Jul 2026 03:30:03 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/vicidial-dead-call-status-why-agents-get-dead-air-how-to-fix-it-12nh</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/vicidial-dead-call-status-why-agents-get-dead-air-how-to-fix-it-12nh</guid>
      <description>&lt;h1&gt;
  
  
  ViciDial DEAD Call Status — Why Agents Get Dead Air &amp;amp; How to Fix It
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Master the root causes of dead calls in ViciDial, diagnose them using logs and database queries, and implement proven fixes to eliminate dead air and improve call quality.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before diving into this tutorial, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A functioning ViciDial installation (v2.12 or later recommended)&lt;/li&gt;
&lt;li&gt;SSH/root access to your ViciDial server&lt;/li&gt;
&lt;li&gt;Basic familiarity with Asterisk CLI commands and SIP protocol&lt;/li&gt;
&lt;li&gt;Access to MySQL/MariaDB database with &lt;code&gt;asterisk&lt;/code&gt; database selected&lt;/li&gt;
&lt;li&gt;Knowledge of ViciDial call flow basics (inbound/outbound, IVR, transfer)&lt;/li&gt;
&lt;li&gt;Ability to read Asterisk log files and query ViciDial tables&lt;/li&gt;
&lt;li&gt;A test phone line or internal extension for testing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding DEAD Call Status in ViciDial
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is a DEAD Call?
&lt;/h3&gt;

&lt;p&gt;A DEAD call status in ViciDial occurs when an agent answers the phone expecting to speak with a caller, but hears only silence—dead air. The call appears answered in the system, but no media (audio) is flowing. The agent may wait 10-30 seconds or longer before realizing the line is dead.&lt;/p&gt;

&lt;p&gt;DEAD calls are frustrating because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agents waste time on unproductive calls&lt;/li&gt;
&lt;li&gt;Call metrics are skewed (false answers affecting answer rate)&lt;/li&gt;
&lt;li&gt;Customer experience degrades if it's an inbound scenario&lt;/li&gt;
&lt;li&gt;Compliance issues arise (wasted call minutes, dropped conversations)&lt;/li&gt;
&lt;li&gt;Troubleshooting becomes difficult without clear error indicators&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where DEAD Calls Appear in ViciDial
&lt;/h3&gt;

&lt;p&gt;Check your ViciDial database for DEAD status records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vicidial_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pause_sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;talk_sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dispo&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;call_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DEAD'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;DAY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;call_status&lt;/code&gt; field records DEAD. Related fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt; — agent disposition (often blank for DEAD calls)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;talk_sec&lt;/code&gt; — usually very low (2-5 seconds)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pause_sec&lt;/code&gt; — time agent held the line waiting&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vicidial_id&lt;/code&gt; — unique call ID for cross-referencing logs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Root Causes of DEAD Calls
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. RTP (Audio Stream) Connection Failures
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom&lt;/strong&gt;: Call connects (SIP 200 OK received), but no audio flows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: RTP packets from carrier or endpoint aren't reaching ViciDial's RTP port range (typically 10000-20000).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Triggers&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Firewall blocking RTP ports&lt;/li&gt;
&lt;li&gt;NAT misconfiguration between carrier and ViciDial&lt;/li&gt;
&lt;li&gt;SIP and RTP on different source IPs&lt;/li&gt;
&lt;li&gt;Carrier sending RTP to wrong destination IP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Check Current RTP Config&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"^rtp"&lt;/span&gt; /etc/asterisk/asterisk.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;rtp&lt;/span&gt;]
&lt;span class="n"&gt;rtpstart&lt;/span&gt;=&lt;span class="m"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;rtpend&lt;/span&gt;=&lt;span class="m"&gt;20000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Early Media Handling Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom&lt;/strong&gt;: Call shows as answered, but agent hears ringing, IVR prompt, or silence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: Early media (180 Ringing, 183 Session Progress) is sent before final 200 OK. If ViciDial doesn't properly bridge early media to the agent, dead air results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check SIP Trunk Config&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 5 &lt;span class="s2"&gt;"context="&lt;/span&gt; /etc/asterisk/sip-vicidial.conf | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for missing &lt;code&gt;progressinband&lt;/code&gt; or incorrect &lt;code&gt;t38pt_udptl&lt;/code&gt; settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. DTMF Tone Negotiation Problems
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom&lt;/strong&gt;: Call connects but DTMF (IVR key presses) fail, freezing call flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: SIP and RTP codec mismatch, or DTMF mode misconfiguration (RFC 2833 vs. Inband vs. INFO).&lt;/p&gt;

&lt;h3&gt;
  
  
  4. One-Way Audio
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom&lt;/strong&gt;: Agent can hear caller, but caller hears nothing (or vice versa).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: Asymmetric RTP routing—media path from caller to agent differs from agent to caller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Cause&lt;/strong&gt;: SIP proxy sending RTP invite to wrong IP; agent NIC not matching advertised SIP IP.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Carrier SIP Gateway Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom&lt;/strong&gt;: DEAD calls spike after carrier deployment or config change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Carrier not properly handling call state&lt;/li&gt;
&lt;li&gt;Premature call termination (180 Ringing → BYE without 200 OK)&lt;/li&gt;
&lt;li&gt;Carrier stripping or mangling SIP headers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. CID Timeout or Caller ID Delivery Delay
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom&lt;/strong&gt;: Call answers but agent screen doesn't populate; agent gets dead air.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: ViciDial waiting for database lookup (caller name, account info) that times out before bridging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check vicidial_campaign config&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;carrier_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lead_filter_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;cid_filter_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait_for_callid_lookup&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'TEST_CAMP'&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;wait_for_callid_lookup = 'Y'&lt;/code&gt;, increase timeout or disable for high-volume campaigns.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Agent Channel Hangup (Premature Disconnect)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom&lt;/strong&gt;: Call appears answered but agent channel drops immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;: Agent extension misconfigured, channel type wrong, or agent phone can't sustain SIP registration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check Agent Phone Status&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peers"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for agents with &lt;code&gt;UNREG&lt;/code&gt; (unregistered) or high response time (&amp;gt;500ms).&lt;/p&gt;

&lt;h2&gt;
  
  
  Diagnostic Tools and Log Analysis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Enable ViciDial Call Detail Logging
&lt;/h3&gt;

&lt;p&gt;Ensure verbose logging is enabled in &lt;code&gt;/etc/asterisk/logger.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;dateformat&lt;/span&gt;=%&lt;span class="n"&gt;F&lt;/span&gt; %&lt;span class="n"&gt;T&lt;/span&gt;

[&lt;span class="n"&gt;logfiles&lt;/span&gt;]
&lt;span class="n"&gt;messages&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;notice&lt;/span&gt;,&lt;span class="n"&gt;warning&lt;/span&gt;,&lt;span class="n"&gt;error&lt;/span&gt;,&lt;span class="n"&gt;verbose&lt;/span&gt;
&lt;span class="n"&gt;full&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;notice&lt;/span&gt;,&lt;span class="n"&gt;warning&lt;/span&gt;,&lt;span class="n"&gt;error&lt;/span&gt;,&lt;span class="n"&gt;verbose&lt;/span&gt;,&lt;span class="n"&gt;debug&lt;/span&gt;,&lt;span class="n"&gt;dtmf&lt;/span&gt;
&lt;span class="n"&gt;verbose_file&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;verbose&lt;/span&gt;(&lt;span class="m"&gt;5&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Asterisk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl restart asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Capture Real-Time Call Debug
&lt;/h3&gt;

&lt;p&gt;When a DEAD call occurs, immediately grab Asterisk debug:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug on"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core set verbose 3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reproduce the issue, then capture logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1000 /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 20 &lt;span class="s2"&gt;"DEAD&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;BYE&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;CANCEL"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/dead_call_debug.txt
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 500 /var/log/asterisk/full &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /tmp/dead_call_debug.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Query ViciDial Log for Call Chain
&lt;/h3&gt;

&lt;p&gt;Use the &lt;code&gt;vicidial_id&lt;/code&gt; from the DEAD call to trace the entire call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;talk_sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;pause_sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vicidial_id&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;vicidial_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1234567890-1234567890'&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also check &lt;code&gt;vicidial_closer_log&lt;/code&gt; (for transferred calls):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;closer_sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;call_status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_closer_log&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;vicidial_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1234567890-1234567890'&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Monitor Active Channels During Call
&lt;/h3&gt;

&lt;p&gt;While call is in progress, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show channels"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for entries like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Channel              State   Type    Sec  Lag
SIP/carrier-00001234 Up      SIP        3  0
Local/user123@vicid  Up      Local      3  0
SIP/user123-00001235 Up      SIP        3  0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If one channel shows &lt;code&gt;Down&lt;/code&gt; or is missing, RTP negotiation failed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Check RTP Stream Status
&lt;/h3&gt;

&lt;p&gt;ViciDial doesn't natively show RTP stats, but Asterisk does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtp show stats"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RTP Stats
SSRC     Sent(pkts/bytes)  Recv(pkts/bytes)  Lost(%)  RTT(ms)
0x12345678 150/2400          145/2320          3.3     45
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;High loss (&amp;gt;5%) or missing recv packets = RTP path problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Examine SIP Trace via PCAP
&lt;/h3&gt;

&lt;p&gt;Capture raw SIP/RTP packets for deep analysis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; any &lt;span class="nt"&gt;-s&lt;/span&gt; 0 &lt;span class="nt"&gt;-w&lt;/span&gt; /tmp/dead_call.pcap &lt;span class="s1"&gt;'sip or rtp'&lt;/span&gt; &amp;amp;
&lt;span class="nb"&gt;sleep &lt;/span&gt;30 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pkill tcpdump
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Analyze with Wireshark (on your workstation):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp root@vicidial-server:/tmp/dead_call.pcap ~/Desktop/
&lt;span class="c"&gt;# Open in Wireshark, filter: sip or rtp&lt;/span&gt;
&lt;span class="c"&gt;# Look for: SIP 200 OK followed by no RTP packets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fixing DEAD Calls — Configuration Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Fix 1: Verify and Secure RTP Port Range
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Firewall or carrier blocking RTP traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check firewall rules:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ufw status numbered | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; rtp
&lt;span class="c"&gt;# Or for iptables:&lt;/span&gt;
iptables &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"10000|20000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Open RTP ports (if using ufw):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ufw allow 10000:20000/udp comment &lt;span class="s2"&gt;"Asterisk RTP"&lt;/span&gt;
ufw allow 10000:20000/tcp comment &lt;span class="s2"&gt;"Asterisk RTP"&lt;/span&gt;
ufw reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Or with iptables:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iptables &lt;span class="nt"&gt;-I&lt;/span&gt; INPUT 1 &lt;span class="nt"&gt;-p&lt;/span&gt; udp &lt;span class="nt"&gt;--dport&lt;/span&gt; 10000:20000 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
iptables &lt;span class="nt"&gt;-I&lt;/span&gt; INPUT 1 &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 10000:20000 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
iptables-save &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/iptables/rules.v4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Increase RTP pool if handling 500+ concurrent calls:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/asterisk.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;rtp&lt;/span&gt;]
&lt;span class="n"&gt;rtpstart&lt;/span&gt;=&lt;span class="m"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;rtpend&lt;/span&gt;=&lt;span class="m"&gt;30000&lt;/span&gt;
&lt;span class="n"&gt;rtpbindaddr&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Asterisk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl restart asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix 2: Configure SIP Trunk for Early Media
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Early media (183 Session Progress) not bridged to agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/sip-vicidial.conf&lt;/code&gt; for your carrier trunk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;carrier_trunk_name&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;peer&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="n"&gt;carrier&lt;/span&gt;.&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt;=&lt;span class="m"&gt;5060&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;carrier&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;,&lt;span class="n"&gt;alaw&lt;/span&gt;
&lt;span class="n"&gt;dtmfmode&lt;/span&gt;=&lt;span class="n"&gt;rfc2833&lt;/span&gt;
&lt;span class="n"&gt;progressinband&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;tos_sip&lt;/span&gt;=&lt;span class="n"&gt;af31&lt;/span&gt;
&lt;span class="n"&gt;tos_audio&lt;/span&gt;=&lt;span class="n"&gt;ef&lt;/span&gt;
&lt;span class="n"&gt;videosupport&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;g726nonstandard&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;t38pt_udptl&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;force_rport&lt;/span&gt;,&lt;span class="n"&gt;comedia&lt;/span&gt;
&lt;span class="n"&gt;directmedia&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Parameters&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;progressinband=yes&lt;/code&gt; — routes 183 Session Progress audio to agent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dtmfmode=rfc2833&lt;/code&gt; — use RFC 2833 for DTMF (most reliable)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nat=force_rport,comedia&lt;/code&gt; — handles NAT correctly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;directmedia=no&lt;/code&gt; — keeps ViciDial in media path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reload SIP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix 3: Check and Fix One-Way Audio
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Asymmetric RTP path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify ViciDial server's advertised SIP IP matches RTP source IP:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ifconfig | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 1 &lt;span class="s2"&gt;"inet "&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check SIP bind address in &lt;code&gt;/etc/asterisk/sip-vicidial.conf&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;bindaddr&lt;/span&gt;=&lt;span class="m"&gt;10&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="m"&gt;50&lt;/span&gt;
&lt;span class="n"&gt;bindport&lt;/span&gt;=&lt;span class="m"&gt;5060&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Must match your actual NIC IP. Do &lt;strong&gt;not&lt;/strong&gt; use 0.0.0.0 in production.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;For carrier SIP trunks, ensure &lt;code&gt;externip&lt;/code&gt; or &lt;code&gt;localnet&lt;/code&gt; is set correctly:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;externip&lt;/span&gt;=&lt;span class="m"&gt;203&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;113&lt;/span&gt;.&lt;span class="m"&gt;50&lt;/span&gt;
&lt;span class="n"&gt;localnet&lt;/span&gt;=&lt;span class="m"&gt;10&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;/&lt;span class="m"&gt;255&lt;/span&gt;.&lt;span class="m"&gt;255&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Asterisk to use &lt;code&gt;externip&lt;/code&gt; for external addresses and keep &lt;code&gt;localnet&lt;/code&gt; addresses as-is.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Disable directmedia to force RTP through ViciDial:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;carrier_trunk&lt;/span&gt;]
&lt;span class="n"&gt;directmedia&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;canreinvite&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Asterisk and test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix 4: Increase SIP Transaction Timeout
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Slow carrier or network causes call to timeout before answering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/sip-vicidial.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;sip_invite_timeout&lt;/span&gt;=&lt;span class="m"&gt;30&lt;/span&gt;
&lt;span class="n"&gt;sip_t1&lt;/span&gt;=&lt;span class="m"&gt;500&lt;/span&gt;
&lt;span class="n"&gt;sip_t1min&lt;/span&gt;=&lt;span class="m"&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Values (in milliseconds):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sip_invite_timeout=30&lt;/code&gt; — max 30 seconds to wait for final response&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sip_t1=500&lt;/code&gt; — retransmit interval (default 500ms)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sip_t1min=100&lt;/code&gt; — minimum retransmit interval&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix 5: Optimize Agent Phone Registration
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Agent SIP phones dropping registration or experiencing jitter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/sip-vicidial.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;agent_default&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;friend&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;dtmfmode&lt;/span&gt;=&lt;span class="n"&gt;rfc2833&lt;/span&gt;
&lt;span class="n"&gt;qualify&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;qualifyfreq&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;
&lt;span class="n"&gt;timert1&lt;/span&gt;=&lt;span class="m"&gt;500&lt;/span&gt;
&lt;span class="n"&gt;registertimeout&lt;/span&gt;=&lt;span class="m"&gt;120&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;-&lt;span class="n"&gt;expires&lt;/span&gt;=&lt;span class="m"&gt;3600&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;-&lt;span class="n"&gt;minse&lt;/span&gt;=&lt;span class="m"&gt;900&lt;/span&gt;
&lt;span class="n"&gt;sipdebug&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;qualifyfreq=60&lt;/code&gt; — check if phone is alive every 60 seconds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;registertimeout=120&lt;/code&gt; — allow 120-second registration window&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;session-expires=3600&lt;/code&gt; — keep session alive for 1 hour&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;session-minse=900&lt;/code&gt; — minimum session duration 15 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Restart Asterisk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl restart asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Monitor agent registration status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peers"&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix 6: Disable ViciDial Caller ID Lookup Timeout
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Database lookup delays cause dead air while agent waits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;/usr/share/astguiclient/&lt;/code&gt; edit the appropriate outbound script. For example, in Vicidial Manager:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Admin&lt;/strong&gt; &amp;gt; &lt;strong&gt;Campaigns&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select your campaign&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;CID Lookup Delay&lt;/strong&gt; to 0 seconds&lt;/li&gt;
&lt;li&gt;Or via SQL:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;wait_for_callid_lookup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'N'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'TEST_CAMP'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need caller lookups, increase timeout in &lt;code&gt;/etc/astguiclient/astguiclient.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;cid_lookup_timeout&lt;/span&gt;=&lt;span class="m"&gt;5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Value in milliseconds. Default is often too low (1000-2000ms).&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix 7: Fix Carrier-Specific DEAD Call Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Specific carrier causing all DEAD calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Test the carrier trunk directly. Replace &lt;code&gt;CARRIER_CONTEXT&lt;/code&gt; with your carrier context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip debug peer carrier_trunk_name"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dial a test call and watch for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SIP 183 vs. 180 vs. 200 — which does carrier send?&lt;/li&gt;
&lt;li&gt;RTP audio — does it start before or after 200 OK?&lt;/li&gt;
&lt;li&gt;Abnormal headers — is carrier stripping required headers?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Common carrier issues and fixes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Carrier Behavior&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DEAD after 5 sec&lt;/td&gt;
&lt;td&gt;Sending BYE without proper disconnect&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;sendrpid=yes&lt;/code&gt; in trunk config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEAD on every call&lt;/td&gt;
&lt;td&gt;Sending 183 but no RTP&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;progressinband=yes&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-way audio&lt;/td&gt;
&lt;td&gt;Expecting direct RTP&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;directmedia=no&lt;/code&gt; and &lt;code&gt;nat=force_rport,comedia&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sporadic DEAD&lt;/td&gt;
&lt;td&gt;RTP port saturation&lt;/td&gt;
&lt;td&gt;Increase rtpstart/rtpend range&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  System-Level Fixes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Increase File Descriptors
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Too many open connections causing RTP stream loss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/security/limits.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;asterisk&lt;/span&gt; &lt;span class="n"&gt;soft&lt;/span&gt; &lt;span class="n"&gt;nofile&lt;/span&gt; &lt;span class="m"&gt;65536&lt;/span&gt;
&lt;span class="n"&gt;asterisk&lt;/span&gt; &lt;span class="n"&gt;hard&lt;/span&gt; &lt;span class="n"&gt;nofile&lt;/span&gt; &lt;span class="m"&gt;65536&lt;/span&gt;
&lt;span class="n"&gt;asterisk&lt;/span&gt; &lt;span class="n"&gt;soft&lt;/span&gt; &lt;span class="n"&gt;memlock&lt;/span&gt; &lt;span class="n"&gt;unlimited&lt;/span&gt;
&lt;span class="n"&gt;asterisk&lt;/span&gt; &lt;span class="n"&gt;hard&lt;/span&gt; &lt;span class="n"&gt;memlock&lt;/span&gt; &lt;span class="n"&gt;unlimited&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify after restart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; asterisk bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'ulimit -n'&lt;/span&gt;
&lt;span class="c"&gt;# Should output: 65536&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Optimize Kernel Network Parameters
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/sysctl.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# Increase socket buffer sizes
&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;.&lt;span class="n"&gt;core&lt;/span&gt;.&lt;span class="n"&gt;rmem_max&lt;/span&gt;=&lt;span class="m"&gt;134217728&lt;/span&gt;
&lt;span class="n"&gt;net&lt;/span&gt;.&lt;span class="n"&gt;core&lt;/span&gt;.&lt;span class="n"&gt;wmem_max&lt;/span&gt;=&lt;span class="m"&gt;134217728&lt;/span&gt;
&lt;span class="n"&gt;net&lt;/span&gt;.&lt;span class="n"&gt;ipv4&lt;/span&gt;.&lt;span class="n"&gt;tcp_rmem&lt;/span&gt;=&lt;span class="m"&gt;4096&lt;/span&gt; &lt;span class="m"&gt;87380&lt;/span&gt; &lt;span class="m"&gt;67108864&lt;/span&gt;
&lt;span class="n"&gt;net&lt;/span&gt;.&lt;span class="n"&gt;ipv4&lt;/span&gt;.&lt;span class="n"&gt;tcp_wmem&lt;/span&gt;=&lt;span class="m"&gt;4096&lt;/span&gt; &lt;span class="m"&gt;65536&lt;/span&gt; &lt;span class="m"&gt;67108864&lt;/span&gt;

&lt;span class="c"&gt;# Increase connection backlog
&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;.&lt;span class="n"&gt;core&lt;/span&gt;.&lt;span class="n"&gt;somaxconn&lt;/span&gt;=&lt;span class="m"&gt;4096&lt;/span&gt;
&lt;span class="n"&gt;net&lt;/span&gt;.&lt;span class="n"&gt;ipv4&lt;/span&gt;.&lt;span class="n"&gt;tcp_max_syn_backlog&lt;/span&gt;=&lt;span class="m"&gt;4096&lt;/span&gt;

&lt;span class="c"&gt;# Enable SYN cookies to prevent SYN floods
&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;.&lt;span class="n"&gt;ipv4&lt;/span&gt;.&lt;span class="n"&gt;tcp_syncookies&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="c"&gt;# Increase UDP buffer sizes
&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;.&lt;span class="n"&gt;core&lt;/span&gt;.&lt;span class="n"&gt;rmem_max&lt;/span&gt;=&lt;span class="m"&gt;134217728&lt;/span&gt;
&lt;span class="n"&gt;net&lt;/span&gt;.&lt;span class="n"&gt;core&lt;/span&gt;.&lt;span class="n"&gt;rmem_default&lt;/span&gt;=&lt;span class="m"&gt;134217728&lt;/span&gt;
&lt;span class="n"&gt;net&lt;/span&gt;.&lt;span class="n"&gt;core&lt;/span&gt;.&lt;span class="n"&gt;wmem_max&lt;/span&gt;=&lt;span class="m"&gt;134217728&lt;/span&gt;
&lt;span class="n"&gt;net&lt;/span&gt;.&lt;span class="n"&gt;core&lt;/span&gt;.&lt;span class="n"&gt;wmem_default&lt;/span&gt;=&lt;span class="m"&gt;134217728&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sysctl &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitor Call Quality in Real-Time
&lt;/h3&gt;

&lt;p&gt;Create a monitoring script at &lt;code&gt;/usr/local/bin/monitor_dead_calls.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5
&lt;span class="nv"&gt;INTERVAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60

&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nv"&gt;DEAD_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt;$DB_PASS&lt;/span&gt; asterisk &lt;span class="nt"&gt;-se&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="s2"&gt;"SELECT COUNT(*) FROM vicidial_log &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
         WHERE call_status = 'DEAD' &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
         AND call_date &amp;gt; DATE_SUB(NOW(), INTERVAL 1 MINUTE);"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEAD_COUNT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$THRESHOLD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ALERT: &lt;/span&gt;&lt;span class="nv"&gt;$DEAD_COUNT&lt;/span&gt;&lt;span class="s2"&gt; dead calls in last minute"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
            mail &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"ViciDial DEAD Call Alert"&lt;/span&gt; admin@example.com

        &lt;span class="c"&gt;# Capture diagnostics&lt;/span&gt;
        asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show channels"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /tmp/dead_alert_&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;.txt
        asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peers"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /tmp/dead_alert_&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;.txt
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="nv"&gt;$INTERVAL&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make executable and run in background:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/monitor_dead_calls.sh
/usr/local/bin/monitor_dead_calls.sh &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Symptom: All Inbound Calls are DEAD
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;call_status&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;call_status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Checks&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is carrier sending any audio?
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtp show stats"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If no stats, RTP isn't flowing.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is IVR running?
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show channels verbose"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; ivr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Should show IVR context, not dead channels.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check inbound context in &lt;code&gt;/etc/asterisk/extensions-vicidial.conf&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 10 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="s2"&gt;from-carrier&lt;/span&gt;&lt;span class="se"&gt;\]&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Verify &lt;code&gt;context&lt;/code&gt; in carrier SIP trunk matches actual Asterisk context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Symptom: DEAD Calls Only on Weekdays 9-5
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis&lt;/strong&gt;: Pattern suggests call volume issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check Channel Saturation&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show channels"&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare to your max concurrent calls setting. If near limit, increase:&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;/etc/asterisk/asterisk.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;rtp&lt;/span&gt;]
&lt;span class="n"&gt;rtpstart&lt;/span&gt;=&lt;span class="m"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;rtpend&lt;/span&gt;=&lt;span class="m"&gt;40000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in &lt;code&gt;/etc/asterisk/sip-vicidial.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;maxforwards&lt;/span&gt;=&lt;span class="m"&gt;70&lt;/span&gt;
&lt;span class="n"&gt;channel_limit&lt;/span&gt;=&lt;span class="m"&gt;1000&lt;/span&gt;,&lt;span class="n"&gt;warn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Symptom: DEAD Calls from Specific Carrier IP
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis&lt;/strong&gt;: Carrier SIP trunk issue, not global.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check if carrier has NAT:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 10 &lt;span class="s2"&gt;"host=CARRIER_IP"&lt;/span&gt; /etc/asterisk/sip-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Force RTP through ViciDial for this trunk:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;   [&lt;span class="n"&gt;carrier_with_nat&lt;/span&gt;]
   &lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="m"&gt;203&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;113&lt;/span&gt;.&lt;span class="m"&gt;100&lt;/span&gt;
   &lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;force_rport&lt;/span&gt;,&lt;span class="n"&gt;comedia&lt;/span&gt;
   &lt;span class="n"&gt;directmedia&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Restart Asterisk and test calls from that carrier only.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Symptom: Random DEAD Calls (Not Reproducible)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis&lt;/strong&gt;: Likely RTP packet loss or temporary network issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check Network Stats&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;watch &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="s2"&gt;"cat /proc/net/udp | grep -E '10000|20000' | wc -l"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Monitor if UDP ports are getting congested.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check for packet loss:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ping &lt;span class="nt"&gt;-c&lt;/span&gt; 100 carrier.example.com | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; packet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;If loss &amp;gt;1%, contact carrier or ISP.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Increase RTP jitter buffer in &lt;code&gt;/etc/asterisk/asterisk.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;   [&lt;span class="n"&gt;rtp&lt;/span&gt;]
   &lt;span class="n"&gt;rtpstart&lt;/span&gt;=&lt;span class="m"&gt;10000&lt;/span&gt;
   &lt;span class="n"&gt;rtpend&lt;/span&gt;=&lt;span class="m"&gt;20000&lt;/span&gt;
   &lt;span class="n"&gt;rtptimeout&lt;/span&gt;=&lt;span class="m"&gt;3600&lt;/span&gt;
   &lt;span class="n"&gt;rtpholdtimeout&lt;/span&gt;=&lt;span class="m"&gt;3600&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Symptom: DEAD Calls Only After Call Transfer
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis&lt;/strong&gt;: Bridge or transfer context issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check Transfer Extension&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-B&lt;/span&gt; 5 &lt;span class="nt"&gt;-A&lt;/span&gt; 10 &lt;span class="s2"&gt;"transfer&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;bridge"&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure transfer context properly bridges RTP. If using &lt;code&gt;Dial()&lt;/code&gt;, verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Dial&lt;/span&gt;(&lt;span class="n"&gt;SIP&lt;/span&gt;/${&lt;span class="n"&gt;EXTEN&lt;/span&gt;}@&lt;span class="n"&gt;agent&lt;/span&gt;-&lt;span class="n"&gt;pool&lt;/span&gt;,,&lt;span class="n"&gt;g&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;g&lt;/code&gt; flag allows graceful hangup. Missing flags can cause dead air.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;DEAD calls in ViciDial stem from misconfigurations across SIP trunks, RTP routing, firewall rules, and database settings. Here's your action plan:&lt;/p&gt;

&lt;h3&gt;
  
  
  Immediate Steps:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify the scope&lt;/strong&gt;: Is it all calls, one carrier, or one campaign?
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;carrier_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;dead_count&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
   &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;call_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DEAD'&lt;/span&gt; 
   &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
   &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;carrier_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enable SIP debugging&lt;/strong&gt; and capture a failed call.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verify RTP flows&lt;/strong&gt;: Check &lt;code&gt;asterisk -rx "rtp show stats"&lt;/code&gt; during calls.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check firewall&lt;/strong&gt;: Ensure ports 10000-20000 UDP are open.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Medium-Term Fixes:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Review carrier trunk config&lt;/strong&gt;: Ensure &lt;code&gt;progressinband=yes&lt;/code&gt;, &lt;code&gt;nat=force_rport,comedia&lt;/code&gt;, &lt;code&gt;directmedia=no&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimize SIP timeouts&lt;/strong&gt;: Increase &lt;code&gt;sip_invite_timeout&lt;/code&gt; if carrier is slow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disable CID lookup timeouts&lt;/strong&gt; for high-volume campaigns.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Long-Term Optimization:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor proactively&lt;/strong&gt;: Use the monitoring script to catch DEAD call spikes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Load test&lt;/strong&gt;: Simulate peak call volume to find channel/RTP saturation limits.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Document carrier behaviors&lt;/strong&gt;: Keep detailed notes on each carrier's SIP/RTP quirks for future troubleshooting.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With these fixes applied systematically, DEAD call rates should drop from 5-10% to &amp;lt;0.1% in production. Test each change individually and measure impact before moving to the next.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
    <item>
      <title>ViciDial carrier_log No Answer — Diagnosing Failed Outbound Calls</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Sat, 25 Jul 2026 03:30:02 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/vicidial-carrierlog-no-answer-diagnosing-failed-outbound-calls-jo6</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/vicidial-carrierlog-no-answer-diagnosing-failed-outbound-calls-jo6</guid>
      <description>&lt;h1&gt;
  
  
  ViciDial carrier_log No Answer — Diagnosing Failed Outbound Calls
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Master the art of troubleshooting "No Answer" call failures in ViciDial by reading carrier_log entries, analyzing Asterisk SIP traces, and identifying root causes in your telephony infrastructure&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before diving into carrier_log diagnostics, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A functioning ViciDial installation (v2.12+) with Asterisk 13+ or PJSIP&lt;/li&gt;
&lt;li&gt;SSH/command-line access to your ViciDial server&lt;/li&gt;
&lt;li&gt;MySQL client tools to query the asterisk database&lt;/li&gt;
&lt;li&gt;Familiarity with basic Asterisk and SIP concepts&lt;/li&gt;
&lt;li&gt;Access to &lt;code&gt;/var/log/asterisk/&lt;/code&gt; logs with read permissions&lt;/li&gt;
&lt;li&gt;Knowledge of your carrier's SIP trunk configuration&lt;/li&gt;
&lt;li&gt;Understanding of ViciDial call flow (Vicidial → Asterisk → Carrier → Destination)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding carrier_log and No Answer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is carrier_log?
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;vicidial_carrier_log&lt;/code&gt; table is a critical diagnostic table in ViciDial that records every interaction between your Asterisk system and your telephony carrier. Unlike &lt;code&gt;vicidial_log&lt;/code&gt; (which tracks agent calls), &lt;code&gt;carrier_log&lt;/code&gt; captures the raw SIP signaling outcomes, timeouts, and provider responses.&lt;/p&gt;

&lt;p&gt;When a call shows "No Answer" in carrier_log, it means one of these scenarios occurred:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The destination phone never rang (pre-answer failure)&lt;/li&gt;
&lt;li&gt;The destination phone rang but nobody picked up within the timeout window&lt;/li&gt;
&lt;li&gt;The carrier rejected the call before it reached the destination&lt;/li&gt;
&lt;li&gt;A SIP timeout occurred during the call attempt&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Key carrier_log Fields
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_carrier_log&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;carrier_log_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Critical fields to examine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;carrier_log_id&lt;/strong&gt;: Unique identifier&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;call_date&lt;/strong&gt;: Timestamp of the call attempt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;carrier_id&lt;/strong&gt;: Which carrier this call used&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;phone_number&lt;/strong&gt;: Dialed destination&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sip_code&lt;/strong&gt;: The SIP response code (100, 180, 183, 200, 486, 487, 408, 503, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sip_hangup_cause&lt;/strong&gt;: Asterisk hangup cause string&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;call_duration&lt;/strong&gt;: How long the call lasted in seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hangup_source&lt;/strong&gt;: Where the disconnect originated (asterisk, carrier, bridge, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture: Call Flow and Where "No Answer" Happens
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent dials → ViciDial engine → Asterisk dialplan
    ↓
Asterisk creates SIP INVITE → Carrier SIP gateway
    ↓
Carrier routes call → Destination carrier
    ↓
Destination phone receives INVITE → Phone rings
    ↓
200 OK received → Bridge established
    ↓
Call connected / Hangup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"No Answer" failures typically occur at these points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pre-dial&lt;/strong&gt;: Carrier rejects INVITE (4xx/5xx before 100 Trying)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ringing&lt;/strong&gt;: Phone rings (180 Ringing received) but caller hangs up or timeout&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout&lt;/strong&gt;: Call exceeds configured timeout without answer (408)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silent failure&lt;/strong&gt;: INVITE lost, no response received&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1: Locate and Query carrier_log Entries
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Find the relevant carrier_log record
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# SSH into ViciDial server&lt;/span&gt;
ssh root@vicidial.example.com

&lt;span class="c"&gt;# Query the database for recent "No Answer" calls&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
SELECT 
    carrier_log_id,
    call_date,
    phone_number,
    sip_code,
    sip_hangup_cause,
    call_duration,
    hangup_source,
    carrier_id
FROM vicidial_carrier_log
WHERE sip_hangup_cause LIKE 'NO_ANSWER%'
    AND call_date &amp;gt;= DATE_SUB(NOW(), INTERVAL 2 HOUR)
ORDER BY call_date DESC
LIMIT 20;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Examine detailed carrier_log entry
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
SELECT 
    carrier_log_id,
    call_date,
    phone_number,
    session_id,
    sip_code,
    sip_hangup_cause,
    call_duration,
    hangup_source,
    carrier_id,
    tried_count,
    user_only
FROM vicidial_carrier_log
WHERE carrier_log_id = 567890&lt;/span&gt;&lt;span class="se"&gt;\G&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;***************************&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="o"&gt;***************************&lt;/span&gt;
        &lt;span class="n"&gt;carrier_log_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;567890&lt;/span&gt;
            &lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;
           &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15551234567&lt;/span&gt;
            &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1705338225&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4892&lt;/span&gt;
              &lt;span class="n"&gt;sip_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;
      &lt;span class="n"&gt;sip_hangup_cause&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NO_ANSWER&lt;/span&gt;
        &lt;span class="n"&gt;call_duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
         &lt;span class="n"&gt;hangup_source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;asterisk&lt;/span&gt;
            &lt;span class="n"&gt;carrier_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
          &lt;span class="n"&gt;tried_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
           &lt;span class="n"&gt;user_only&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Correlate with Asterisk Full Logs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Enable verbose SIP logging
&lt;/h3&gt;

&lt;p&gt;First, ensure Asterisk SIP debugging is configured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/asterisk/logger.conf
&lt;/span&gt;[&lt;span class="n"&gt;logfiles&lt;/span&gt;]
&lt;span class="n"&gt;messages&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;verbose&lt;/span&gt;,&lt;span class="n"&gt;debug&lt;/span&gt;,&lt;span class="n"&gt;notice&lt;/span&gt;,&lt;span class="n"&gt;warning&lt;/span&gt;,&lt;span class="n"&gt;error&lt;/span&gt;
&lt;span class="n"&gt;full&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;verbose&lt;/span&gt;,&lt;span class="n"&gt;debug&lt;/span&gt;,&lt;span class="n"&gt;notice&lt;/span&gt;,&lt;span class="n"&gt;warning&lt;/span&gt;,&lt;span class="n"&gt;error&lt;/span&gt;,&lt;span class="n"&gt;dtmf&lt;/span&gt;,&lt;span class="n"&gt;fax&lt;/span&gt;
&lt;span class="n"&gt;sip&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;verbose&lt;/span&gt;,&lt;span class="n"&gt;debug&lt;/span&gt;,&lt;span class="n"&gt;notice&lt;/span&gt;,&lt;span class="n"&gt;warning&lt;/span&gt;,&lt;span class="n"&gt;error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload logger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"logger reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Capture SIP trace for the specific call
&lt;/h3&gt;

&lt;p&gt;Use the session_id from carrier_log to find related Asterisk logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Real-time SIP trace (next call)&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug on"&lt;/span&gt;

&lt;span class="c"&gt;# Make test call, then:&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug off"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Review /var/log/asterisk/messages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Search for the session_id from carrier_log&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"1705338225-4892"&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt;

&lt;span class="c"&gt;# Or search for the phone number&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"15551234567"&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example SIP trace showing "No Answer":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[2024-01-15 14:23:45] VERBOSE[31245][C-0000a2d7] app_dial.c: Called SIP/carrier3/15551234567
[2024-01-15 14:23:45] VERBOSE[31245][C-0000a2d7] netsock2.c: == Probation passed - setting RTP source address to 203.0.113.15
&lt;/span&gt;&lt;span class="gp"&gt;[2024-01-15 14:23:46] VERBOSE[31245][C-0000a2d7] pbx.c: &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Channel SIP/carrier3-0000a2d8 joined &lt;span class="s1"&gt;'simple_bridge'&lt;/span&gt; basic-bridge &amp;lt;C-0000a2d7&amp;gt;
&lt;span class="go"&gt;[2024-01-15 14:23:46] VERBOSE[31245][C-0000a2d7] app_dial.c: SIP/carrier3-0000a2d8 is ringing
[2024-01-15 14:23:52] VERBOSE[31245][C-0000a2d7] pbx.c: == Spawn extension (from-vicidial, 15551234567, 1) exited non-zero on 'SIP/carrier3-0000a2d8'
[2024-01-15 14:23:52] VERBOSE[31245][C-0000a2d7] app_dial.c: SIP/carrier3-0000a2d8 answered
[2024-01-15 14:24:07] VERBOSE[31245][C-0000a2d7] app_dial.c: Timeout on SIP/carrier3-0000a2d8, checking if we should move on
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Analyze SIP Response Codes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Common SIP codes in carrier_log
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SIP Code&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;No Answer Cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;Trying&lt;/td&gt;
&lt;td&gt;Call still in progress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;180&lt;/td&gt;
&lt;td&gt;Ringing&lt;/td&gt;
&lt;td&gt;Destination phone ringing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;183&lt;/td&gt;
&lt;td&gt;Session Progress&lt;/td&gt;
&lt;td&gt;In-band info (ringback)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;OK / Answered&lt;/td&gt;
&lt;td&gt;Call answered — NOT "No Answer"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;408&lt;/td&gt;
&lt;td&gt;Request Timeout&lt;/td&gt;
&lt;td&gt;No response within timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;486&lt;/td&gt;
&lt;td&gt;Busy Here&lt;/td&gt;
&lt;td&gt;Destination busy — NOT "No Answer"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;487&lt;/td&gt;
&lt;td&gt;Request Terminated&lt;/td&gt;
&lt;td&gt;Caller cancelled before answer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;503&lt;/td&gt;
&lt;td&gt;Service Unavailable&lt;/td&gt;
&lt;td&gt;Carrier/destination unavailable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;480&lt;/td&gt;
&lt;td&gt;Temporarily Unavailable&lt;/td&gt;
&lt;td&gt;Destination unavailable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Query for SIP code patterns
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
SELECT 
    sip_code,
    sip_hangup_cause,
    COUNT(*) as count,
    AVG(call_duration) as avg_duration
FROM vicidial_carrier_log
WHERE call_date &amp;gt;= DATE_SUB(NOW(), INTERVAL 24 HOUR)
    AND carrier_id = 3
GROUP BY sip_code, sip_hangup_cause
ORDER BY count DESC;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No Answer diagnosis:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;180 + NO_ANSWER + 22 seconds&lt;/strong&gt;: Phone rang, caller abandoned or timeout hit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;408 + NO_ANSWER&lt;/strong&gt;: Carrier didn't respond to INVITE in time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100 + NO_ANSWER + &amp;lt;2 sec&lt;/strong&gt;: Call failed before reaching destination&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Check Carrier Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Verify SIP carrier is correctly defined
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
SELECT 
    carrier_id,
    carrier_name,
    protocol,
    active,
    port,
    server_ip,
    failover_server_ip
FROM carriers
WHERE carrier_id = 3&lt;/span&gt;&lt;span class="se"&gt;\G&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Review Asterisk SIP peer configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find carrier peer definition&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 30 &lt;span class="s2"&gt;"carrier3"&lt;/span&gt; /etc/asterisk/sip-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;carrier3&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;peer&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="n"&gt;sip&lt;/span&gt;.&lt;span class="n"&gt;carrier&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt;=&lt;span class="m"&gt;5060&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;vicidial&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;,&lt;span class="n"&gt;alaw&lt;/span&gt;
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;insecure&lt;/span&gt;=&lt;span class="n"&gt;port&lt;/span&gt;,&lt;span class="n"&gt;invite&lt;/span&gt;
&lt;span class="n"&gt;qualify&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;qualifyfreq&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;
&lt;span class="n"&gt;defaultuser&lt;/span&gt;=&lt;span class="n"&gt;vicidial_account&lt;/span&gt;
&lt;span class="n"&gt;secret&lt;/span&gt;=&lt;span class="n"&gt;your_sip_password&lt;/span&gt;
&lt;span class="n"&gt;dtmfmode&lt;/span&gt;=&lt;span class="n"&gt;rfc2833&lt;/span&gt;
&lt;span class="n"&gt;progressinband&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check for common carrier config issues
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify carrier is reachable&lt;/span&gt;
ping &lt;span class="nt"&gt;-c&lt;/span&gt; 3 sip.carrier.com

&lt;span class="c"&gt;# Test SIP connectivity&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peers"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;carrier3

&lt;span class="c"&gt;# Check registration status&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show registry"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;carrier3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output — look for "Registered":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host                                                    dnsmgr Username       Reg.Expires Status
sip.carrier.com:5060                                    N      vicidial_acco 3600       Registered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Examine Dialplan Execution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Review the dialplan handling outbound calls
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 50 &lt;span class="s2"&gt;"from-vicidial"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typical outbound dialplan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;vicidial&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Outbound&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; ${&lt;span class="n"&gt;EXTEN&lt;/span&gt;})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;CHANNEL&lt;/span&gt;(&lt;span class="n"&gt;hangup_handler_push&lt;/span&gt;)=&lt;span class="n"&gt;custom&lt;/span&gt;-&lt;span class="n"&gt;handler&lt;/span&gt;,&lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Dial&lt;/span&gt;(&lt;span class="n"&gt;SIP&lt;/span&gt;/&lt;span class="n"&gt;carrier3&lt;/span&gt;/${&lt;span class="n"&gt;EXTEN&lt;/span&gt;},&lt;span class="m"&gt;30&lt;/span&gt;,&lt;span class="n"&gt;m&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Dial&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;: ${&lt;span class="n"&gt;DIALSTATUS&lt;/span&gt;})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The critical parameter is the timeout (30 seconds in above example).&lt;/p&gt;

&lt;h3&gt;
  
  
  Check actual call timeout configuration in ViciDial
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
SELECT 
    campaign_id,
    campaign_name,
    dial_timeout,
    ringback_time
FROM vicidial_campaigns
WHERE campaign_id = 'TESTCAMP'&lt;/span&gt;&lt;span class="se"&gt;\G&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Diagnose Ring Timeout vs Actual No Answer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Determine if destination is actually ringing
&lt;/h3&gt;

&lt;p&gt;Query for calls with 180 Ringing and NO_ANSWER:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
SELECT 
    carrier_log_id,
    call_date,
    phone_number,
    sip_code,
    call_duration,
    hangup_source
FROM vicidial_carrier_log
WHERE sip_code = 180
    AND sip_hangup_cause = 'NO_ANSWER'
    AND call_date &amp;gt;= DATE_SUB(NOW(), INTERVAL 24 HOUR)
    AND carrier_id = 3
LIMIT 10;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Interpretation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;180 + call_duration 3-5 sec&lt;/strong&gt;: Destination phone may not be ringing (SIP implementation issue)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;180 + call_duration 20+ sec&lt;/strong&gt;: Phone is ringing; customer simply not answering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;180 + call_duration 30 sec&lt;/strong&gt;: Timeout hit exactly at dialplan timeout&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Check ringback delay vs actual ring detection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test call to known answering service&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug on"&lt;/span&gt;

&lt;span class="c"&gt;# From CLI or dialplan, originate test call&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"channel originate SIP/carrier3/15551234567 application playback silence/1"&lt;/span&gt;

asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug off"&lt;/span&gt;

&lt;span class="c"&gt;# Review messages&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-200&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"180|183|200|Called"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Investigate Carrier-Side Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Check carrier's SIP response headers
&lt;/h3&gt;

&lt;p&gt;Enable full SIP packet capture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install tcpdump if needed&lt;/span&gt;
yum &lt;span class="nb"&gt;install &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Capture SIP traffic to/from carrier&lt;/span&gt;
tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; any &lt;span class="nt"&gt;-s&lt;/span&gt; 0 &lt;span class="nt"&gt;-w&lt;/span&gt; /tmp/sip_capture.pcap &lt;span class="s2"&gt;"host sip.carrier.com and port 5060"&lt;/span&gt;

&lt;span class="c"&gt;# Let it run during a test call, then Ctrl+C&lt;/span&gt;

&lt;span class="c"&gt;# Analyze with Wireshark or tshark&lt;/span&gt;
tshark &lt;span class="nt"&gt;-r&lt;/span&gt; /tmp/sip_capture.pcap &lt;span class="nt"&gt;-Y&lt;/span&gt; &lt;span class="s2"&gt;"sip.method==INVITE or sip.CSeq.method==INVITE"&lt;/span&gt; &lt;span class="nt"&gt;-V&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check carrier logs through API or portal
&lt;/h3&gt;

&lt;p&gt;Most carriers provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web portal CDR access&lt;/li&gt;
&lt;li&gt;API access to call records&lt;/li&gt;
&lt;li&gt;Support email for detailed call traces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Request from carrier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;INVITE received timestamp&lt;/li&gt;
&lt;li&gt;180 Ringing timestamp&lt;/li&gt;
&lt;li&gt;Whether call reached destination&lt;/li&gt;
&lt;li&gt;Destination carrier response codes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Test with known phone numbers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test with carrier's demo/testing numbers&lt;/span&gt;
&lt;span class="c"&gt;# Most carriers provide test numbers that:&lt;/span&gt;
&lt;span class="c"&gt;# - Always answer immediately (instant 200 OK)&lt;/span&gt;
&lt;span class="c"&gt;# - Simulate no-answer (ring then timeout)&lt;/span&gt;
&lt;span class="c"&gt;# - Simulate busy tone&lt;/span&gt;
&lt;span class="c"&gt;# - Simulate invalid number&lt;/span&gt;

&lt;span class="c"&gt;# Ask your carrier support for test numbers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 8: Check ViciDial Agent Timeout Settings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Review call timeout in campaign
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
SELECT 
    campaign_id,
    campaign_name,
    dial_timeout,
    ringback_time,
    answer_detect,
    amd_level
FROM vicidial_campaigns
WHERE active = 'Y'&lt;/span&gt;&lt;span class="se"&gt;\G&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check list-level overrides
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
SELECT 
    list_id,
    list_name,
    dial_timeout,
    ringback_time
FROM vicidial_lists
WHERE list_id = 101&lt;/span&gt;&lt;span class="se"&gt;\G&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Examine specific lead in vicidial_log
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
SELECT 
    uniqueid,
    lead_id,
    list_id,
    campaign_id,
    status,
    call_date,
    call_duration,
    phone_number
FROM vicidial_log
WHERE lead_id = 98765
    AND call_date &amp;gt;= DATE_SUB(NOW(), INTERVAL 24 HOUR)
ORDER BY call_date DESC&lt;/span&gt;&lt;span class="se"&gt;\G&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 9: Common Causes and Fixes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue #1: Timeout Set Too Short
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Calls consistently showing NO_ANSWER with 180 Ringing, but call_duration is exactly your dial_timeout value (e.g., always 15 seconds).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Phone is ringing, but timeout expires before destination has chance to pick up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Increase dial_timeout in campaign&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
UPDATE vicidial_campaigns 
SET dial_timeout = 45 
WHERE campaign_id = 'TESTCAMP';
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Verify change&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
SELECT campaign_id, dial_timeout FROM vicidial_campaigns WHERE campaign_id = 'TESTCAMP';
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue #2: Carrier Not Routing Calls to Destination
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; SIP code 180 or 183 received, but call_duration is very short (2-5 seconds), then NO_ANSWER.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Carrier answered with ringback but didn't complete call to actual destination.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Contact carrier support with INVITE timestamp from your logs&lt;/li&gt;
&lt;li&gt;Ask carrier to verify routing rules for your account&lt;/li&gt;
&lt;li&gt;Check if your CLI (caller ID) is being rejected by destination&lt;/li&gt;
&lt;li&gt;Test from carrier's test system&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Issue #3: No Progress Indication (Silent Failure)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; SIP code 100 (Trying), NO_ANSWER, call_duration &amp;lt;2 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Call failed at carrier before progressing toward destination.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check carrier registration status&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show registry"&lt;/span&gt;

&lt;span class="c"&gt;# Verify carrier peer is available&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peers"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;carrier3

&lt;span class="c"&gt;# Test carrier connectivity&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer carrier3"&lt;/span&gt;

&lt;span class="c"&gt;# Check ACL rules&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
SELECT * FROM carriers WHERE carrier_id = 3&lt;/span&gt;&lt;span class="se"&gt;\G&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue #4: Destination Network Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Intermittent NO_ANSWER; sometimes works, sometimes doesn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Destination carrier or phone system is experiencing packet loss or jitter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Monitor RTP packet loss&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtp show stats"&lt;/span&gt;

&lt;span class="c"&gt;# Check jitter measurements&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtcp show"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 5 &lt;span class="s2"&gt;"carrier3"&lt;/span&gt;

&lt;span class="c"&gt;# Increase RTP payload size if needed&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"maxrtpplayer"&lt;/span&gt; /etc/asterisk/rtp.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue #5: DTMF or Codec Mismatch
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Call connects, but phone may not be detecting dialed tones or audio issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Codec incompatibility or DTMF mode mismatch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify codec agreement
# /etc/asterisk/sip-vicidial.conf
&lt;/span&gt;
[&lt;span class="n"&gt;carrier3&lt;/span&gt;]
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;,&lt;span class="n"&gt;alaw&lt;/span&gt;
&lt;span class="n"&gt;dtmfmode&lt;/span&gt;=&lt;span class="n"&gt;rfc2833&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test call with codec specification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer carrier3"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"Codec|DTMF"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem: Too many "No Answer" calls suddenly appearing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check if carrier is registered:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show registry"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Query for recent changes:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
   SELECT * FROM vicidial_campaigns WHERE modified_date &amp;gt;= DATE_SUB(NOW(), INTERVAL 1 HOUR);
&lt;/span&gt;&lt;span class="no"&gt;   EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check Asterisk uptime:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show uptime"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Review carrier status in last hour:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"carrier3"&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem: Only specific carrier has "No Answer" issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compare working vs. non-working carrier:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
   SELECT carrier_id, COUNT(*) as total, 
       SUM(CASE WHEN sip_hangup_cause='NO_ANSWER' THEN 1 ELSE 0 END) as no_answer_count
   FROM vicidial_carrier_log
   WHERE call_date &amp;gt;= DATE_SUB(NOW(), INTERVAL 24 HOUR)
   GROUP BY carrier_id
   ORDER BY no_answer_count DESC;
&lt;/span&gt;&lt;span class="no"&gt;   EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Test failover:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Temporarily disable primary carrier&lt;/span&gt;
   mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
   UPDATE carriers SET active='N' WHERE carrier_id=3;
&lt;/span&gt;&lt;span class="no"&gt;   EOF

&lt;/span&gt;   &lt;span class="c"&gt;# Make test calls to see if secondary carrier works&lt;/span&gt;
   &lt;span class="c"&gt;# Then re-enable&lt;/span&gt;
   mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
   UPDATE carriers SET active='Y' WHERE carrier_id=3;
&lt;/span&gt;&lt;span class="no"&gt;   EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check carrier-specific dialplan:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"carrier3&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;Dial.*carrier3"&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem: "No Answer" but agent claims phone rang at destination
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get destination phone's call logs&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Correlate timestamps between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your carrier_log call_date&lt;/li&gt;
&lt;li&gt;Destination's received call timestamp&lt;/li&gt;
&lt;li&gt;Timezone differences&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Request detailed CDR from destination carrier&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check if CLI (caller ID) is being blocked:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
   SELECT * FROM vicidial_caller_id_log WHERE call_date &amp;gt;= DATE_SUB(NOW(), INTERVAL 24 HOUR) LIMIT 5;
&lt;/span&gt;&lt;span class="no"&gt;   EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem: Intermittent "No Answer" on specific numbers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find pattern in failed numbers:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'YOURPASSWORD'&lt;/span&gt; asterisk &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
   SELECT 
       phone_number,
       COUNT(*) as attempts,
       SUM(CASE WHEN sip_hangup_cause='NO_ANSWER' THEN 1 ELSE 0 END) as no_answers
   FROM vicidial_carrier_log
   WHERE call_date &amp;gt;= DATE_SUB(NOW(), INTERVAL 7 DAYS)
   GROUP BY phone_number
   HAVING no_answers &amp;gt; 3
   ORDER BY no_answers DESC;
&lt;/span&gt;&lt;span class="no"&gt;   EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check if specific area codes/exchanges are problematic&lt;/li&gt;
&lt;li&gt;Test with different CLI origination&lt;/li&gt;
&lt;li&gt;Contact destination carrier about those specific numbers&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Diagnosing "No Answer" issues in ViciDial's carrier_log requires a systematic approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify the carrier_log record&lt;/strong&gt; — Query vicidial_carrier_log for specific failed call&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note the SIP code&lt;/strong&gt; — Understand what the carrier responded with&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check call duration&lt;/strong&gt; — Short duration = pre-answer failure; Long = ringing timeout&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correlate with Asterisk logs&lt;/strong&gt; — Use session_id to find detailed SIP traces in /var/log/asterisk/messages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify carrier configuration&lt;/strong&gt; — Ensure SIP peer is active, registered, and accessible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with known numbers&lt;/strong&gt; — Use carrier-provided test numbers to isolate issues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adjust timeout settings&lt;/strong&gt; — Increase dial_timeout if phones legitimately need more time to ring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escalate to carrier&lt;/strong&gt; — Provide detailed CDR, timestamps, and SIP traces for investigation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key ViciDial tables for carrier_log analysis:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;vicidial_carrier_log&lt;/code&gt; — Raw call attempt records&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vicidial_log&lt;/code&gt; — Agent-facing call records (may not match carrier_log)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vicidial_campaigns&lt;/code&gt; — Campaign timeout settings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;carriers&lt;/code&gt; — Carrier configuration and status&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vicidial_lists&lt;/code&gt; — List-level overrides&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Most common root causes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Timeout set too short (45+ seconds recommended for most destinations)&lt;/li&gt;
&lt;li&gt;Carrier not routing calls (contact carrier support)&lt;/li&gt;
&lt;li&gt;Carrier registration issues (verify with &lt;code&gt;sip show registry&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Destination network problems (verify destination carrier status)&lt;/li&gt;
&lt;li&gt;CLI being rejected (check vicidial_caller_id_log)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By methodically working through these steps, you can pinpoint whether "No Answer" failures originate from your Asterisk/ViciDial configuration, your carrier's SIP implementation, or the destination phone/network — and take appropriate corrective action.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
    <item>
      <title>ViciDial 'No One Is in Your Session' Error — Causes &amp; Fixes</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Fri, 24 Jul 2026 03:30:03 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/vicidial-no-one-is-in-your-session-error-causes-fixes-3ibk</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/vicidial-no-one-is-in-your-session-error-causes-fixes-3ibk</guid>
      <description>&lt;h1&gt;
  
  
  ViciDial 'No One Is in Your Session' Error — Causes &amp;amp; Fixes
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Master the root causes of ViciDial's most frustrating connection error and implement proven solutions to keep your contact center running smoothly.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before diving into this guide, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A production or staging ViciDial installation (version 2.7+)&lt;/li&gt;
&lt;li&gt;SSH access to your ViciDial server with sudo privileges&lt;/li&gt;
&lt;li&gt;Basic understanding of ViciDial architecture (dialer, web interface, database)&lt;/li&gt;
&lt;li&gt;Familiarity with Asterisk CLI commands&lt;/li&gt;
&lt;li&gt;Access to MySQL/MariaDB for database inspection&lt;/li&gt;
&lt;li&gt;Log file access (&lt;code&gt;/var/log/asterisk/&lt;/code&gt; directory)&lt;/li&gt;
&lt;li&gt;A text editor (vi, nano, or equivalent)&lt;/li&gt;
&lt;li&gt;Understanding of SIP protocol basics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide assumes a standard CentOS/RHEL or Debian ViciDial installation with Asterisk 11.x or higher.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the "No One Is in Your Session" Error
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What the Error Means
&lt;/h3&gt;

&lt;p&gt;The "No One Is in Your Session" error appears in the ViciDial agent screen when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An agent attempts to dial a contact&lt;/li&gt;
&lt;li&gt;The dialer initiates the outbound call&lt;/li&gt;
&lt;li&gt;The call connects to the contact&lt;/li&gt;
&lt;li&gt;But no agent audio channel is present or properly bridged&lt;/li&gt;
&lt;li&gt;The contact hears silence or the agent cannot hear the contact&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is &lt;strong&gt;not&lt;/strong&gt; a call failure at the SIP level—the call technically completes, but the audio bridge between agent and contact never establishes correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why It Happens
&lt;/h3&gt;

&lt;p&gt;The error occurs due to one of these core issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Channel variable misalignment&lt;/strong&gt; — Agent and contact channels don't share required variables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asterisk channel bridge failure&lt;/strong&gt; — The Asterisk bridging application fails silently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SIP registration issues&lt;/strong&gt; — Agent extension not properly registered to Asterisk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database desynchronization&lt;/strong&gt; — &lt;code&gt;vicidial_log&lt;/code&gt; and live channel state don't match&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codec mismatch&lt;/strong&gt; — Agent and contact use incompatible audio codecs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firewall/NAT issues&lt;/strong&gt; — Media path broken between agent and PSTN&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory exhaustion&lt;/strong&gt; — Too many channels cause Asterisk to drop new channels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dialplan routing errors&lt;/strong&gt; — Extensions-vicidial.conf has incorrect context handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Causes and Diagnosis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cause 1: SIP Registration Failure
&lt;/h3&gt;

&lt;p&gt;The most common cause—agents appear logged in, but their SIP extension isn't registered to Asterisk.&lt;/p&gt;

&lt;h4&gt;
  
  
  Diagnosis
&lt;/h4&gt;

&lt;p&gt;Check Asterisk SIP peer status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peers"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for agents with state "UNREACHABLE" or showing as offline.&lt;/p&gt;

&lt;p&gt;Check specific agent extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer 6001"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output showing a problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name       : 6001
Qualify mem : 12500
Defaddr    : 203.0.113.45:5060
Forcerport : Yes
Useragent  : Zoiper 5.4.33
Status     : UNREACHABLE (18 ms)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key indicator: &lt;code&gt;UNREACHABLE&lt;/code&gt; status.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fix: Force Re-registration
&lt;/h4&gt;

&lt;p&gt;From the ViciDial web admin interface, navigate to &lt;strong&gt;Agent&lt;/strong&gt; and manually kick the agent offline, then have them log back in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Or use the command line to reload SIP configuration&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/sip-vicidial.conf&lt;/code&gt; and ensure the agent extension exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="m"&gt;6001&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;friend&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="n"&gt;dynamic&lt;/span&gt;
&lt;span class="n"&gt;secret&lt;/span&gt;=&lt;span class="n"&gt;YourSecretPassword123&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;
&lt;span class="n"&gt;dtmfmode&lt;/span&gt;=&lt;span class="n"&gt;rfc2833&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;gsm&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;g729&lt;/span&gt;
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;qualify&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;qualifyfreq&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key parameters to verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;host=dynamic&lt;/strong&gt; — Allows devices from any IP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nat=yes&lt;/strong&gt; — Enables NAT traversal (critical for remote agents)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;qualify=yes&lt;/strong&gt; — Enables keepalive packets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;context=from-internal&lt;/strong&gt; — Must be set to ViciDial's internal context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reload SIP after changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify re-registration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peers"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;6001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cause 2: Database Desynchronization
&lt;/h3&gt;

&lt;p&gt;ViciDial stores live session information in the database. If a crash or improper logout occurs, stale data blocks new sessions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Diagnosis
&lt;/h4&gt;

&lt;p&gt;Query the database for orphaned sessions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;asterisk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;vicidial_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
       &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INCALL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'QUEUE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'LIVE'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
       &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query finds calls marked as "LIVE" older than 2 hours—these are definitely stale.&lt;/p&gt;

&lt;p&gt;Check the agent's current session status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_update&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users_log&lt;/span&gt; 
       &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agent001'&lt;/span&gt; 
       &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;last_update&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the timestamp is old but status shows "LIVE," the session is orphaned.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fix: Clean Stale Sessions
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Update vicidial_log directly&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
       &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ABANDONED'&lt;/span&gt; 
       &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INCALL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'QUEUE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'LIVE'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
       &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: Use ViciDial's cleanup script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ViciDial includes a maintenance script. Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; asterisk /usr/share/astguiclient/ADMIN_cleaning_scripts.pl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removes orphaned calls from &lt;code&gt;vicidial_log&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cleans stale sessions from &lt;code&gt;vicidial_users_log&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Resets agent statuses to "READY"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Option 3: Manual agent reset via web interface&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to &lt;code&gt;/vicidial/admin.php&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Agent Management&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Find the problematic agent&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Reset Agent&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Confirm the action&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Cause 3: Asterisk Channel Bridge Misconfiguration
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Dial()&lt;/code&gt; application in Asterisk must properly bridge the agent and contact channels. If the dialplan context is wrong, bridging fails.&lt;/p&gt;

&lt;h4&gt;
  
  
  Diagnosis
&lt;/h4&gt;

&lt;p&gt;Capture a call attempt in the Asterisk CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core set debug 3"&lt;/span&gt; &lt;span class="c"&gt;# Enable verbose debugging&lt;/span&gt;
&lt;span class="c"&gt;# Now have an agent attempt a call&lt;/span&gt;
&lt;span class="c"&gt;# Monitor output in /var/log/asterisk/messages&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; bridge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for lines like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[2024-01-15 14:23:45] ERROR: Bridge failed: Channel not found
[2024-01-15 14:23:46] WARNING: Cannot create bridge for local/6001
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inspect the dialplan routing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan show from-internal"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Fix: Verify extensions-vicidial.conf Dialplan
&lt;/h4&gt;

&lt;p&gt;Check &lt;code&gt;/etc/asterisk/extensions-vicidial.conf&lt;/code&gt; for the agent context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="n"&gt;XXX&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(=== &lt;span class="n"&gt;AGENT&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; ${&lt;span class="n"&gt;EXTEN&lt;/span&gt;} ===)
 &lt;span class="n"&gt;same&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;CHANNEL&lt;/span&gt;(&lt;span class="n"&gt;hangup_handler_push&lt;/span&gt;)=&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;,&lt;span class="n"&gt;hangup_handler&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;)
 &lt;span class="n"&gt;same&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Dial&lt;/span&gt;(&lt;span class="n"&gt;SIP&lt;/span&gt;/${&lt;span class="n"&gt;EXTEN&lt;/span&gt;},&lt;span class="m"&gt;120&lt;/span&gt;,&lt;span class="n"&gt;tT&lt;/span&gt;)
 &lt;span class="n"&gt;same&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;hangup_handler&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(=== &lt;span class="n"&gt;HANGUP&lt;/span&gt; &lt;span class="n"&gt;HANDLER&lt;/span&gt; ===)
 &lt;span class="n"&gt;same&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure the &lt;code&gt;Dial()&lt;/code&gt; application uses the correct parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;tT&lt;/code&gt;&lt;/strong&gt; — Allow DTMF transfer (critical for ViciDial's transfer features)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;120&lt;/code&gt;&lt;/strong&gt; — 120-second timeout before auto-hangup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this is missing or incorrect, add or update it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf /etc/asterisk/extensions-vicidial.conf.bak
&lt;span class="c"&gt;# Edit the file with your editor&lt;/span&gt;
nano /etc/asterisk/extensions-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload the dialplan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan show from-internal"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 5 &lt;span class="s2"&gt;"_6XXX"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cause 4: Codec Mismatch Between Agent and Contact
&lt;/h3&gt;

&lt;p&gt;When an agent and contact use incompatible audio codecs, no audio passes between them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Diagnosis
&lt;/h4&gt;

&lt;p&gt;Check the allowed codecs for an agent extension in &lt;code&gt;sip-vicidial.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer 6001"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;allow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ulaw&lt;/span&gt;
&lt;span class="py"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gsm&lt;/span&gt;
&lt;span class="py"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;g729&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check what codec was negotiated on a specific call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show channels verbose"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;6001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for codec information in the channel details.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fix: Standardize Codec Configuration
&lt;/h4&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/sip-vicidial.conf&lt;/code&gt; to ensure all agent extensions have compatible codecs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;gsm&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;g729&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;opus&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in individual agent peers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="m"&gt;6001&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;friend&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="n"&gt;dynamic&lt;/span&gt;
&lt;span class="n"&gt;secret&lt;/span&gt;=&lt;span class="n"&gt;YourSecretPassword123&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;gsm&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;g729&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Best practice&lt;/strong&gt;: Order codecs by priority (most compatible first):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;          ; &lt;span class="n"&gt;Standard&lt;/span&gt;, &lt;span class="n"&gt;uncompressed&lt;/span&gt;, &lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;compatible&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;gsm&lt;/span&gt;           ; &lt;span class="n"&gt;Good&lt;/span&gt; &lt;span class="n"&gt;compression&lt;/span&gt;, &lt;span class="n"&gt;good&lt;/span&gt; &lt;span class="n"&gt;quality&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;g729&lt;/span&gt;          ; &lt;span class="n"&gt;High&lt;/span&gt; &lt;span class="n"&gt;compression&lt;/span&gt;, &lt;span class="n"&gt;lower&lt;/span&gt; &lt;span class="n"&gt;quality&lt;/span&gt; &lt;span class="n"&gt;but&lt;/span&gt; &lt;span class="n"&gt;bandwidth&lt;/span&gt;-&lt;span class="n"&gt;efficient&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload SIP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cause 5: Firewall and NAT Issues
&lt;/h3&gt;

&lt;p&gt;Media streams between agent and contact may be blocked by firewalls or misconfigured NAT.&lt;/p&gt;

&lt;h4&gt;
  
  
  Diagnosis
&lt;/h4&gt;

&lt;p&gt;Check Asterisk RTP port range configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; rtp /etc/asterisk/rtp.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;rtpstart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10000&lt;/span&gt;
&lt;span class="py"&gt;rtpend&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;20000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify these ports are open on the firewall:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--list-ports&lt;/span&gt;
&lt;span class="c"&gt;# or for ufw:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test connectivity from an agent's IP to the ViciDial server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On the agent's machine&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-I&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; udp &lt;span class="nt"&gt;--dport&lt;/span&gt; 10000:20000 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;span class="c"&gt;# or using nc (netcat)&lt;/span&gt;
nc &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 10000 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Fix: Configure Asterisk for NAT
&lt;/h4&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/rtp.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;rtpstart&lt;/span&gt;=&lt;span class="m"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;rtpend&lt;/span&gt;=&lt;span class="m"&gt;20000&lt;/span&gt;
&lt;span class="n"&gt;rtpbind&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/sip.conf&lt;/code&gt; for NAT handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;externip&lt;/span&gt;=&lt;span class="m"&gt;203&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;113&lt;/span&gt;.&lt;span class="m"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;localnet&lt;/span&gt;=&lt;span class="m"&gt;192&lt;/span&gt;.&lt;span class="m"&gt;168&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;/&lt;span class="m"&gt;255&lt;/span&gt;.&lt;span class="m"&gt;255&lt;/span&gt;.&lt;span class="m"&gt;255&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;externip&lt;/strong&gt; — Your ViciDial server's public IP (as seen from the internet)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;localnet&lt;/strong&gt; — Your internal network range&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Configure agent extension for NAT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="m"&gt;6001&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;friend&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="n"&gt;dynamic&lt;/span&gt;
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;insecure&lt;/span&gt;=&lt;span class="n"&gt;port&lt;/span&gt;,&lt;span class="n"&gt;invite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload SIP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check RTP binding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtp show stats"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Complete SIP Extension Configuration
&lt;/h3&gt;

&lt;p&gt;Here's a production-ready SIP agent extension configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="m"&gt;6001&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;friend&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="n"&gt;dynamic&lt;/span&gt;
&lt;span class="n"&gt;secret&lt;/span&gt;=&lt;span class="n"&gt;SuperSecurePassword2024&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;
&lt;span class="n"&gt;dtmfmode&lt;/span&gt;=&lt;span class="n"&gt;rfc2833&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;gsm&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;g729&lt;/span&gt;
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;insecure&lt;/span&gt;=&lt;span class="n"&gt;port&lt;/span&gt;,&lt;span class="n"&gt;invite&lt;/span&gt;
&lt;span class="n"&gt;qualify&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;qualifyfreq&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;
&lt;span class="n"&gt;directmedia&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;videosupport&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;maxcallbitrate&lt;/span&gt;=&lt;span class="m"&gt;384&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;-&lt;span class="n"&gt;timers&lt;/span&gt;=&lt;span class="n"&gt;uac&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;-&lt;span class="n"&gt;expires&lt;/span&gt;=&lt;span class="m"&gt;1800&lt;/span&gt;
&lt;span class="n"&gt;minse&lt;/span&gt;=&lt;span class="m"&gt;90&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key parameters explained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;dtmfmode=rfc2833&lt;/strong&gt; — DTMF sent in-band (required for ViciDial's IVR)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;directmedia=no&lt;/strong&gt; — All media flows through Asterisk (safer for ViciDial)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;session-timers=uac&lt;/strong&gt; — Enables session refresh (prevents audio dropouts)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;session-expires=1800&lt;/strong&gt; — Refresh every 30 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Complete Dialplan Context for ViciDial
&lt;/h3&gt;

&lt;p&gt;Add this to &lt;code&gt;/etc/asterisk/extensions-vicidial.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="n"&gt;XXX&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(=== &lt;span class="n"&gt;Agent&lt;/span&gt; &lt;span class="n"&gt;Call&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; ${&lt;span class="n"&gt;CALLERID&lt;/span&gt;(&lt;span class="n"&gt;num&lt;/span&gt;)} ===)
 &lt;span class="n"&gt;same&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;CHANNEL&lt;/span&gt;(&lt;span class="n"&gt;hangup_handler_push&lt;/span&gt;)=&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;,&lt;span class="n"&gt;h_agent_hangup&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;)
 &lt;span class="n"&gt;same&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Answer&lt;/span&gt;()
 &lt;span class="n"&gt;same&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;VICIDIAL_AGENT&lt;/span&gt;=${&lt;span class="n"&gt;EXTEN&lt;/span&gt;})
 &lt;span class="n"&gt;same&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Dial&lt;/span&gt;(&lt;span class="n"&gt;SIP&lt;/span&gt;/${&lt;span class="n"&gt;EXTEN&lt;/span&gt;},&lt;span class="m"&gt;120&lt;/span&gt;,&lt;span class="n"&gt;tT&lt;/span&gt;)
 &lt;span class="n"&gt;same&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;h_agent_hangup&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(=== &lt;span class="n"&gt;Agent&lt;/span&gt; &lt;span class="n"&gt;Hangup&lt;/span&gt; &lt;span class="n"&gt;Handler&lt;/span&gt; ===)
 &lt;span class="n"&gt;same&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;CDR&lt;/span&gt;(&lt;span class="n"&gt;userfield&lt;/span&gt;)=${&lt;span class="n"&gt;VICIDIAL_AGENT&lt;/span&gt;})
 &lt;span class="n"&gt;same&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;t&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;T&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Database Cleanup Script
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;/usr/local/bin/vicidial_cleanup.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# ViciDial Stale Session Cleanup Script&lt;/span&gt;
&lt;span class="c"&gt;# Run via cron: */30 * * * * /usr/local/bin/vicidial_cleanup.sh&lt;/span&gt;

&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/log/vicidial_cleanup.log"&lt;/span&gt;
&lt;span class="nv"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"root"&lt;/span&gt;
&lt;span class="nv"&gt;MYSQL_PASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_password_here"&lt;/span&gt;
&lt;span class="nv"&gt;DB_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"asterisk"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'+%Y-%m-%d %H:%M:%S'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;] Starting cleanup..."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;

&lt;span class="c"&gt;# Remove stale LIVE calls older than 4 hours&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nv"&gt;$MYSQL_USER&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt;$MYSQL_PASS&lt;/span&gt; &lt;span class="nv"&gt;$DB_NAME&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="sh"&gt; 2&amp;gt;&amp;amp;1
UPDATE vicidial_log 
SET status = 'ABANDONED' 
WHERE status IN ('INCALL', 'QUEUE', 'LIVE') 
AND call_date &amp;lt; DATE_SUB(NOW(), INTERVAL 4 HOUR);

UPDATE vicidial_users_log 
SET status = 'READY' 
WHERE status IN ('INCALL', 'QUEUE', 'LIVE') 
AND last_update &amp;lt; DATE_SUB(NOW(), INTERVAL 4 HOUR);
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'+%Y-%m-%d %H:%M:%S'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;] Cleanup completed."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make it executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/vicidial_cleanup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add to crontab to run every 30 minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;crontab &lt;span class="nt"&gt;-l&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"*/30 * * * * /usr/local/bin/vicidial_cleanup.sh"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; | crontab -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Debugging Techniques
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Enable Verbose SIP Logging
&lt;/h3&gt;

&lt;p&gt;Temporarily enable SIP debug for a specific agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug peer 6001"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This logs all SIP messages for extension 6001. Monitor the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"6001"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output showing successful registration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[2024-01-15 14:05:22] VERBOSE[12345] app_dial.c: SIP/6001 to 'SIP/203.0.113.50:5060'
[2024-01-15 14:05:22] DEBUG[12345] chan_sip.c: Reliably Transmitting (NAT):
INVITE sip:203.0.113.50:5060 SIP/2.0
[2024-01-15 14:05:22] VERBOSE[12345] chan_sip.c: Called SIP/203.0.113.50:5060
[2024-01-15 14:05:23] VERBOSE[12345] chan_sip.c: SIP/6001 is ringing
[2024-01-15 14:05:25] VERBOSE[12345] chan_sip.c: SIP/6001 answered SIP/inbound-00a1c2d
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Disable debug when finished:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug off"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check Active Channels and Bridges
&lt;/h3&gt;

&lt;p&gt;View all active channels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show channels"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for your agent extension. Output example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Channel              State   Application(Data)
SIP/6001-00000a12    6       Dial(SIP/outbound)
SIP/inbound-00000a13 6       Bridged Call(SIP/6001-00000a12)
————————————————————————————————————————————————————————
2 active channels
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get detailed verbose info on a specific channel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show channel SIP/6001-00000a12"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inspect ViciDial's Call Log in Real-Time
&lt;/h3&gt;

&lt;p&gt;Check what ViciDial logged for a recent call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;vicidial_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_length&lt;/span&gt; 
       &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
       &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agent001'&lt;/span&gt; 
       &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a call shows status &lt;code&gt;ABANDONED&lt;/code&gt; or &lt;code&gt;NOANSWER&lt;/code&gt; but you expected &lt;code&gt;LIVE&lt;/code&gt;, the issue occurred at the Asterisk level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitor Asterisk Resource Usage
&lt;/h3&gt;

&lt;p&gt;Check if memory or file descriptors are exhausted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show uptime"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show channels"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If channel counts approach your system limit (typically 2000-4000 per server), increase the file descriptor limit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ulimit&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 65536
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make this permanent in &lt;code&gt;/etc/security/limits.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;asterisk&lt;/span&gt; &lt;span class="n"&gt;soft&lt;/span&gt; &lt;span class="n"&gt;nofile&lt;/span&gt; &lt;span class="m"&gt;65536&lt;/span&gt;
&lt;span class="n"&gt;asterisk&lt;/span&gt; &lt;span class="n"&gt;hard&lt;/span&gt; &lt;span class="n"&gt;nofile&lt;/span&gt; &lt;span class="m"&gt;65536&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Agent Can Log In But Gets "No One Is in Your Session" on Every Call
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Verify SIP registration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peers"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"agent_extension_number"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If UNREACHABLE, have the agent log off and back on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Check the agent's device codec support. Log in to &lt;code&gt;/agc/vicidial.php&lt;/code&gt;, initiate a test call, and monitor logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"codec&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;bridge"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Verify dialplan context matches the agent's SIP context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer AGENT_EXTENSION"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Calls Connect But One-Way Audio
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Possible causes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Asymmetric firewall rules&lt;/li&gt;
&lt;li&gt;Incorrect &lt;code&gt;directmedia&lt;/code&gt; setting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/sip-vicidial.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;directmedia&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This forces all media through Asterisk, bypassing direct media negotiation which often fails with NAT.&lt;/p&gt;

&lt;p&gt;Reload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stale Database Entries Blocking New Sessions
&lt;/h3&gt;

&lt;p&gt;Run the cleanup script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/share/astguiclient/ADMIN_cleaning_scripts.pl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or manually clean via SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users_log&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'READY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calls_today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agent001'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  High Latency or Occasional Dropouts
&lt;/h3&gt;

&lt;p&gt;Check RTP stats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtp show stats"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If packet loss is high (&amp;gt;2%), verify network quality and firewall rules. Enable RTP keepalive:&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/rtp.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;dtmf_payload_type&lt;/span&gt;=&lt;span class="m"&gt;101&lt;/span&gt;
&lt;span class="n"&gt;rtpstart&lt;/span&gt;=&lt;span class="m"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;rtpend&lt;/span&gt;=&lt;span class="m"&gt;20000&lt;/span&gt;
&lt;span class="n"&gt;rtcpinterval&lt;/span&gt;=&lt;span class="m"&gt;5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Asterisk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The "No One Is in Your Session" error in ViciDial stems from channel bridge failures caused by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SIP registration issues&lt;/strong&gt; — Agents not properly registered to Asterisk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database desynchronization&lt;/strong&gt; — Stale session records blocking new calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dialplan misconfiguration&lt;/strong&gt; — Incorrect context or bridge parameters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codec mismatches&lt;/strong&gt; — Agent and contact using incompatible audio codecs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firewall/NAT problems&lt;/strong&gt; — Media path blocked or misconfigured&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Immediate fixes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Force agent re-registration via web interface or &lt;code&gt;asterisk -rx "sip reload"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Clean stale database records using &lt;code&gt;/usr/share/astguiclient/ADMIN_cleaning_scripts.pl&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Verify SIP configuration has &lt;code&gt;nat=yes&lt;/code&gt; and &lt;code&gt;directmedia=no&lt;/code&gt; for remote agents&lt;/li&gt;
&lt;li&gt;Ensure dialplan uses &lt;code&gt;Dial(SIP/${EXTEN},120,tT)&lt;/code&gt; with tT flags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schedule regular cleanup scripts via cron&lt;/li&gt;
&lt;li&gt;Monitor SIP peer status using &lt;code&gt;asterisk -rx "sip show peers"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Test codec negotiation after configuration changes&lt;/li&gt;
&lt;li&gt;Maintain firewall rules allowing RTP ports 10000-20000&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Advanced debugging:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable SIP debug: &lt;code&gt;asterisk -rx "sip set debug peer EXTENSION"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check channel state: &lt;code&gt;asterisk -rx "core show channels"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Query database for call status: &lt;code&gt;SELECT * FROM vicidial_log WHERE user = 'agentID'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Monitor logs in real-time: &lt;code&gt;tail -f /var/log/asterisk/messages&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these techniques, you'll resolve "No One Is in Your Session" errors quickly and reduce future occurrences through proactive monitoring and maintenance.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
    <item>
      <title>ViciDial Login Incorrect Error — Fix Agent &amp; Admin Auth Failures</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Thu, 23 Jul 2026 03:30:04 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/vicidial-login-incorrect-error-fix-agent-admin-auth-failures-dbd</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/vicidial-login-incorrect-error-fix-agent-admin-auth-failures-dbd</guid>
      <description>&lt;h1&gt;
  
  
  ViciDial Login Incorrect Error — Fix Agent &amp;amp; Admin Auth Failures
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Learn how to diagnose and resolve "login incorrect" authentication failures in ViciDial for both agents and administrators, including database verification, configuration checks, and real-world debugging techniques.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before troubleshooting ViciDial login errors, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Root or sudo access to your ViciDial server&lt;/li&gt;
&lt;li&gt;SSH access to the production system&lt;/li&gt;
&lt;li&gt;Database credentials for the asterisk database&lt;/li&gt;
&lt;li&gt;Basic understanding of MySQL/MariaDB queries&lt;/li&gt;
&lt;li&gt;Familiarity with Linux file permissions and Apache/PHP logs&lt;/li&gt;
&lt;li&gt;A backup of your ViciDial configuration before making changes&lt;/li&gt;
&lt;li&gt;Knowledge of your ViciDial version (check &lt;code&gt;/usr/share/astguiclient/VERSION&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify your environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SELECT VERSION();"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show version"&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"apache|mysql|asterisk"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding ViciDial Authentication Architecture
&lt;/h2&gt;

&lt;p&gt;ViciDial uses a dual-layer authentication system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Agent login&lt;/strong&gt; — Handled by &lt;code&gt;/agc/vicidial.php&lt;/code&gt;, authenticates against the &lt;code&gt;vicidial_users&lt;/code&gt; table with MD5 password hashing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin login&lt;/strong&gt; — Handled by &lt;code&gt;/vicidial/admin.php&lt;/code&gt;, authenticates against the &lt;code&gt;vicidial_users&lt;/code&gt; table with role-based access control (RBAC)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both systems validate credentials against the same user database but enforce different permission levels. The authentication flow checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User existence in &lt;code&gt;vicidial_users&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Password hash match (MD5 in most installations, though some use SHA-256)&lt;/li&gt;
&lt;li&gt;User status (active vs. inactive)&lt;/li&gt;
&lt;li&gt;User group permissions&lt;/li&gt;
&lt;li&gt;IP whitelist rules (if configured)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Causes of "Login Incorrect" Errors
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Password Hash Mismatches
&lt;/h3&gt;

&lt;p&gt;The most frequent cause is an incorrect password hash stored in the database. ViciDial's default password hashing uses MD5, but some installations have been upgraded to use SHA-256 or bcrypt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Database Sync Issues
&lt;/h3&gt;

&lt;p&gt;In clustered ViciDial setups, database replication lag can cause authentication failures on secondary nodes while working on the primary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Corrupted User Records
&lt;/h3&gt;

&lt;p&gt;Missing or malformed entries in the &lt;code&gt;vicidial_users&lt;/code&gt; table prevent proper authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP Session Configuration
&lt;/h3&gt;

&lt;p&gt;Incorrect PHP session settings, disabled extensions, or permission issues on session storage directories cause failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  IP Whitelist Blocking
&lt;/h3&gt;

&lt;p&gt;ViciDial can restrict logins by IP address. An overly restrictive whitelist blocks legitimate agents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Asterisk User Correlation Issues
&lt;/h3&gt;

&lt;p&gt;The agent user must exist in both &lt;code&gt;vicidial_users&lt;/code&gt; and the Asterisk &lt;code&gt;sip.conf&lt;/code&gt; or &lt;code&gt;extensions.conf&lt;/code&gt; for full functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 1: Verify User Existence in the Database
&lt;/h2&gt;

&lt;p&gt;The first diagnostic step is confirming the user exists and is properly configured.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query the vicidial_users Table
&lt;/h3&gt;

&lt;p&gt;Connect to the asterisk database and retrieve the user record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;user_group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_phone&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output should show the user with &lt;code&gt;active = 'Y'&lt;/code&gt;. Note the exact password hash value — you'll use this for validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify User Status and Permissions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;closer_login_allowed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lead_search_enabled&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;active = 'Y'&lt;/code&gt; (not 'N' or NULL)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user_level&lt;/code&gt; is appropriate (1-9, where 1 is admin, 9 is agent-only)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user_group&lt;/code&gt; exists and is not NULL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;closer_login_allowed = 'Y'&lt;/code&gt; for agents attempting to log in&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Check for Duplicate Users
&lt;/h3&gt;

&lt;p&gt;Duplicate usernames cause authentication confusion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; 
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If duplicates exist, delete the obsolete record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 2: Password Hash Validation and Repair
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Identify the Password Hashing Algorithm
&lt;/h3&gt;

&lt;p&gt;Check the ViciDial configuration to determine which algorithm is in use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"password"&lt;/span&gt; /etc/asterisk/vicidial.conf | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"MD5&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;SHA"&lt;/span&gt; /usr/share/astguiclient/ | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the actual login script to identify the algorithm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"md5&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;sha&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;hash"&lt;/span&gt; /var/www/html/agc/vicidial.php | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generate an MD5 Hash for Password Reset
&lt;/h3&gt;

&lt;p&gt;If you need to reset an agent's password to a known value, generate the MD5 hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"newpassword123"&lt;/span&gt; | &lt;span class="nb"&gt;md5sum&lt;/span&gt;
&lt;span class="c"&gt;# Output: 2e99758ae89ac59c117d7aab61d41f18  -&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hash (without the trailing dash) goes into the database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update the User Password
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;user_pass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MD5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'newpassword123'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_pass&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test Password Reset via CLI
&lt;/h3&gt;

&lt;p&gt;Generate a test hash and verify it matches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"echo md5('testpass'); echo PHP_EOL;"&lt;/span&gt;
&lt;span class="c"&gt;# Output: 5a105e8b9d40e1329780d62ea2265d8a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handle SHA-256 Installations
&lt;/h3&gt;

&lt;p&gt;Some modern ViciDial installations use SHA-256. Check for this in the PHP login code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"hash&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;sha"&lt;/span&gt; /var/www/html/agc/vicidial.php | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; sha
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If SHA-256 is in use, generate the hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"newpassword123"&lt;/span&gt; | &lt;span class="nb"&gt;sha256sum&lt;/span&gt;
&lt;span class="c"&gt;# Output: 9f86d081884c7d6582ef8f3f00d7e1d7  -&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then update the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;user_pass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SHA2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'newpassword123'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 3: Diagnose Login Script Failures
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Enable PHP Error Logging
&lt;/h3&gt;

&lt;p&gt;Edit the PHP configuration to log errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/php.ini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure these settings are configured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;error_reporting&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;E_ALL&lt;/span&gt;
&lt;span class="py"&gt;display_errors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Off&lt;/span&gt;
&lt;span class="py"&gt;log_errors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;On&lt;/span&gt;
&lt;span class="py"&gt;error_log&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/var/log/php-errors.log&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the log file if it doesn't exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo touch&lt;/span&gt; /var/log/php-errors.log
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;apache:apache /var/log/php-errors.log
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 /var/log/php-errors.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Apache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart apache2
&lt;span class="c"&gt;# or for CentOS/RHEL:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart httpd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitor Login Attempts
&lt;/h3&gt;

&lt;p&gt;Tail the Apache error log while attempting login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/apache2/error.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another terminal, attempt a login and observe the error output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check PHP Session Directory Permissions
&lt;/h3&gt;

&lt;p&gt;ViciDial stores PHP sessions. Verify the directory has correct permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-ld&lt;/span&gt; /var/lib/php/sessions
&lt;span class="c"&gt;# Should output: drwxrwxrwt ... /var/lib/php/sessions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If permissions are wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;1777 /var/lib/php/sessions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify MySQL Connectivity from PHP
&lt;/h3&gt;

&lt;p&gt;Create a test script to verify the PHP-to-MySQL connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /var/www/html/test_db.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$db_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db_pass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'yourpassword'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'asterisk'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;mysqli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$db_host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$db_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$db_pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$db_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$conn&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;connect_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Connection failed: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$conn&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;connect_error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conn&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SELECT user_name, user_pass FROM vicidial_users LIMIT 1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;num_rows&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch_assoc&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"User: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"user_name"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;" | Pass Hash: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"user_pass"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No users found&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$conn&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access it via browser: &lt;code&gt;http://your-server/test_db.php&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then remove the test file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo rm&lt;/span&gt; /var/www/html/test_db.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 4: Check IP Whitelist Restrictions
&lt;/h2&gt;

&lt;p&gt;ViciDial can restrict logins by source IP address. Verify this isn't blocking legitimate agents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query IP Restrictions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;vicidial_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check if there's an IP restriction table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%ip%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%whitelist%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%access%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Locate IP Whitelist Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"ip_address&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;whitelist"&lt;/span&gt; /usr/share/astguiclient/ | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;".git"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Review the Login PHP Script for IP Checks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"ip&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;whitelist&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;access"&lt;/span&gt; /var/www/html/agc/vicidial.php | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If IP restrictions are enforced, identify the agent's actual IP address from Apache logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo tail&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt; /var/log/apache2/access.log | &lt;span class="nb"&gt;grep &lt;/span&gt;agentname
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for the IP address in the columns before the timestamp.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disable or Update IP Restrictions
&lt;/h3&gt;

&lt;p&gt;If the IP whitelist is too restrictive, you can disable it temporarily for testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;ip_address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'0.0.0.0/0'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows login from any IP. For production, use the agent's actual subnet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;ip_address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'192.168.1.0/24'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 5: Verify Asterisk User Configuration
&lt;/h2&gt;

&lt;p&gt;Agents must exist in both the ViciDial database and the Asterisk SIP configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check sip-vicidial.conf
&lt;/h3&gt;

&lt;p&gt;Examine the Asterisk SIP configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 5 &lt;span class="s2"&gt;"agentname"&lt;/span&gt; /etc/asterisk/sip-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;agentname&lt;/span&gt;](&lt;span class="n"&gt;vicidial_agent&lt;/span&gt;)
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;friend&lt;/span&gt;
&lt;span class="n"&gt;username&lt;/span&gt;=&lt;span class="n"&gt;agentname&lt;/span&gt;
&lt;span class="n"&gt;secret&lt;/span&gt;=&lt;span class="n"&gt;sippassword&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify extensions.conf
&lt;/h3&gt;

&lt;p&gt;The agent must be defined in extensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-B&lt;/span&gt; 2 &lt;span class="nt"&gt;-A&lt;/span&gt; 10 &lt;span class="s2"&gt;"exten =&amp;gt; agentname"&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reload Asterisk SIP Configuration
&lt;/h3&gt;

&lt;p&gt;If you add or modify a user, reload the SIP module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the user loaded correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer agentname"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output shows the peer details including IP, port, and registration status.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 6: Troubleshoot Admin Login Issues
&lt;/h2&gt;

&lt;p&gt;Admin login uses the same database but enforces additional role checks. Address these specific issues:&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify Admin User Level
&lt;/h3&gt;

&lt;p&gt;Admin users must have &lt;code&gt;user_level&lt;/code&gt; between 1-5 (where 1 is superadmin):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_level&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'adminname'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user_level is 6 or higher, they're classified as an agent, not admin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update User to Admin Level
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;user_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'adminname'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check Admin-Specific Permissions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin_login_allowed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin_phonebook&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;admin_reports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin_settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin_users&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'adminname'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure admin privilege columns are set to 'Y':&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;admin_login_allowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;admin_reports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;admin_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;admin_users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Y'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'adminname'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Clear Admin Session Cache
&lt;/h3&gt;

&lt;p&gt;Some ViciDial versions cache admin sessions. Clear this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /tmp/vicidial_&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/php/sessions/vicidial_&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restart Apache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart apache2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 7: Database Replication and Sync Issues
&lt;/h2&gt;

&lt;p&gt;In clustered ViciDial deployments, database replication lag can cause authentication failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check Replication Status
&lt;/h3&gt;

&lt;p&gt;On the database slave:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SHOW SLAVE STATUS&lt;/span&gt;&lt;span class="se"&gt;\G&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Slave_IO_Running: Yes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Slave_SQL_Running: Yes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Seconds_Behind_Master: 0&lt;/code&gt; (or a small number)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If &lt;code&gt;Seconds_Behind_Master&lt;/code&gt; is high, replication is lagging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify Master-Slave Consistency
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- On master:&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;MASTER&lt;/span&gt; &lt;span class="n"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- On slave:&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;SLAVE&lt;/span&gt; &lt;span class="n"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare the &lt;code&gt;File&lt;/code&gt; and &lt;code&gt;Position&lt;/code&gt; columns. They should match between master and slave after replication catches up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Force User Sync
&lt;/h3&gt;

&lt;p&gt;If replication is stuck, manually sync the user record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Export from master&lt;/span&gt;
mysqldump &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk vicidial_users &lt;span class="nt"&gt;--where&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"user_name='agentname'"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/user_export.sql

&lt;span class="c"&gt;# Import on slave&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &amp;lt; /tmp/user_export.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Restart Replication
&lt;/h3&gt;

&lt;p&gt;If replication is broken:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On slave, as root:&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"STOP SLAVE;"&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;"&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"START SLAVE;"&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SHOW SLAVE STATUS&lt;/span&gt;&lt;span class="se"&gt;\G&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 8: Check Web Server and PHP Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Verify Apache is Serving ViciDial Correctly
&lt;/h3&gt;

&lt;p&gt;Test the web paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://localhost/agc/vicidial.php
curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://localhost/vicidial/admin.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both should return HTTP 200.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check PHP Modules
&lt;/h3&gt;

&lt;p&gt;Ensure required PHP modules are loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php &lt;span class="nt"&gt;-m&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"mysql|mysqli|json|session|zlib"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Missing modules will prevent database access. Install missing extensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ubuntu/Debian:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;php-mysql php-json php-zlib

&lt;span class="c"&gt;# CentOS/RHEL:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;php-mysql php-json php-zlib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify ViciDial File Permissions
&lt;/h3&gt;

&lt;p&gt;The web server must read ViciDial files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /var/www/html/agc/vicidial.php
&lt;span class="c"&gt;# Should show: -rw-r--r-- apache:apache&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix permissions if needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; apache:apache /var/www/html/agc
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; apache:apache /var/www/html/vicidial
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 755 /var/www/html/agc
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 755 /var/www/html/vicidial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check Apache Configuration for ViciDial
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"ViciDial&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;vicidial"&lt;/span&gt; /etc/apache2/sites-enabled/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure the VirtualHost or Directory block includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&amp;lt;&lt;span class="n"&gt;Directory&lt;/span&gt; /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;www&lt;/span&gt;/&lt;span class="n"&gt;html&lt;/span&gt;/&lt;span class="n"&gt;agc&lt;/span&gt;&amp;gt;
    &lt;span class="n"&gt;Options&lt;/span&gt; &lt;span class="n"&gt;FollowSymLinks&lt;/span&gt;
    &lt;span class="n"&gt;AllowOverride&lt;/span&gt; &lt;span class="n"&gt;All&lt;/span&gt;
    &lt;span class="n"&gt;Require&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="n"&gt;granted&lt;/span&gt;
&amp;lt;/&lt;span class="n"&gt;Directory&lt;/span&gt;&amp;gt;

&amp;lt;&lt;span class="n"&gt;Directory&lt;/span&gt; /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;www&lt;/span&gt;/&lt;span class="n"&gt;html&lt;/span&gt;/&lt;span class="n"&gt;vicidial&lt;/span&gt;&amp;gt;
    &lt;span class="n"&gt;Options&lt;/span&gt; &lt;span class="n"&gt;FollowSymLinks&lt;/span&gt;
    &lt;span class="n"&gt;AllowOverride&lt;/span&gt; &lt;span class="n"&gt;All&lt;/span&gt;
    &lt;span class="n"&gt;Require&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="n"&gt;granted&lt;/span&gt;
&amp;lt;/&lt;span class="n"&gt;Directory&lt;/span&gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 9: Audit Login Attempt Logs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Find Login Audit Tables
&lt;/h3&gt;

&lt;p&gt;ViciDial logs authentication attempts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%log%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query recent login failures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notes&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check Asterisk Logs for Authentication Errors
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo tail&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"auth&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;login&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;fail&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;agentname"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for patterns like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Authentication failed"&lt;/li&gt;
&lt;li&gt;"Invalid extension"&lt;/li&gt;
&lt;li&gt;"Connection refused"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monitor Asterisk in Real-Time
&lt;/h3&gt;

&lt;p&gt;Open Asterisk CLI and monitor events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-r&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the Asterisk console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;*CLI&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sip &lt;span class="nb"&gt;set &lt;/span&gt;debug on
&lt;span class="gp"&gt;*CLI&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;core &lt;span class="nb"&gt;set &lt;/span&gt;verbose 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attempt login and watch for SIP errors. Exit with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;*CLI&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 10: Database Repair and Optimization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Repair Corrupted User Records
&lt;/h3&gt;

&lt;p&gt;Run a database check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;REPAIR&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If corruption is found, rebuild the table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InnoDB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Data Integrity
&lt;/h3&gt;

&lt;p&gt;Ensure no NULL values in critical fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; 
&lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;user_pass&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; 
&lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;user_level&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If results are found, delete those rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; 
&lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;user_pass&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; 
&lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;user_level&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Optimize the Table
&lt;/h3&gt;

&lt;p&gt;After repairs, optimize:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;OPTIMIZE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Agent Login Works on One Server but Not Another
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Agent successfully logs in via Server A but gets "login incorrect" on Server B.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database replication lag on Server B&lt;/li&gt;
&lt;li&gt;Different PHP configuration between servers&lt;/li&gt;
&lt;li&gt;Mismatched password hashing algorithms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify database sync on Server B&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SELECT COUNT(*) FROM vicidial_users;"&lt;/span&gt;

&lt;span class="c"&gt;# Compare with Server A&lt;/span&gt;
&lt;span class="c"&gt;# If different, force sync:&lt;/span&gt;
mysqldump &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk vicidial_users | mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; serverB asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  "Login Incorrect" Despite Correct Password
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; User enters correct password but receives "login incorrect".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Password hash algorithm mismatch&lt;/li&gt;
&lt;li&gt;Corrupted user record in database&lt;/li&gt;
&lt;li&gt;PHP session cache corruption&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Reset password with known hash&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"testpassword"&lt;/span&gt; | &lt;span class="nb"&gt;md5sum&lt;/span&gt;
&lt;span class="c"&gt;# Use the output hash:&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"UPDATE vicidial_users SET user_pass=MD5('testpassword') WHERE user_name='agentname';"&lt;/span&gt;

&lt;span class="c"&gt;# Clear session cache&lt;/span&gt;
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/php/sessions/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart apache2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Admin Login Fails but Agent Login Works
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Admin cannot log in to &lt;code&gt;/vicidial/admin.php&lt;/code&gt; but agents use &lt;code&gt;/agc/vicidial.php&lt;/code&gt; successfully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admin user lacks proper role/permissions&lt;/li&gt;
&lt;li&gt;Admin login script specific error&lt;/li&gt;
&lt;li&gt;Admin IP whitelist is too restrictive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;user_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;admin_login_allowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;ip_address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'0.0.0.0/0'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'adminname'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check for admin-specific errors&lt;/span&gt;
&lt;span class="nb"&gt;sudo tail&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt; /var/log/apache2/error.log | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Login Works from Office Network but Fails from Home
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Agent logs in successfully from office but gets "login incorrect" from home IP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IP whitelist is blocking home IP address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find agent's home IP from browser&lt;/span&gt;
&lt;span class="c"&gt;# Add it to the whitelist:&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"UPDATE vicidial_users SET ip_address='192.168.0.0/16,203.0.113.100' WHERE user_name='agentname';"&lt;/span&gt;

&lt;span class="c"&gt;# Or disable IP whitelist for testing:&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"UPDATE vicidial_users SET ip_address='' WHERE user_name='agentname';"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Password Hash Length Mismatch
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Password hash in database is wrong length (e.g., 32 chars for SHA-256).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Password was hashed with different algorithm than what login expects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check current hash length and algorithm&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SELECT user_name, LENGTH(user_pass) FROM vicidial_users LIMIT 5;"&lt;/span&gt;

&lt;span class="c"&gt;# If MD5 (32 chars), regenerate:&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"UPDATE vicidial_users SET user_pass=MD5('newpass') WHERE user_name='agentname';"&lt;/span&gt;

&lt;span class="c"&gt;# If SHA-256 (64 chars):&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"UPDATE vicidial_users SET user_pass=SHA2('newpass', 256) WHERE user_name='agentname';"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Multiple Failed Login Attempts Lock Account
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; After several failed logins, account becomes locked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ViciDial has login attempt limiting enabled&lt;/li&gt;
&lt;li&gt;Account lockout table stores failed attempts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Check for lockout table&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%lockout%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%attempt%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Query failed attempts&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_user_lock&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Clear lockout&lt;/span&gt;
&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_user_lock&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agentname'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Or reset via ViciDial config (check settings):&lt;/span&gt;
&lt;span class="n"&gt;grep&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="nv"&gt;"lockout&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="nv"&gt;max_attempt"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;asterisk&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vicidial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Case Sensitivity in Usernames
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; "agentname" and "AgentName" are treated as different users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database has case-sensitive username collation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Check collation&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;

&lt;span class="c1"&gt;-- If case-sensitive, standardize to lowercase:&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;LOWER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Change collation to case-insensitive&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;MODIFY&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;CHARACTER&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;utf8mb4&lt;/span&gt; &lt;span class="k"&gt;COLLATE&lt;/span&gt; &lt;span class="n"&gt;utf8mb4_general_ci&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;ViciDial "login incorrect" errors stem from misaligned database records, password hash issues, or web server configuration problems. Systematically verify:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;User existence&lt;/strong&gt; — Confirm the user exists in &lt;code&gt;vicidial_users&lt;/code&gt; with &lt;code&gt;active = 'Y'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password hash&lt;/strong&gt; — Verify the hash matches the authentication algorithm (MD5 or SHA-256)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User permissions&lt;/strong&gt; — Ensure &lt;code&gt;user_level&lt;/code&gt;, &lt;code&gt;user_group&lt;/code&gt;, and role permissions are correct&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database sync&lt;/strong&gt; — Check that replication is current in clustered deployments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IP restrictions&lt;/strong&gt; — Confirm the agent's IP isn't blocked by whitelist rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asterisk correlation&lt;/strong&gt; — Verify the user exists in SIP configuration for agent logins&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web server&lt;/strong&gt; — Validate PHP modules, file permissions, and Apache configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session handling&lt;/strong&gt; — Ensure PHP session directory permissions are correct&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use the database queries and CLI commands provided to diagnose issues quickly. When in doubt, reset the password with a known hash, clear the PHP session cache, and restart Apache. For production systems, always test changes on a non-critical agent account first, maintain database backups, and log all authentication modifications for audit compliance.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
    <item>
      <title>Securing ViciDial Admin Interface — HTTPS, IP Whitelist, 2FA</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Wed, 22 Jul 2026 03:30:03 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/securing-vicidial-admin-interface-https-ip-whitelist-2fa-2n8j</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/securing-vicidial-admin-interface-https-ip-whitelist-2fa-2n8j</guid>
      <description>&lt;h1&gt;
  
  
  Securing ViciDial Admin Interface — HTTPS, IP Whitelist, 2FA
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Learn how to lock down your ViciDial admin panel with production-grade security: enforce HTTPS, restrict access by IP, implement two-factor authentication, and harden the database layer.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before implementing these security measures, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A running ViciDial installation (v2.14+ recommended, though applicable to v2.9+)&lt;/li&gt;
&lt;li&gt;Root or sudo access to your ViciDial server&lt;/li&gt;
&lt;li&gt;An SSL/TLS certificate (self-signed or from a CA like Let's Encrypt)&lt;/li&gt;
&lt;li&gt;Asterisk 11+ installed and running&lt;/li&gt;
&lt;li&gt;Apache or Nginx web server (this guide covers Apache primarily)&lt;/li&gt;
&lt;li&gt;MySQL/MariaDB database access with root credentials&lt;/li&gt;
&lt;li&gt;Basic Linux command-line competency&lt;/li&gt;
&lt;li&gt;Backup of your ViciDial database before making changes&lt;/li&gt;
&lt;li&gt;A test account to verify 2FA without locking out your main account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify your current setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-V&lt;/span&gt;
apache2 &lt;span class="nt"&gt;-v&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-V&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Securing the Admin Interface Matters
&lt;/h2&gt;

&lt;p&gt;The ViciDial admin interface (&lt;code&gt;/vicidial/admin.php&lt;/code&gt;) controls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User and agent account creation/deletion&lt;/li&gt;
&lt;li&gt;Campaign configuration and call routing&lt;/li&gt;
&lt;li&gt;Pause codes, dispositions, and call outcomes&lt;/li&gt;
&lt;li&gt;Database access and backups&lt;/li&gt;
&lt;li&gt;System logs and call recordings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unauthorized access to the admin panel can result in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Malicious campaign deployment&lt;/li&gt;
&lt;li&gt;Call data theft and GDPR violations&lt;/li&gt;
&lt;li&gt;Agent credential compromise&lt;/li&gt;
&lt;li&gt;Call recording exfiltration&lt;/li&gt;
&lt;li&gt;Service disruption and DDoS via call flooding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tutorial covers three complementary layers of defense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 1: Enforce HTTPS and Disable HTTP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Obtain an SSL/TLS Certificate
&lt;/h3&gt;

&lt;p&gt;If you don't have a certificate, obtain one using Let's Encrypt (free):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get update
apt-get &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-apache &lt;span class="nt"&gt;-y&lt;/span&gt;
certbot certonly &lt;span class="nt"&gt;--standalone&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; admin.yourdomain.com &lt;span class="nt"&gt;-d&lt;/span&gt; vicidial.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the prompts. Certificates are saved to &lt;code&gt;/etc/letsencrypt/live/admin.yourdomain.com/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For self-signed certificates (test environments only):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:2048 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-keyout&lt;/span&gt; /etc/ssl/private/vicidial-self.key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-out&lt;/span&gt; /etc/ssl/certs/vicidial-self.crt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/C=US/ST=State/L=City/O=Company/CN=vicidial.local"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.2 Configure Apache for HTTPS
&lt;/h3&gt;

&lt;p&gt;Enable the SSL module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;a2enmod ssl
a2enmod rewrite
systemctl restart apache2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Locate your ViciDial VirtualHost configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find /etc/apache2 &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*vicidial*"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.conf"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; vicidial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If none exists, create one at &lt;code&gt;/etc/apache2/sites-available/vicidial-ssl.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;VirtualHost&lt;/span&gt;&lt;span class="sr"&gt; *:80&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="nc"&gt;ServerName&lt;/span&gt; admin.yourdomain.com
    &lt;span class="nc"&gt;DocumentRoot&lt;/span&gt; /var/www/html

    &lt;span class="c"&gt;# Force all HTTP traffic to HTTPS&lt;/span&gt;
    &lt;span class="nc"&gt;RewriteEngine&lt;/span&gt; &lt;span class="ss"&gt;On&lt;/span&gt;
    &lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{HTTPS} &lt;span class="ss"&gt;off&lt;/span&gt;
    &lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;VirtualHost&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;VirtualHost&lt;/span&gt;&lt;span class="sr"&gt; *:443&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="nc"&gt;ServerName&lt;/span&gt; admin.yourdomain.com
    &lt;span class="nc"&gt;DocumentRoot&lt;/span&gt; /var/www/html

    &lt;span class="nc"&gt;SSLEngine&lt;/span&gt; &lt;span class="ss"&gt;on&lt;/span&gt;
    &lt;span class="nc"&gt;SSLCertificateFile&lt;/span&gt; /etc/letsencrypt/live/admin.yourdomain.com/fullchain.pem
    &lt;span class="nc"&gt;SSLCertificateKeyFile&lt;/span&gt; /etc/letsencrypt/live/admin.yourdomain.com/privkey.pem

    &lt;span class="c"&gt;# Modern SSL/TLS configuration&lt;/span&gt;
    &lt;span class="nc"&gt;SSLProtocol&lt;/span&gt; TLSv1.2 TLSv1.3
    &lt;span class="nc"&gt;SSLCipherSuite&lt;/span&gt; ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
    &lt;span class="nc"&gt;SSLHonorCipherOrder&lt;/span&gt; &lt;span class="ss"&gt;on&lt;/span&gt;
    &lt;span class="nc"&gt;SSLCompression&lt;/span&gt; &lt;span class="ss"&gt;off&lt;/span&gt;

    &lt;span class="c"&gt;# HSTS - Force HTTPS for 1 year&lt;/span&gt;
    &lt;span class="nc"&gt;Header&lt;/span&gt; &lt;span class="ss"&gt;always&lt;/span&gt; &lt;span class="ss"&gt;set&lt;/span&gt; Strict-Transport-Security "max-age=31536000; includeSubDomains"

    &lt;span class="c"&gt;# Disable server signature exposure&lt;/span&gt;
    &lt;span class="nc"&gt;Header&lt;/span&gt; &lt;span class="ss"&gt;always&lt;/span&gt; &lt;span class="ss"&gt;unset&lt;/span&gt; "Server"
    &lt;span class="nc"&gt;Header&lt;/span&gt; &lt;span class="ss"&gt;always&lt;/span&gt; &lt;span class="ss"&gt;unset&lt;/span&gt; "X-Powered-By"
    &lt;span class="nc"&gt;ServerTokens&lt;/span&gt; Prod

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;Directory&lt;/span&gt;&lt;span class="sr"&gt; /var/www/html/vicidial&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="nc"&gt;DirectoryIndex&lt;/span&gt; index.html admin.php
        &lt;span class="nc"&gt;AllowOverride&lt;/span&gt; &lt;span class="ss"&gt;All&lt;/span&gt;
        &lt;span class="nc"&gt;Require&lt;/span&gt; &lt;span class="ss"&gt;all&lt;/span&gt; granted

        &lt;span class="c"&gt;# Disable directory listing&lt;/span&gt;
        &lt;span class="nc"&gt;Options&lt;/span&gt; -Indexes
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;
    &lt;span class="c"&gt;# Proxy to Asterisk AGI if needed&lt;/span&gt;
    &lt;span class="nc"&gt;ProxyPreserveHost&lt;/span&gt; &lt;span class="ss"&gt;On&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;Location&lt;/span&gt;&lt;span class="sr"&gt; /agi-bin/&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="nc"&gt;ProxyPass&lt;/span&gt; http://127.0.0.1:8088/agi-bin/
        &lt;span class="nc"&gt;ProxyPassReverse&lt;/span&gt; http://127.0.0.1:8088/agi-bin/
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;
    &lt;span class="nc"&gt;ErrorLog&lt;/span&gt; ${APACHE_LOG_DIR}/vicidial-error.log
    &lt;span class="nc"&gt;CustomLog&lt;/span&gt; ${APACHE_LOG_DIR}/vicidial-access.log combined
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;VirtualHost&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable the site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;a2ensite vicidial-ssl
apache2ctl configtest
systemctl restart apache2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify HTTPS is working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://admin.yourdomain.com 2&amp;gt;/dev/null | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;200 OK&lt;/code&gt; and HTTP/1.1 or HTTP/2.0.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.3 Disable Weak Protocols and Ciphers
&lt;/h3&gt;

&lt;p&gt;Verify your SSL configuration with testssl.sh or similar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;testssl.sh
testssl.sh https://admin.yourdomain.com 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"protocol&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;cipher"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove any references to SSLv3, TLSv1.0, or TLSv1.1 from &lt;code&gt;/etc/apache2/mods-available/ssl.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="nc"&gt;SSLProtocol&lt;/span&gt; -all +TLSv1.2 +TLSv1.3
&lt;span class="nc"&gt;SSLCipherSuite&lt;/span&gt; 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Apache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl restart apache2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 2: IP Whitelist for Admin Access
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Implement IP Filtering at Apache Level
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/apache2/sites-available/vicidial-ssl.conf&lt;/code&gt; to restrict access to &lt;code&gt;/vicidial/admin.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;VirtualHost&lt;/span&gt;&lt;span class="sr"&gt; *:443&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="nc"&gt;ServerName&lt;/span&gt; admin.yourdomain.com
    &lt;span class="c"&gt;# ... SSL config as above ...&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;Location&lt;/span&gt;&lt;span class="sr"&gt; /vicidial/admin.php&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="c"&gt;# Whitelist office IPs&lt;/span&gt;
        &lt;span class="nc"&gt;Require&lt;/span&gt; ip 203.0.113.10
        &lt;span class="nc"&gt;Require&lt;/span&gt; ip 203.0.113.11
        &lt;span class="nc"&gt;Require&lt;/span&gt; ip 198.51.100.0/24

        &lt;span class="c"&gt;# Log denied access attempts&lt;/span&gt;
        &lt;span class="nc"&gt;SetEnvIf&lt;/span&gt; &lt;span class="ss"&gt;Request_URI&lt;/span&gt; "/vicidial/admin.php" admin_access
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;Directory&lt;/span&gt;&lt;span class="sr"&gt; /var/www/html/vicidial&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="c"&gt;# ... other directives ...&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;VirtualHost&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="c"&gt;# Log admin access to separate file&lt;/span&gt;
&lt;span class="nc"&gt;CustomLog&lt;/span&gt; ${APACHE_LOG_DIR}/vicidial-admin-access.log combined env=admin_access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apache2ctl configtest
systemctl restart apache2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify from an allowed IP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; https://admin.yourdomain.com/vicidial/admin.php 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From a blocked IP, you should receive a 403 Forbidden error.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Create a Dynamic IP Whitelist File
&lt;/h3&gt;

&lt;p&gt;For environments where admin IPs change frequently, use an include file:&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;/etc/apache2/vicidial-whitelist.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;If&lt;/span&gt;&lt;span class="sr"&gt; "%{REQUEST_URI} =~ m#/vicidial/admin\.php#"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="nc"&gt;Require&lt;/span&gt; ip 203.0.113.10 203.0.113.11
    &lt;span class="nc"&gt;Require&lt;/span&gt; ip 198.51.100.0/24
    &lt;span class="nc"&gt;Require&lt;/span&gt; ip 192.168.1.0/24
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;If&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include it in your VirtualHost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;VirtualHost&lt;/span&gt;&lt;span class="sr"&gt; *:443&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="c"&gt;# ... SSL config ...&lt;/span&gt;

    &lt;span class="nc"&gt;Include&lt;/span&gt; /etc/apache2/vicidial-whitelist.conf
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;VirtualHost&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To add IPs dynamically without restarting Apache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"    203.0.113.99"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/apache2/vicidial-whitelist.conf
systemctl reload apache2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.3 Add ModSecurity Rules (Optional but Recommended)
&lt;/h3&gt;

&lt;p&gt;Install ModSecurity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;libapache2-mod-security2 modsecurity-crs &lt;span class="nt"&gt;-y&lt;/span&gt;
a2enmod security2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;/etc/apache2/conf-available/vicidial-modsecurity.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;SecRuleEngine &lt;span class="ss"&gt;On&lt;/span&gt;
SecRequestBodyAccess &lt;span class="ss"&gt;On&lt;/span&gt;
SecRequestBodyLimit 16777216
SecRequestBodyNoFiles &lt;span class="ss"&gt;Off&lt;/span&gt;

&lt;span class="c"&gt;# Log all requests to admin interface&lt;/span&gt;
SecRule &lt;span class="ss"&gt;REQUEST_URI&lt;/span&gt; "@beginsWith /vicidial/admin.php" \
    &lt;span class="err"&gt;"&lt;/span&gt;id:1001,phase:1,log,logdata:%{REMOTE_ADDR},pass"

&lt;span class="c"&gt;# Block common injection attacks&lt;/span&gt;
SecRule ARGS "@rx (?:union|select|insert|update|delete|drop)" \
    &lt;span class="err"&gt;"&lt;/span&gt;id:1002,phase:2,deny,status:403,msg:'SQL Injection attempt'"

SecRule ARGS "@rx (?:\.\./|\.\.\\)" \
    &lt;span class="err"&gt;"&lt;/span&gt;id:1003,phase:2,deny,status:403,msg:'Path traversal attempt'"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable the module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;a2enconf vicidial-modsecurity
systemctl restart apache2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Monitor ModSecurity logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/apache2/modsec_audit.log | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; vicidial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 3: Implement Two-Factor Authentication (2FA)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Choose a 2FA Method
&lt;/h3&gt;

&lt;p&gt;ViciDial does not include native 2FA in the admin interface. We'll implement 2FA by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Database-level tracking&lt;/strong&gt; of OTP secrets and verified states&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP wrapper&lt;/strong&gt; that intercepts login attempts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TOTP generation&lt;/strong&gt; using Google Authenticator or Authy&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3.2 Prepare the Database
&lt;/h3&gt;

&lt;p&gt;Create a new table to store TOTP secrets and recovery codes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;asterisk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="nv"&gt;`vicidial_admin_2fa`&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;`user_id`&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;`username`&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;`totp_secret`&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nv"&gt;`totp_enabled`&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'N'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'N'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;`backup_codes`&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;`created_date`&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;`last_verified`&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`user_id`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`user_id`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="nv"&gt;`vicidial_users`&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`user_id`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InnoDB&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;CHARSET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;utf8mb4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Add 2FA attempt logging&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="nv"&gt;`vicidial_2fa_audit`&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;`id`&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;`username`&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;`attempt_type`&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LOGIN'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'OTP_VERIFY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'BACKUP_CODE'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;`result`&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SUCCESS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'INVALID_USER'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;`remote_ip`&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nv"&gt;`attempt_time`&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`username`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`attempt_time`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InnoDB&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;CHARSET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;utf8mb4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify table creation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"DESCRIBE vicidial_admin_2fa;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.3 Install PHP TOTP Library
&lt;/h3&gt;

&lt;p&gt;Navigate to the ViciDial web root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/html/vicidial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install PHPGangsta_GoogleAuthenticator (TOTP implementation):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;php-composer &lt;span class="nt"&gt;-y&lt;/span&gt;
composer require phpgangsta/googleauthenticator 1.0.&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or download manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /var/www/html/vicidial/GoogleAuthenticator.php &lt;span class="se"&gt;\&lt;/span&gt;
  https://raw.githubusercontent.com/PHPGangsta/GoogleAuthenticator/master/PHPGangsta/GoogleAuthenticator.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /var/www/html/vicidial/GoogleAuthenticator.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.4 Create 2FA Verification Script
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;/var/www/html/vicidial/admin_2fa_login.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
 * ViciDial Admin 2FA Login Handler
 * Validates TOTP before granting access to admin.php
 */&lt;/span&gt;

&lt;span class="nb"&gt;session_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nb"&gt;error_reporting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Database configuration&lt;/span&gt;
&lt;span class="nv"&gt;$db_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db_pass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_DB_PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'asterisk'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$mysqli&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;mysqli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$db_host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$db_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$db_pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$db_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;connect_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Database connection failed: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;connect_error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Include TOTP library&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="s1"&gt;'GoogleAuthenticator.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$ga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PHPGangsta_GoogleAuthenticator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$error_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$info_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Handle OTP verification&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_METHOD'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'action'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'action'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'verify_otp'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sanitizeInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$otp_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sanitizeInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'otp_code'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$remote_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REMOTE_ADDR'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="c1"&gt;// Validate inputs&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$otp_code&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$error_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Username and OTP code required'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nf"&gt;logAuditEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'OTP_VERIFY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$remote_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Empty fields'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Fetch TOTP secret from database&lt;/span&gt;
            &lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"
                SELECT totp_secret FROM vicidial_admin_2fa 
                WHERE username = ? AND totp_enabled = 'Y'
            "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_result&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;num_rows&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nv"&gt;$error_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'User not found or 2FA not enabled'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nf"&gt;logAuditEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'OTP_VERIFY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'INVALID_USER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$remote_ip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nv"&gt;$row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch_assoc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nv"&gt;$totp_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'totp_secret'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

                &lt;span class="c1"&gt;// Verify the OTP (allows 1 minute window for clock drift)&lt;/span&gt;
                &lt;span class="nv"&gt;$checkResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ga&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;verifyCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$totp_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$otp_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$checkResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// OTP valid - allow access&lt;/span&gt;
                    &lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'2fa_verified'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'admin_user'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'2fa_verify_time'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                    &lt;span class="c1"&gt;// Update last verified timestamp&lt;/span&gt;
                    &lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"
                        UPDATE vicidial_admin_2fa 
                        SET last_verified = NOW() 
                        WHERE username = ?
                    "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                    &lt;span class="nf"&gt;logAuditEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'OTP_VERIFY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'SUCCESS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$remote_ip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                    &lt;span class="c1"&gt;// Redirect to admin.php&lt;/span&gt;
                    &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Location: admin.php'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nv"&gt;$error_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Invalid OTP code. Please try again.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nf"&gt;logAuditEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'OTP_VERIFY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'FAILURE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$remote_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Invalid OTP'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;sanitizeInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;htmlspecialchars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;ENT_QUOTES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'UTF-8'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;logAuditEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$notes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"
        INSERT INTO vicidial_2fa_audit (username, attempt_type, result, remote_ip) 
        VALUES (?, ?, ?, ?)
    "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ssss'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;ViciDial Admin - Two-Factor Authentication&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;135deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#667eea&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#764ba2&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.login-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;25px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.login-container&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.form-group&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#555&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
        &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"password"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ddd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"password"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#667eea&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;126&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#667eea&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="m"&gt;0.3s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#764ba2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#c33&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#c33&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.info&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e3f2fd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1976d2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#1976d2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"login-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;🔐 ViciDial Admin&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

        &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$error_msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$error_msg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;

        &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$info_msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"info"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$info_msg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"action"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"verify_otp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-group"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Username&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Admin username"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;autofocus&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-group"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"otp_code"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Authenticator Code&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"otp_code"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"otp_code"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"000000"&lt;/span&gt; &lt;span class="na"&gt;maxlength=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt; &lt;span class="na"&gt;pattern=&lt;/span&gt;&lt;span class="s"&gt;"\d{6}"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"info"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin-top: 8px; margin-bottom: 0;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    Enter the 6-digit code from your authenticator app (Google Authenticator, Authy, etc.)
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Verify &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Access Admin Panel&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set proper permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chown &lt;/span&gt;www-data:www-data /var/www/html/vicidial/admin_2fa_login.php
&lt;span class="nb"&gt;chmod &lt;/span&gt;640 /var/www/html/vicidial/admin_2fa_login.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.5 Wrap the Existing admin.php to Enforce 2FA
&lt;/h3&gt;

&lt;p&gt;Create a backup of the original admin.php:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; /var/www/html/vicidial/admin.php /var/www/html/vicidial/admin.php.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;/var/www/html/vicidial/admin_2fa_check.php&lt;/code&gt; (new wrapper):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
 * 2FA Check Wrapper for ViciDial Admin
 * Must be included at the top of admin.php
 */&lt;/span&gt;

&lt;span class="nb"&gt;session_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'db_host'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'db_user'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'db_pass'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_DB_PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'db_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'asterisk'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'2fa_enabled'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Set to false to disable 2FA globally&lt;/span&gt;
    &lt;span class="s1"&gt;'2fa_session_timeout'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;  &lt;span class="c1"&gt;// 1 hour in seconds&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// List of admin usernames that MUST use 2FA&lt;/span&gt;
&lt;span class="nv"&gt;$mandatory_2fa_users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'superadmin'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Check if user is already verified in this session&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'2fa_verified'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'admin_user'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check session timeout&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'2fa_verify_time'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'2fa_verify_time'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'2fa_session_timeout'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Session still valid - continue to admin.php&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Session expired - force re-authentication&lt;/span&gt;
            &lt;span class="nb"&gt;session_destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Location: admin_2fa_login.php'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// User not yet verified - redirect to 2FA login&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'2fa_enabled'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check if accessing admin.php directly&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'PHP_SELF'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'admin.php'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Location: admin_2fa_login.php'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, modify the original &lt;code&gt;/var/www/html/vicidial/admin.php&lt;/code&gt; to include the 2FA check at the very top (after &lt;code&gt;&amp;lt;?php&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="c1"&gt;// Add this immediately after opening PHP tag&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'admin_2fa_check.php'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ... rest of original admin.php code ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, create a new &lt;code&gt;admin.php&lt;/code&gt; that acts as a wrapper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'admin_2fa_check.php'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'admin.php.bak'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.6 Generate QR Codes for User Enrollment
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;/var/www/html/vicidial/setup_2fa.php&lt;/code&gt; (accessible only to logged-in admins):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nb"&gt;session_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nb"&gt;error_reporting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$db_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db_pass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_DB_PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'asterisk'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$mysqli&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;mysqli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$db_host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$db_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$db_pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$db_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="s1"&gt;'GoogleAuthenticator.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$ga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PHPGangsta_GoogleAuthenticator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Only accessible via POST with proper session&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_METHOD'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Invalid request'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Generate new TOTP secret&lt;/span&gt;
&lt;span class="nv"&gt;$secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ga&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createSecret&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sanitizeInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Username required'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Generate QR code URL&lt;/span&gt;
&lt;span class="nv"&gt;$qr_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ga&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getQRCodeGoogleUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ViciDial Admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$secret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Generate backup codes (10 codes)&lt;/span&gt;
&lt;span class="nv"&gt;$backup_codes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$backup_codes&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bin2hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;random_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$backup_codes_json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$backup_codes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Store in database (not yet enabled until user confirms)&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"
    INSERT INTO vicidial_admin_2fa (username, totp_secret, backup_codes, totp_enabled)
    VALUES (?, ?, ?, 'N')
    ON DUPLICATE KEY UPDATE
        totp_secret = VALUES(totp_secret),
        backup_codes = VALUES(backup_codes),
        totp_enabled = 'N'
"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sss'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$backup_codes_json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Return JSON response&lt;/span&gt;
&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'qr_code_url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$qr_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'secret'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'backup_codes'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$backup_codes&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;sanitizeInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;htmlspecialchars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;ENT_QUOTES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'UTF-8'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.7 Enable 2FA for Specific Users
&lt;/h3&gt;

&lt;p&gt;Run this SQL to enable 2FA for critical admin accounts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_admin_2fa&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;totp_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Y'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'superadmin'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- View current 2FA status&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;totp_enabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_verified&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_date&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_admin_2fa&lt;/span&gt; 
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Monitor 2FA login attempts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"
SELECT username, attempt_type, result, remote_ip, attempt_time 
FROM vicidial_2fa_audit 
WHERE attempt_time &amp;gt;= DATE_SUB(NOW(), INTERVAL 24 HOUR)
ORDER BY attempt_time DESC;
"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 4: Harden Database and Related Access
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Restrict MySQL Access
&lt;/h3&gt;

&lt;p&gt;By default, MySQL listens on all interfaces. Restrict it to localhost:&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/mysql/mysql.conf.d/mysqld.cnf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mysqld]&lt;/span&gt;
&lt;span class="py"&gt;bind-address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1&lt;/span&gt;
&lt;span class="err"&gt;skip-external-locking&lt;/span&gt;

&lt;span class="c"&gt;# Disable DNS resolution (faster, more secure)
&lt;/span&gt;&lt;span class="err"&gt;skip-name-resolve&lt;/span&gt;

&lt;span class="c"&gt;# Disable file-based access
&lt;/span&gt;&lt;span class="err"&gt;skip-load-data-local-infile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart MySQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl restart mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2 Create Least-Privilege Database User
&lt;/h3&gt;

&lt;p&gt;Instead of using root for the web application, create a dedicated user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create new user for web app&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="s1"&gt;'vicidial_web'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;'COMPLEX_PASSWORD_HERE'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Grant minimal privileges&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;asterisk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="s1"&gt;'vicidial_web'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;asterisk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="s1"&gt;'vicidial_web'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;asterisk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vicidial_admin_2fa&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="s1"&gt;'vicidial_web'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;asterisk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vicidial_2fa_audit&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="s1"&gt;'vicidial_web'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Apply changes&lt;/span&gt;
&lt;span class="n"&gt;FLUSH&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Verify&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authentication_string&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'vicidial_web'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;GRANTS&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="s1"&gt;'vicidial_web'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update your PHP scripts to use this user instead of root.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Log All Admin Database Queries
&lt;/h3&gt;

&lt;p&gt;Enable query logging for the asterisk database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;GLOBAL&lt;/span&gt; &lt;span class="n"&gt;general_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ON'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;GLOBAL&lt;/span&gt; &lt;span class="n"&gt;log_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'TABLE'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query log is stored in &lt;code&gt;mysql.general_log&lt;/code&gt; table. To export:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"
SELECT event_time, user_host, command_type, argument 
FROM mysql.general_log 
WHERE db = 'asterisk' AND command_type IN ('Query', 'Execute')
LIMIT 100;
"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /var/log/vicidial-db-queries.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To disable general log (after investigation):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;GLOBAL&lt;/span&gt; &lt;span class="n"&gt;general_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'OFF'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 5: Monitoring and Logging
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Configure Centralized Logging
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;/var/www/html/vicidial/log_admin_events.php&lt;/code&gt;:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
php
&amp;lt;?php
/**
 * Log all admin access events to file and database
 */

function logAdminEvent($event_type, $username, $action, $details = array()) {
    $log_file = '/var/log/vicidial/admin-events.log';

    // Ensure log directory exists
    if (!is_dir('/var/log/vicidial')) {
        mkdir('/var/log/vicidial', 0750, true);
        chown('/var/log/vicidial', 'www-data');
    }

    $timestamp = date('Y-m-d H:i:s');
    $remote_ip = $_SERVER['REMOTE_ADDR'];
    $user_agent = substr($_SERVER['HTTP_USER_AGENT'] ?? 'Unknown', 0, 100);

    $log_entry = sprintf(
        "[%s] Type:%s | User:%s | Action:%s | IP:%s | UA:%s | Details:%s\n",
        $timestamp,
        $event_type,
        $username,
        $action,
        $remote_ip,
        $user_agent,
        json_encode($details)
    );

    // Write to file
    file_put_contents($log_file, $log_entry, FILE_APPEND | LOCK_EX);

    // Also log to syslog
    openlog('vicidial-admin', LOG_PID | LOG_PERROR, LOG_LOCAL0);
    sys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
    <item>
      <title>ViciDial Time Zone Routing — Call at the Right Local Time</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Tue, 21 Jul 2026 03:30:02 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/vicidial-time-zone-routing-call-at-the-right-local-time-22pp</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/vicidial-time-zone-routing-call-at-the-right-local-time-22pp</guid>
      <description>&lt;h1&gt;
  
  
  ViciDial Time Zone Routing — Call at the Right Local Time
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Master ViciDial's timezone routing capabilities to deliver calls to the right prospect at the right local time, increasing answer rates and compliance with do-not-call regulations.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Time zone routing in ViciDial is one of the most underutilized yet powerful features for improving contact center efficiency. Whether you're running a multi-state sales operation, managing customer support across regions, or navigating strict calling hour restrictions, intelligent timezone-aware call delivery directly impacts your metrics.&lt;/p&gt;

&lt;p&gt;This tutorial covers the practical implementation of time zone routing in ViciDial/Asterisk production environments—from database configuration and campaign setup through real-time call routing logic and validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before implementing timezone routing, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ViciDial 2.13+&lt;/strong&gt; installed and fully functional (2.14.1+ recommended for latest timezone fixes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asterisk 13+&lt;/strong&gt; with proper TLS/SIP configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MariaDB/MySQL&lt;/strong&gt; root or administrative access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH access&lt;/strong&gt; to your ViciDial server with sudo privileges&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Existing campaigns&lt;/strong&gt; configured with at least one carrier/trunk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic understanding&lt;/strong&gt; of ViciDial admin interface, MySQL, and Asterisk dialplan&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timezone database files&lt;/strong&gt; at &lt;code&gt;/usr/share/zoneinfo/&lt;/code&gt; (standard on Linux)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test phone numbers&lt;/strong&gt; across multiple time zones for validation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding ViciDial's Time Zone Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How ViciDial Handles Time Zones
&lt;/h3&gt;

&lt;p&gt;ViciDial stores timezone information at multiple levels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Server timezone&lt;/strong&gt; — System timezone on the ViciDial machine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Campaign timezone&lt;/strong&gt; — Default timezone for a campaign&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lead timezone&lt;/strong&gt; — Individual prospect timezone (overrides campaign setting)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Carrier timezone&lt;/strong&gt; — Routing rules per carrier&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent timezone&lt;/strong&gt; — Desktop agent location (for callback scenarios)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The ViciDial database uses the &lt;code&gt;vicidial_campaigns&lt;/code&gt; table to store campaign-level timezone settings, and &lt;code&gt;vicidial_list&lt;/code&gt; table stores lead-specific timezone data. When a call is queued, the dialer checks these values against the current time to determine if a call should be placed or held.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Database Tables
&lt;/h3&gt;

&lt;p&gt;Three tables handle timezone logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vicidial_campaigns.campaign_timezone (varchar 45)
vicidial_campaigns.lead_filter_level (int, affects timezone logic)
vicidial_list.lead_timezone (varchar 45)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dialer processes leads in order, but only initiates calls when the prospect's local time falls within allowed calling hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Campaign-Level Time Zone Settings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Set Default Campaign Timezone
&lt;/h3&gt;

&lt;p&gt;Access your ViciDial admin interface and navigate to &lt;strong&gt;Campaigns&lt;/strong&gt; → &lt;strong&gt;Edit Campaign&lt;/strong&gt;. Or directly update the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;campaign_timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'America/New_York'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'TEST_CAMP'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid timezone strings use the IANA timezone database format. Common examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;America/New_York
America/Chicago
America/Denver
America/Los_Angeles
America/Anchorage
Pacific/Honolulu
Europe/London
Europe/Paris
Asia/Tokyo
Australia/Sydney
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Configure Calling Hours
&lt;/h3&gt;

&lt;p&gt;Calling hours are stored in the &lt;code&gt;vicidial_campaigns&lt;/code&gt; table in two fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;start_call_hour&lt;/code&gt; (integer 0-23)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;end_call_hour&lt;/code&gt; (integer 0-23)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Set these to your business hours in the campaign timezone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;start_call_hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;end_call_hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'TEST_CAMP'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This restricts all calls to 9:00 AM – 8:00 PM prospect local time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Enable Hour-Based Lead Filtering
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;lead_filter_level&lt;/code&gt; controls how aggressively the dialer filters calls by time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;lead_filter_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'TEST_CAMP'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt; — No time-based filtering (call anytime)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt; — Filter by start/end hours only&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2&lt;/code&gt; — Filter by hours + respect timezone (recommended)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3&lt;/code&gt; — Strict mode (filter even if uncertain)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For production multi-timezone campaigns, use &lt;strong&gt;level 2&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lead-Level Timezone Assignment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Bulk Assign Timezones
&lt;/h3&gt;

&lt;p&gt;If your leads import without timezone data, assign them based on area codes or geographic data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Assign New York timezone to 212 area code&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;lead_timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'America/New_York'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'212%'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'TEST_CAMP'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Assign Pacific timezone to 415 area code&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;lead_timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'America/Los_Angeles'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'415%'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'TEST_CAMP'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Import Timezones from CSV
&lt;/h3&gt;

&lt;p&gt;When importing leads via ViciDial's upload interface, include a &lt;code&gt;lead_timezone&lt;/code&gt; column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;phone_number,first_name,last_name,lead_timezone
5551234567,John,Doe,America/New_York
7075551234,Jane,Smith,America/Los_Angeles
3125559876,Bob,Johnson,America/Chicago
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ViciDial upload processor automatically populates the &lt;code&gt;lead_timezone&lt;/code&gt; field.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify Timezone Data
&lt;/h3&gt;

&lt;p&gt;Check that timezones are properly assigned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lead_timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;DATE_FORMAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;'%H:%i:%s'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="s1"&gt;'Server_Time'&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'TEST_CAMP'&lt;/span&gt; 
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| lead_id | phone_number | lead_timezone       | Server_Time |
|---------|--------------|---------------------|-------------|
| 1       | 5551234567   | America/New_York    | 14:30:45    |
| 2       | 7075556789   | America/Los_Angeles | 14:30:45    |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Carrier and Trunk Routing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Route Calls by Time Zone and Carrier
&lt;/h3&gt;

&lt;p&gt;For operations with geographically distributed carriers, route calls through region-specific trunks to minimize latency and comply with local regulations:&lt;/p&gt;

&lt;p&gt;Edit your Asterisk extensions file at &lt;code&gt;/etc/asterisk/extensions-vicidial.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;vicidial&lt;/span&gt;-&lt;span class="n"&gt;dialer&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;ViciDial&lt;/span&gt; &lt;span class="n"&gt;Call&lt;/span&gt; &lt;span class="n"&gt;Router&lt;/span&gt; - &lt;span class="n"&gt;Timezone&lt;/span&gt; &lt;span class="n"&gt;Aware&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;TIMEZONE&lt;/span&gt;=${&lt;span class="n"&gt;DB&lt;/span&gt;(&lt;span class="n"&gt;vicidial&lt;/span&gt;/&lt;span class="n"&gt;lead&lt;/span&gt;/${&lt;span class="n"&gt;LEADID&lt;/span&gt;}/&lt;span class="n"&gt;timezone&lt;/span&gt;)})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;GotoIf&lt;/span&gt;($[&lt;span class="s2"&gt;"${TIMEZONE}"&lt;/span&gt; = &lt;span class="s2"&gt;"America/New_York"&lt;/span&gt;]?&lt;span class="n"&gt;route_ny&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;GotoIf&lt;/span&gt;($[&lt;span class="s2"&gt;"${TIMEZONE}"&lt;/span&gt; = &lt;span class="s2"&gt;"America/Los_Angeles"&lt;/span&gt;]?&lt;span class="n"&gt;route_la&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;GotoIf&lt;/span&gt;($[&lt;span class="s2"&gt;"${TIMEZONE}"&lt;/span&gt; = &lt;span class="s2"&gt;"America/Chicago"&lt;/span&gt;]?&lt;span class="n"&gt;route_chi&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Goto&lt;/span&gt;(&lt;span class="n"&gt;route_default&lt;/span&gt;)

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;route_ny&lt;/span&gt;),&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;OUTBOUND_ROUTE&lt;/span&gt;=&lt;span class="n"&gt;NY_CARRIER&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;OUTBOUND_TRUNK&lt;/span&gt;=&lt;span class="n"&gt;SIP&lt;/span&gt;/&lt;span class="n"&gt;ny&lt;/span&gt;-&lt;span class="n"&gt;carrier&lt;/span&gt;-&lt;span class="n"&gt;trunk&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Goto&lt;/span&gt;(&lt;span class="n"&gt;dial_lead&lt;/span&gt;)

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;route_la&lt;/span&gt;),&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;OUTBOUND_ROUTE&lt;/span&gt;=&lt;span class="n"&gt;LA_CARRIER&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;OUTBOUND_TRUNK&lt;/span&gt;=&lt;span class="n"&gt;SIP&lt;/span&gt;/&lt;span class="n"&gt;la&lt;/span&gt;-&lt;span class="n"&gt;carrier&lt;/span&gt;-&lt;span class="n"&gt;trunk&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Goto&lt;/span&gt;(&lt;span class="n"&gt;dial_lead&lt;/span&gt;)

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;route_chi&lt;/span&gt;),&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;OUTBOUND_ROUTE&lt;/span&gt;=&lt;span class="n"&gt;CHI_CARRIER&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;OUTBOUND_TRUNK&lt;/span&gt;=&lt;span class="n"&gt;SIP&lt;/span&gt;/&lt;span class="n"&gt;chi&lt;/span&gt;-&lt;span class="n"&gt;carrier&lt;/span&gt;-&lt;span class="n"&gt;trunk&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Goto&lt;/span&gt;(&lt;span class="n"&gt;dial_lead&lt;/span&gt;)

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;route_default&lt;/span&gt;),&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;OUTBOUND_ROUTE&lt;/span&gt;=&lt;span class="n"&gt;DEFAULT&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;OUTBOUND_TRUNK&lt;/span&gt;=&lt;span class="n"&gt;SIP&lt;/span&gt;/&lt;span class="n"&gt;default&lt;/span&gt;-&lt;span class="n"&gt;carrier&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Goto&lt;/span&gt;(&lt;span class="n"&gt;dial_lead&lt;/span&gt;)

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;dial_lead&lt;/span&gt;),&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Dialing&lt;/span&gt; ${&lt;span class="n"&gt;EXTEN&lt;/span&gt;} &lt;span class="n"&gt;via&lt;/span&gt; ${&lt;span class="n"&gt;OUTBOUND_TRUNK&lt;/span&gt;})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Dial&lt;/span&gt;(${&lt;span class="n"&gt;OUTBOUND_TRUNK&lt;/span&gt;}/${&lt;span class="n"&gt;EXTEN&lt;/span&gt;},&lt;span class="m"&gt;45&lt;/span&gt;,&lt;span class="n"&gt;tT&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;h&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Call&lt;/span&gt; &lt;span class="n"&gt;ended&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload Asterisk after changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing Do-Not-Call Hour Compliance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Set Compliant Calling Windows
&lt;/h3&gt;

&lt;p&gt;The most critical use case is respecting local calling hour laws. Update campaigns to enforce compliance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Sales campaign: 9 AM - 8 PM prospect time&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;start_call_hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;end_call_hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;campaign_timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'America/New_York'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lead_filter_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;allow_inbound_call_queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'0'&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'SALES_USA'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Support campaign: 8 AM - 6 PM&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;start_call_hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;end_call_hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;campaign_timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'America/Chicago'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lead_filter_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'SUPPORT_USA'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validate Time Zone Logic
&lt;/h3&gt;

&lt;p&gt;Run this query to identify leads that would be called outside allowed hours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_call_hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end_call_hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;CONVERT_TZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time_zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CONCAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'+00:00'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;CONVERT_TZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time_zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CONCAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_timezone&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;prospect_local_time&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; &lt;span class="n"&gt;vc&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'SALES_USA'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'NEW'&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This identifies prospects and their current local times relative to calling hour restrictions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Example: Multi-Region Campaign
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Complete Configuration Walkthrough
&lt;/h3&gt;

&lt;p&gt;Scenario: Running a sales campaign across all US time zones with region-specific carriers.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Create Campaign with Timezone Settings
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;campaign_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;campaign_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;campaign_timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;start_call_hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;end_call_hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;lead_filter_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;allow_inbound_call_queue&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'MULTI_US'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'Multi-Region USA Sales'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'America/Chicago'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'N'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Assign Timezones to Leads
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Batch assign by area code pattern&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;lead_timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'America/New_York'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="n"&gt;REGEXP&lt;/span&gt; &lt;span class="s1"&gt;'^(201|202|203|212|215|516|718|845|914)'&lt;/span&gt; 
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'MULTI_US'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;lead_timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'America/Chicago'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="n"&gt;REGEXP&lt;/span&gt; &lt;span class="s1"&gt;'^(217|312|618|630|708|773|815)'&lt;/span&gt; 
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'MULTI_US'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;lead_timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'America/Denver'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="n"&gt;REGEXP&lt;/span&gt; &lt;span class="s1"&gt;'^(303|719|720|970)'&lt;/span&gt; 
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'MULTI_US'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;lead_timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'America/Los_Angeles'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="n"&gt;REGEXP&lt;/span&gt; &lt;span class="s1"&gt;'^(209|213|310|408|415|510|619|650|707|714|760|805|818|916|949)'&lt;/span&gt; 
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'MULTI_US'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Verify Distribution
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;lead_timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;lead_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;sample_number&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'MULTI_US'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;lead_timezone&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;lead_timezone&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| lead_timezone       | lead_count | sample_number |
|---------------------|------------|---------------|
| America/Chicago     | 1250       | 3125551001    |
| America/Denver      | 450        | 3035551234    |
| America/Los_Angeles | 2100       | 4155559876    |
| America/New_York    | 1800       | 2125554321    |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring and Logging
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Check Active Call Distribution by Timezone
&lt;/h3&gt;

&lt;p&gt;Real-time view of which timezones are currently receiving calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uniqueid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;active_calls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DATE_FORMAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'%H:%i:%s'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;earliest_call&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_closer_log&lt;/span&gt; &lt;span class="n"&gt;vcl&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'MULTI_US'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_timezone&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;active_calls&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Audit Call Attempts Against Calling Hours
&lt;/h3&gt;

&lt;p&gt;Identify any calls placed outside compliant windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_call_hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end_call_hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CONVERT_TZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time_zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_timezone&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;call_hour_local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;CASE&lt;/span&gt; 
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CONVERT_TZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time_zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_timezone&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_call_hour&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CONVERT_TZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time_zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_timezone&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end_call_hour&lt;/span&gt; 
    &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'COMPLIANT'&lt;/span&gt;
    &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'NON-COMPLIANT'&lt;/span&gt;
  &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;compliance_status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_closer_log&lt;/span&gt; &lt;span class="n"&gt;vcl&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; &lt;span class="n"&gt;vc&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'MULTI_US'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="n"&gt;compliance_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'NON-COMPLIANT'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;vcl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable Timezone Logging in Asterisk
&lt;/h3&gt;

&lt;p&gt;Add verbose logging to track timezone decisions in the dialer:&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/logger.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;logfiles&lt;/span&gt;]
&lt;span class="n"&gt;messages&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;notice&lt;/span&gt;,&lt;span class="n"&gt;warning&lt;/span&gt;,&lt;span class="n"&gt;error&lt;/span&gt;,&lt;span class="n"&gt;verbose&lt;/span&gt;(&lt;span class="m"&gt;3&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then view logs in real-time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; timezone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced: Custom Timezone Logic with AGI
&lt;/h2&gt;

&lt;p&gt;For sophisticated routing beyond standard campaigns, implement an Asterisk Gateway Interface (AGI) script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/perl&lt;/span&gt;
&lt;span class="c1"&gt;# /usr/share/astguiclient/agi-bin/timezone-router.agi&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;DBI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;DateTime::&lt;/span&gt;&lt;span class="nv"&gt;TimeZone&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;%ENV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="bp"&gt;STDIN&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;chomp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;last&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^([\w\d_]*):\s?(.*)$/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;agi_output&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="bp"&gt;STDOUT&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$string&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
    &lt;span class="nb"&gt;warn&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$string&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$lead_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$phone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="p"&gt;'';&lt;/span&gt;

&lt;span class="c1"&gt;# Connect to ViciDial database&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$dbh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;DBI&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;DBI:mysql:asterisk:localhost&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;')&lt;/span&gt; 
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;die&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cannot connect: &lt;/span&gt;&lt;span class="si"&gt;$DBI&lt;/span&gt;&lt;span class="s2"&gt;::errstr&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;

&lt;span class="c1"&gt;# Fetch lead timezone&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$sth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$dbh&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;SELECT lead_timezone FROM vicidial_list WHERE lead_id = ?&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;
&lt;span class="nv"&gt;$sth&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$lead_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tz_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$sth&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;fetchrow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$sth&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tz_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;agi_output&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;SET VARIABLE TIMEZONE_VALID 0&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
    &lt;span class="nv"&gt;$dbh&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Get current time in prospect timezone&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$tz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;DateTime::&lt;/span&gt;&lt;span class="nv"&gt;TimeZone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$tz_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;time_zone&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$tz&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$now&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$dow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$now&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;day_of_week&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;# 1=Monday, 7=Sunday&lt;/span&gt;

&lt;span class="c1"&gt;# Fetch campaign calling hours&lt;/span&gt;
&lt;span class="nv"&gt;$sth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$dbh&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;
    SELECT start_call_hour, end_call_hour 
    FROM vicidial_campaigns 
    WHERE campaign_id = (SELECT campaign_id FROM vicidial_list WHERE lead_id = ?)
&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;
&lt;span class="nv"&gt;$sth&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$lead_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$start_hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$end_hour&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$sth&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;fetchrow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$sth&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;# Check if within calling hours&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$can_call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defined&lt;/span&gt; &lt;span class="nv"&gt;$start_hour&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt; &lt;span class="nv"&gt;$end_hour&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$can_call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hour&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;$start_hour&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$hour&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$end_hour&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;agi_output&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;SET VARIABLE LEAD_TIMEZONE &lt;/span&gt;&lt;span class="si"&gt;$tz_name&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
&lt;span class="nv"&gt;agi_output&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;SET VARIABLE PROSPECT_LOCAL_HOUR &lt;/span&gt;&lt;span class="si"&gt;$hour&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
&lt;span class="nv"&gt;agi_output&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;SET VARIABLE CAN_CALL &lt;/span&gt;&lt;span class="si"&gt;$can_call&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
&lt;span class="nv"&gt;agi_output&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;SET VARIABLE CALL_START_HOUR &lt;/span&gt;&lt;span class="si"&gt;$start_hour&lt;/span&gt;&lt;span class="p"&gt;")&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt; &lt;span class="nv"&gt;$start_hour&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$dbh&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute from dialplan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;AGI&lt;/span&gt;(&lt;span class="n"&gt;agi&lt;/span&gt;-&lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;timezone&lt;/span&gt;-&lt;span class="n"&gt;router&lt;/span&gt;.&lt;span class="n"&gt;agi&lt;/span&gt;,${&lt;span class="n"&gt;LEADID&lt;/span&gt;},${&lt;span class="n"&gt;EXTEN&lt;/span&gt;})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;GotoIf&lt;/span&gt;($[${&lt;span class="n"&gt;CAN_CALL&lt;/span&gt;} = &lt;span class="m"&gt;1&lt;/span&gt;]?&lt;span class="n"&gt;proceed&lt;/span&gt;:&lt;span class="n"&gt;blocked&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;proceed&lt;/span&gt;),&lt;span class="n"&gt;Dial&lt;/span&gt;(&lt;span class="n"&gt;SIP&lt;/span&gt;/&lt;span class="n"&gt;default&lt;/span&gt;-&lt;span class="n"&gt;carrier&lt;/span&gt;/${&lt;span class="n"&gt;EXTEN&lt;/span&gt;},&lt;span class="m"&gt;45&lt;/span&gt;,&lt;span class="n"&gt;tT&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;blocked&lt;/span&gt;),&lt;span class="n"&gt;Busy&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem: Calls Being Placed Outside Calling Hours
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; ViciDial is calling prospects outside configured windows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; &lt;code&gt;lead_filter_level&lt;/code&gt; is set to 0 or timezone data is missing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check current filter level&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-pcron&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"
SELECT campaign_id, lead_filter_level 
FROM vicidial_campaigns 
WHERE campaign_id = 'TEST_CAMP';"&lt;/span&gt;

&lt;span class="c"&gt;# Update to enforce filtering&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-pcron&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"
UPDATE vicidial_campaigns 
SET lead_filter_level = 2 
WHERE campaign_id = 'TEST_CAMP';"&lt;/span&gt;

&lt;span class="c"&gt;# Verify all leads have timezone data&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-pcron&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"
SELECT COUNT(*) as leads_without_tz 
FROM vicidial_list 
WHERE campaign_id = 'TEST_CAMP' 
AND lead_timezone IS NULL;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If leads lack timezone, assign defaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;lead_timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'America/New_York'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'TEST_CAMP'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;lead_timezone&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem: Dialer Skipping Too Many Leads
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Dialer queue moving slowly; many leads skipped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; Timezone filtering is too restrictive or timezone database is outdated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check for daylight saving time transitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List recent timezone changes&lt;/span&gt;
zdump &lt;span class="nt"&gt;-v&lt;/span&gt; /etc/localtime | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"2024"&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update timezone database if stale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--only-upgrade&lt;/span&gt; tzdata
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify dialer is using correct server timezone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check server timezone&lt;/span&gt;
timedatectl

&lt;span class="c"&gt;# Verify MySQL timezone&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-pcron&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SELECT @@session.time_zone, @@global.time_zone;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set explicit timezone in MySQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;GLOBAL&lt;/span&gt; &lt;span class="n"&gt;time_zone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'UTC'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="n"&gt;time_zone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'UTC'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem: Inconsistent Results Between Predictions and Actual Call Times
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Query predicts calls should be blocked, but they're being placed anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; Server timezone differs from database timezone assumptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Normalize to UTC in all queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_call_hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end_call_hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;-- Convert server time to UTC first, then to prospect timezone&lt;/span&gt;
  &lt;span class="n"&gt;CONVERT_TZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time_zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'UTC'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;utc_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;DATE_FORMAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;CONVERT_TZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;CONVERT_TZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time_zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'UTC'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
      &lt;span class="s1"&gt;'UTC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_timezone&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="s1"&gt;'%H'&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;prospect_hour_24&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_list&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; &lt;span class="n"&gt;vc&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'TEST_CAMP'&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem: Timezone in Asterisk Logs Shows as UTC, Not Prospect Time
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; CDR timestamps don't reflect prospect local time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; This is expected behavior. ViciDial stores all times in server time (usually UTC) and converts during reporting. To see prospect time in logs, add a custom variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;PROSPECT_TZ_TIME&lt;/span&gt;=${&lt;span class="n"&gt;DB&lt;/span&gt;(&lt;span class="n"&gt;vicidial&lt;/span&gt;/&lt;span class="n"&gt;lead&lt;/span&gt;/${&lt;span class="n"&gt;LEADID&lt;/span&gt;}/&lt;span class="n"&gt;local_time&lt;/span&gt;)})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Calling&lt;/span&gt; ${&lt;span class="n"&gt;EXTEN&lt;/span&gt;} &lt;span class="n"&gt;in&lt;/span&gt; ${&lt;span class="n"&gt;PROSPECT_TZ_TIME&lt;/span&gt;})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem: CONVERT_TZ() Returns NULL
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; MySQL CONVERT_TZ function returning NULL in queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; MySQL timezone tables not populated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Load timezone data into MySQL&lt;/span&gt;
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; mysql

&lt;span class="c"&gt;# Restart MySQL&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart mysql

&lt;span class="c"&gt;# Verify load&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-pcron&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SELECT COUNT(*) FROM mysql.time_zone;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Should return a count &amp;gt; 500. If it's still NULL, ensure database has proper permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mysql_upgrade &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Implementing timezone routing in ViciDial requires attention to five key layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Campaign Configuration&lt;/strong&gt; — Set &lt;code&gt;campaign_timezone&lt;/code&gt;, calling hours, and &lt;code&gt;lead_filter_level&lt;/code&gt; to 2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lead Data&lt;/strong&gt; — Populate &lt;code&gt;lead_timezone&lt;/code&gt; accurately via import or bulk update&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Timezone&lt;/strong&gt; — Ensure MySQL and server timezones are properly synchronized&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dialplan Logic&lt;/strong&gt; — Route calls through appropriate carriers and apply AGI scripts for complex rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring&lt;/strong&gt; — Audit compliance and validate calls align with configured windows&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always use &lt;code&gt;lead_filter_level = 2&lt;/code&gt; for production multi-timezone campaigns&lt;/li&gt;
&lt;li&gt;Populate &lt;code&gt;lead_timezone&lt;/code&gt; at import time; use area code patterns as fallback&lt;/li&gt;
&lt;li&gt;Test timezone logic with direct SQL queries before enabling on live campaigns&lt;/li&gt;
&lt;li&gt;Monitor dialer logs hourly during daylight saving transitions&lt;/li&gt;
&lt;li&gt;Implement compliance audits weekly to catch misconfigured leads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With proper configuration, ViciDial's timezone routing increases answer rates by 15-25% while ensuring 100% compliance with calling hour regulations across all US time zones and international markets.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
    <item>
      <title>Asterisk Music on Hold — Custom Audio &amp; Per-Queue MOH</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Mon, 20 Jul 2026 03:30:03 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/asterisk-music-on-hold-custom-audio-per-queue-moh-34g8</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/asterisk-music-on-hold-custom-audio-per-queue-moh-34g8</guid>
      <description>&lt;h1&gt;
  
  
  Asterisk Music on Hold — Custom Audio &amp;amp; Per-Queue MOH
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Master custom music on hold setup, per-queue MOH routing, and troubleshooting for high-volume ViciDial production systems&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before implementing custom MOH in Asterisk/ViciDial, verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asterisk version 11+&lt;/strong&gt; (tested on 13.x, 16.x, 18.x)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ViciDial 2.14+&lt;/strong&gt; running on CentOS 7/8 or Ubuntu 18.04+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root or sudo access&lt;/strong&gt; to the Asterisk server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audio files in WAV or MP3 format&lt;/strong&gt; (MOH files must be 8 kHz, 16-bit mono for optimal performance)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sufficient disk space&lt;/strong&gt; in &lt;code&gt;/var/lib/asterisk/moh/&lt;/code&gt; (typically 500 MB–2 GB depending on queue count)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asterisk service control&lt;/strong&gt;: &lt;code&gt;systemctl restart asterisk&lt;/code&gt; or &lt;code&gt;/etc/init.d/asterisk restart&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database credentials&lt;/strong&gt; for ViciDial (&lt;code&gt;root&lt;/code&gt; or dedicated &lt;code&gt;asterisk&lt;/code&gt; user)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understanding of Asterisk dialplan basics&lt;/strong&gt; and conf file syntax&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run &lt;code&gt;asterisk -rx "core show version"&lt;/code&gt; to confirm Asterisk is running and accessible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Asterisk Music on Hold Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How MOH Works in Asterisk
&lt;/h3&gt;

&lt;p&gt;Music on Hold is triggered when a call enters a queue, is placed on hold, or meets a &lt;code&gt;Wait()&lt;/code&gt; application. Asterisk uses:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;MOH Classes&lt;/strong&gt; — defined in &lt;code&gt;musiconhold.conf&lt;/code&gt;, each with a directory or file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MOH Selection&lt;/strong&gt; — via dialplan (&lt;code&gt;SetMusicOnHold()&lt;/code&gt;) or queue configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MOH Playback&lt;/strong&gt; — native playback to calling party via audio stream&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MOH Generators&lt;/strong&gt; — optional generator for on-the-fly tone/silence generation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Default MOH directory: &lt;code&gt;/var/lib/asterisk/moh/&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ViciDial-Specific MOH Integration
&lt;/h3&gt;

&lt;p&gt;ViciDial uses MOH at these points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Queue hold&lt;/strong&gt; — when agent is unavailable or queue is full&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Callback queues&lt;/strong&gt; — while customer waits for agent callback&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IVR prompts&lt;/strong&gt; — background music during menu selection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transfer hold&lt;/strong&gt; — during blind/attended transfers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Core MOH Configuration Files
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Primary Configuration: musiconhold.conf
&lt;/h3&gt;

&lt;p&gt;Located at &lt;code&gt;/etc/asterisk/musiconhold.conf&lt;/code&gt;, this is the master MOH configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
; &lt;span class="n"&gt;Default&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="n"&gt;specified&lt;/span&gt;
&lt;span class="n"&gt;default&lt;/span&gt;=&lt;span class="n"&gt;default&lt;/span&gt;

; &lt;span class="n"&gt;Directory&lt;/span&gt; &lt;span class="n"&gt;scanning&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; - &lt;span class="n"&gt;rescan&lt;/span&gt; &lt;span class="n"&gt;disk&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="n"&gt;automatically&lt;/span&gt;
; &lt;span class="n"&gt;Useful&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dynamic&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt;
&lt;span class="n"&gt;moh_quiet&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;

[&lt;span class="n"&gt;default&lt;/span&gt;]
; &lt;span class="n"&gt;Standard&lt;/span&gt; &lt;span class="n"&gt;MP3&lt;/span&gt; &lt;span class="n"&gt;playback&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;quieting&lt;/span&gt;
&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="n"&gt;directory&lt;/span&gt;=/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;default&lt;/span&gt;
; &lt;span class="n"&gt;Quieting&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt; (&lt;span class="m"&gt;0&lt;/span&gt;-&lt;span class="m"&gt;100&lt;/span&gt;)
&lt;span class="n"&gt;quiet&lt;/span&gt;=&lt;span class="m"&gt;100&lt;/span&gt;

[&lt;span class="n"&gt;on&lt;/span&gt;-&lt;span class="n"&gt;hold&lt;/span&gt;]
&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="n"&gt;directory&lt;/span&gt;=/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;on&lt;/span&gt;-&lt;span class="n"&gt;hold&lt;/span&gt;
&lt;span class="n"&gt;quiet&lt;/span&gt;=&lt;span class="m"&gt;100&lt;/span&gt;

[&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;]
&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="n"&gt;directory&lt;/span&gt;=/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;sales&lt;/span&gt;
&lt;span class="n"&gt;quiet&lt;/span&gt;=&lt;span class="m"&gt;100&lt;/span&gt;

[&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;support&lt;/span&gt;]
&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="n"&gt;directory&lt;/span&gt;=/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;support&lt;/span&gt;
&lt;span class="n"&gt;quiet&lt;/span&gt;=&lt;span class="m"&gt;100&lt;/span&gt;

[&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;billing&lt;/span&gt;]
&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="n"&gt;directory&lt;/span&gt;=/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;billing&lt;/span&gt;
&lt;span class="n"&gt;quiet&lt;/span&gt;=&lt;span class="m"&gt;100&lt;/span&gt;

[&lt;span class="n"&gt;silence&lt;/span&gt;]
; &lt;span class="n"&gt;Silent&lt;/span&gt; &lt;span class="n"&gt;MOH&lt;/span&gt; - &lt;span class="n"&gt;useful&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;internal&lt;/span&gt; &lt;span class="n"&gt;transfers&lt;/span&gt;
&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;silence&lt;/span&gt;

[&lt;span class="n"&gt;tone&lt;/span&gt;]
; &lt;span class="n"&gt;Audible&lt;/span&gt; &lt;span class="n"&gt;ringback&lt;/span&gt; &lt;span class="n"&gt;tone&lt;/span&gt;
&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="n"&gt;directory&lt;/span&gt;=/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;tone&lt;/span&gt;
&lt;span class="n"&gt;quiet&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Parameters:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mode=files&lt;/code&gt; — use audio files from directory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mode=mp3&lt;/code&gt; — legacy; use &lt;code&gt;files&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mode=silence&lt;/code&gt; — no audio, prevents dead-air&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;directory=&lt;/code&gt; — absolute path to MOH files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;quiet=100&lt;/code&gt; — reduce volume (0=loud, 100=silent); use 60–80 for balanced audio&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;application=&lt;/code&gt; — for generator-based MOH (DAHDI, SIP)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Extensions Dialplan: extensions-vicidial.conf
&lt;/h3&gt;

&lt;p&gt;Add MOH selection logic to your dialplan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;]
; &lt;span class="n"&gt;Example&lt;/span&gt;: &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;MOH&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Incoming&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; ${&lt;span class="n"&gt;CALLERID&lt;/span&gt;(&lt;span class="n"&gt;num&lt;/span&gt;)})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;SetMusicOnHold&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Queue&lt;/span&gt;(&lt;span class="n"&gt;sales&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;,&lt;span class="n"&gt;t&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Support&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;SetMusicOnHold&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;support&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Queue&lt;/span&gt;(&lt;span class="n"&gt;support&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;,&lt;span class="n"&gt;t&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Queue Configuration: queues.conf
&lt;/h3&gt;

&lt;p&gt;ViciDial typically manages queues via database, but direct &lt;code&gt;queues.conf&lt;/code&gt; configuration is still used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;sales&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;]
&lt;span class="n"&gt;strategy&lt;/span&gt;=&lt;span class="n"&gt;linear&lt;/span&gt;
&lt;span class="n"&gt;timeout&lt;/span&gt;=&lt;span class="m"&gt;30&lt;/span&gt;
&lt;span class="n"&gt;retry&lt;/span&gt;=&lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;maxlen&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;
; &lt;span class="n"&gt;Music&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;hold&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;
&lt;span class="n"&gt;musiconhold&lt;/span&gt;=&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;
; &lt;span class="n"&gt;Announce&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="n"&gt;every&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;
&lt;span class="n"&gt;announce&lt;/span&gt;-&lt;span class="n"&gt;frequency&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;
&lt;span class="n"&gt;announce&lt;/span&gt;-&lt;span class="n"&gt;round&lt;/span&gt;-&lt;span class="n"&gt;seconds&lt;/span&gt;=&lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;setinterfacevar&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;setqueuevar&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;

[&lt;span class="n"&gt;support&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;]
&lt;span class="n"&gt;strategy&lt;/span&gt;=&lt;span class="n"&gt;linear&lt;/span&gt;
&lt;span class="n"&gt;timeout&lt;/span&gt;=&lt;span class="m"&gt;30&lt;/span&gt;
&lt;span class="n"&gt;retry&lt;/span&gt;=&lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;maxlen&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;musiconhold&lt;/span&gt;=&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;support&lt;/span&gt;
&lt;span class="n"&gt;announce&lt;/span&gt;-&lt;span class="n"&gt;frequency&lt;/span&gt;=&lt;span class="m"&gt;120&lt;/span&gt;

[&lt;span class="n"&gt;callback&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;]
&lt;span class="n"&gt;strategy&lt;/span&gt;=&lt;span class="n"&gt;linear&lt;/span&gt;
&lt;span class="n"&gt;timeout&lt;/span&gt;=&lt;span class="m"&gt;300&lt;/span&gt;
&lt;span class="n"&gt;retry&lt;/span&gt;=&lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;maxlen&lt;/span&gt;=&lt;span class="m"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;musiconhold&lt;/span&gt;=&lt;span class="n"&gt;on&lt;/span&gt;-&lt;span class="n"&gt;hold&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Setting Up Per-Queue Music on Hold
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create Per-Queue MOH Directories
&lt;/h3&gt;

&lt;p&gt;Establish separate directories for each queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/lib/asterisk/moh/&lt;span class="o"&gt;{&lt;/span&gt;default,sales,support,billing,vip&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; asterisk:asterisk /var/lib/asterisk/moh/
&lt;span class="nb"&gt;chmod &lt;/span&gt;755 /var/lib/asterisk/moh/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Prepare and Validate Audio Files
&lt;/h3&gt;

&lt;p&gt;Audio files must meet specific requirements:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For WAV files:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; original-music.mp3 &lt;span class="nt"&gt;-acodec&lt;/span&gt; pcm_s16le &lt;span class="nt"&gt;-ar&lt;/span&gt; 8000 &lt;span class="nt"&gt;-ac&lt;/span&gt; 1 /var/lib/asterisk/moh/sales/music.wav
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For MP3 files (if using mp3 mode):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; original-music.mp3 &lt;span class="nt"&gt;-acodec&lt;/span&gt; libmp3lame &lt;span class="nt"&gt;-ar&lt;/span&gt; 8000 &lt;span class="nt"&gt;-ac&lt;/span&gt; 1 &lt;span class="nt"&gt;-b&lt;/span&gt;:a 32k /var/lib/asterisk/moh/sales/music.mp3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Batch conversion for directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/tmp/moh-source"&lt;/span&gt;
&lt;span class="nv"&gt;DEST_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/lib/asterisk/moh/sales"&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;file &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SOURCE_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-acodec&lt;/span&gt; pcm_s16le &lt;span class="nt"&gt;-ar&lt;/span&gt; 8000 &lt;span class="nt"&gt;-ac&lt;/span&gt; 1 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEST_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.wav"&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; asterisk:asterisk &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEST_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;644 &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEST_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Validate audio format:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sox /var/lib/asterisk/moh/sales/music.wav &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nb"&gt;stat&lt;/span&gt;
&lt;span class="c"&gt;# Output should show: Sample Rate 8000, Channels 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Update musiconhold.conf with Queue-Specific Entries
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;]
&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="n"&gt;directory&lt;/span&gt;=/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;sales&lt;/span&gt;
&lt;span class="n"&gt;quiet&lt;/span&gt;=&lt;span class="m"&gt;70&lt;/span&gt;

[&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;support&lt;/span&gt;]
&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="n"&gt;directory&lt;/span&gt;=/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;support&lt;/span&gt;
&lt;span class="n"&gt;quiet&lt;/span&gt;=&lt;span class="m"&gt;70&lt;/span&gt;

[&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;billing&lt;/span&gt;]
&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="n"&gt;directory&lt;/span&gt;=/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;billing&lt;/span&gt;
&lt;span class="n"&gt;quiet&lt;/span&gt;=&lt;span class="m"&gt;70&lt;/span&gt;

[&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;vip&lt;/span&gt;]
&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="n"&gt;directory&lt;/span&gt;=/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;vip&lt;/span&gt;
&lt;span class="n"&gt;quiet&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Configure Queues with MOH Class Assignment
&lt;/h3&gt;

&lt;p&gt;Edit or create &lt;code&gt;/etc/asterisk/queues.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="n"&gt;shared_lastcall&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;monitor&lt;/span&gt;-&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;MixMonitor&lt;/span&gt;

[&lt;span class="n"&gt;sales&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;]
&lt;span class="n"&gt;strategy&lt;/span&gt;=&lt;span class="n"&gt;linear&lt;/span&gt;
&lt;span class="n"&gt;timeout&lt;/span&gt;=&lt;span class="m"&gt;30&lt;/span&gt;
&lt;span class="n"&gt;retry&lt;/span&gt;=&lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;musiconhold&lt;/span&gt;=&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;
&lt;span class="n"&gt;autofill&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;setinterfacevar&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;setqueuevar&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;announce&lt;/span&gt;-&lt;span class="n"&gt;frequency&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;
&lt;span class="n"&gt;announce&lt;/span&gt;-&lt;span class="n"&gt;round&lt;/span&gt;-&lt;span class="n"&gt;seconds&lt;/span&gt;=&lt;span class="m"&gt;10&lt;/span&gt;

[&lt;span class="n"&gt;support&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;]
&lt;span class="n"&gt;strategy&lt;/span&gt;=&lt;span class="n"&gt;linear&lt;/span&gt;
&lt;span class="n"&gt;timeout&lt;/span&gt;=&lt;span class="m"&gt;30&lt;/span&gt;
&lt;span class="n"&gt;retry&lt;/span&gt;=&lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;musiconhold&lt;/span&gt;=&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;support&lt;/span&gt;
&lt;span class="n"&gt;autofill&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;setinterfacevar&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;setqueuevar&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;

[&lt;span class="n"&gt;billing&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;]
&lt;span class="n"&gt;strategy&lt;/span&gt;=&lt;span class="n"&gt;ring&lt;/span&gt;
&lt;span class="n"&gt;timeout&lt;/span&gt;=&lt;span class="m"&gt;45&lt;/span&gt;
&lt;span class="n"&gt;retry&lt;/span&gt;=&lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;musiconhold&lt;/span&gt;=&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;billing&lt;/span&gt;
&lt;span class="n"&gt;maxlen&lt;/span&gt;=&lt;span class="m"&gt;50&lt;/span&gt;

[&lt;span class="n"&gt;callback&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;]
&lt;span class="n"&gt;strategy&lt;/span&gt;=&lt;span class="n"&gt;linear&lt;/span&gt;
&lt;span class="n"&gt;timeout&lt;/span&gt;=&lt;span class="m"&gt;300&lt;/span&gt;
&lt;span class="n"&gt;retry&lt;/span&gt;=&lt;span class="m"&gt;15&lt;/span&gt;
&lt;span class="n"&gt;musiconhold&lt;/span&gt;=&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;
&lt;span class="n"&gt;maxlen&lt;/span&gt;=&lt;span class="m"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;joined&lt;/span&gt;-&lt;span class="n"&gt;sound&lt;/span&gt;=&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;youare&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Reload and Test MOH
&lt;/h3&gt;

&lt;p&gt;After configuration changes, reload Asterisk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload musiconhold"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload queues"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload res_musiconhold"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify MOH classes are loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"moh show classes"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;Class&lt;/span&gt;: &lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;
   &lt;span class="n"&gt;Mode&lt;/span&gt;: &lt;span class="n"&gt;files&lt;/span&gt;
   &lt;span class="n"&gt;Directory&lt;/span&gt;: /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;sales&lt;/span&gt;
   &lt;span class="n"&gt;Quieting&lt;/span&gt;: &lt;span class="m"&gt;70&lt;/span&gt;

&lt;span class="n"&gt;Class&lt;/span&gt;: &lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;support&lt;/span&gt;
   &lt;span class="n"&gt;Mode&lt;/span&gt;: &lt;span class="n"&gt;files&lt;/span&gt;
   &lt;span class="n"&gt;Directory&lt;/span&gt;: /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;support&lt;/span&gt;
   &lt;span class="n"&gt;Quieting&lt;/span&gt;: &lt;span class="m"&gt;70&lt;/span&gt;

&lt;span class="n"&gt;Class&lt;/span&gt;: &lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;billing&lt;/span&gt;
   &lt;span class="n"&gt;Mode&lt;/span&gt;: &lt;span class="n"&gt;files&lt;/span&gt;
   &lt;span class="n"&gt;Directory&lt;/span&gt;: /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;asterisk&lt;/span&gt;/&lt;span class="n"&gt;moh&lt;/span&gt;/&lt;span class="n"&gt;billing&lt;/span&gt;
   &lt;span class="n"&gt;Quieting&lt;/span&gt;: &lt;span class="m"&gt;70&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ViciDial Database Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Integrating MOH with ViciDial Queue Tables
&lt;/h3&gt;

&lt;p&gt;ViciDial stores queue configuration in the &lt;code&gt;asterisk&lt;/code&gt; database. Use these tables:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table: vicidial_queues&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_queues&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;queue_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sales-queue'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;queue_id&lt;/code&gt; — unique identifier&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;queue_name&lt;/code&gt; — queue name (matches Asterisk queue)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;queue_maxlen&lt;/code&gt; — max queue depth&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;queue_timeout&lt;/code&gt; — agent ring timeout&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;queue_strategy&lt;/code&gt; — linear, ring, weighted_random&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;music_on_hold&lt;/code&gt; — &lt;strong&gt;MOH class name&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Update ViciDial Queue MOH via SQL:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_queues&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;music_on_hold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'queue-sales'&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;queue_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sales-queue'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_queues&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;music_on_hold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'queue-support'&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;queue_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'support-queue'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_queues&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;music_on_hold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'queue-billing'&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;queue_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'billing-queue'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify via ViciDial web UI:&lt;/strong&gt;&lt;br&gt;
Navigate to &lt;code&gt;/vicidial/admin.php&lt;/code&gt; → &lt;strong&gt;Queues&lt;/strong&gt; → Edit each queue → Set "Music on Hold" dropdown to desired class.&lt;/p&gt;
&lt;h3&gt;
  
  
  Database Sync Script
&lt;/h3&gt;

&lt;p&gt;Create a script to sync MOH settings from database to dialplan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# /usr/local/bin/sync-moh-to-dialplan.sh&lt;/span&gt;

&lt;span class="nv"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"root"&lt;/span&gt;
&lt;span class="nv"&gt;MYSQL_PASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_password"&lt;/span&gt;
&lt;span class="nv"&gt;MYSQL_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"asterisk"&lt;/span&gt;
&lt;span class="nv"&gt;OUTFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/etc/asterisk/queues-vicidial.conf"&lt;/span&gt;

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTFILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
[general]
shared_lastcall=yes
monitor-type=MixMonitor
&lt;/span&gt;&lt;span class="no"&gt;
EOF

&lt;/span&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_PASS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MYSQL_DB&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;SQLEOF&lt;/span&gt;&lt;span class="sh"&gt;' | while read queue_name moh_class; do
SELECT queue_name, COALESCE(music_on_hold, 'default') FROM vicidial_queues WHERE active='Y' ORDER BY queue_name;
&lt;/span&gt;&lt;span class="no"&gt;SQLEOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTFILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;QEOF&lt;/span&gt;&lt;span class="sh"&gt;
[&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;queue_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;]
strategy=linear
timeout=30
retry=5
musiconhold=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;moh_class&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
autofill=yes
setinterfacevar=yes
setqueuevar=yes
&lt;/span&gt;&lt;span class="no"&gt;QEOF

&lt;/span&gt;&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;chown &lt;/span&gt;asterisk:asterisk &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTFILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload queues"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schedule with cron:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 &lt;span class="k"&gt;*&lt;/span&gt;/4 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/bin/sync-moh-to-dialplan.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Advanced MOH Techniques
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dynamic MOH Selection via Channel Variables
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;SetMusicOnHold()&lt;/code&gt; in dialplan to override queue-level MOH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;vicidial&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Dynamic&lt;/span&gt; &lt;span class="n"&gt;MOH&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;CALLER_LANG&lt;/span&gt;=${&lt;span class="n"&gt;CALLERID&lt;/span&gt;(&lt;span class="n"&gt;name&lt;/span&gt;):&lt;span class="m"&gt;0&lt;/span&gt;:&lt;span class="m"&gt;2&lt;/span&gt;})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;GotoIf&lt;/span&gt;($[&lt;span class="s2"&gt;"${CALLER_LANG}"&lt;/span&gt;=&lt;span class="s2"&gt;"ES"&lt;/span&gt;]?&lt;span class="n"&gt;spanish&lt;/span&gt;:&lt;span class="n"&gt;english&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;english&lt;/span&gt;),&lt;span class="n"&gt;SetMusicOnHold&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Goto&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;main&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;spanish&lt;/span&gt;),&lt;span class="n"&gt;SetMusicOnHold&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;-&lt;span class="n"&gt;spanish&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;main&lt;/span&gt;),&lt;span class="n"&gt;Queue&lt;/span&gt;(&lt;span class="n"&gt;sales&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;,&lt;span class="n"&gt;t&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom Voicemail Greeting Integration
&lt;/h3&gt;

&lt;p&gt;Combine MOH with IVR greetings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;announcement&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Queue&lt;/span&gt; &lt;span class="n"&gt;announcement&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;MOH&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;SetMusicOnHold&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;/&lt;span class="n"&gt;position&lt;/span&gt;-&lt;span class="n"&gt;in&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;/&lt;span class="n"&gt;est&lt;/span&gt;-&lt;span class="n"&gt;wait&lt;/span&gt;-&lt;span class="n"&gt;time&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Queue&lt;/span&gt;(&lt;span class="n"&gt;sales&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;,&lt;span class="n"&gt;t&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Silence on Hold for Internal Transfers
&lt;/h3&gt;

&lt;p&gt;For agent-to-agent transfers, use silent MOH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;agent&lt;/span&gt;-&lt;span class="n"&gt;transfer&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; *&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Blind&lt;/span&gt; &lt;span class="n"&gt;transfer&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; ${&lt;span class="n"&gt;ARG1&lt;/span&gt;})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; *&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;SetMusicOnHold&lt;/span&gt;(&lt;span class="n"&gt;silence&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; *&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Blind&lt;/span&gt; &lt;span class="n"&gt;Transfer&lt;/span&gt;(${&lt;span class="n"&gt;ARG1&lt;/span&gt;})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; *&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conditional MOH Based on Queue Depth
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;vicidial&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Queue&lt;/span&gt; ${&lt;span class="n"&gt;QUEUENAME&lt;/span&gt;} &lt;span class="n"&gt;check&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Queue&lt;/span&gt;(&lt;span class="n"&gt;sales&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;,&lt;span class="n"&gt;t&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;GotoIf&lt;/span&gt;($[&lt;span class="s2"&gt;"${QUEUE_MEMBER_COUNT}"&lt;/span&gt;&amp;gt;&lt;span class="s2"&gt;"5"&lt;/span&gt;]?&lt;span class="n"&gt;busy&lt;/span&gt;:&lt;span class="n"&gt;available&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;available&lt;/span&gt;),&lt;span class="n"&gt;SetMusicOnHold&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Goto&lt;/span&gt;(&lt;span class="n"&gt;end&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;busy&lt;/span&gt;),&lt;span class="n"&gt;SetMusicOnHold&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;vip&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;end&lt;/span&gt;),&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Looped MOH with Periodic Announcements
&lt;/h3&gt;

&lt;p&gt;Create a custom application that plays MOH, then an announcement, repeatedly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;with&lt;/span&gt;-&lt;span class="n"&gt;announcements&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;MOH&lt;/span&gt; + &lt;span class="n"&gt;periodic&lt;/span&gt; &lt;span class="n"&gt;announcement&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;SetMusicOnHold&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;WaitExten&lt;/span&gt;(&lt;span class="m"&gt;30&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;queue&lt;/span&gt;/&lt;span class="n"&gt;estimated&lt;/span&gt;-&lt;span class="n"&gt;wait&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;WaitExten&lt;/span&gt;(&lt;span class="m"&gt;30&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Queue&lt;/span&gt;(&lt;span class="n"&gt;sales&lt;/span&gt;-&lt;span class="n"&gt;queue&lt;/span&gt;,&lt;span class="n"&gt;t&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Monitoring and Performance Tuning
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Check MOH Playback in Real-Time
&lt;/h3&gt;

&lt;p&gt;View active calls using MOH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show calls"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"music&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;Queue"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sample output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SIP/2001-00000001     Queue(sales-queue,t)    0:00:15
SIP/2002-00000002     Queue(support-queue,t)  0:00:42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitor MOH File Usage
&lt;/h3&gt;

&lt;p&gt;Track which MOH classes are actively used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"moh show classes"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show channels concise"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'!'&lt;/span&gt; &lt;span class="s1"&gt;'{print $10}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Audio Quality Diagnostics
&lt;/h3&gt;

&lt;p&gt;Verify audio playback quality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sox /var/lib/asterisk/moh/sales/music.wav &lt;span class="nt"&gt;-n&lt;/span&gt; remix - | sox - &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nb"&gt;stat&lt;/span&gt;
&lt;span class="c"&gt;# Check: RMS amplitude, Peak amplitude, Crest factor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Optimize quiet Parameter
&lt;/h3&gt;

&lt;p&gt;Test MOH with different quieting levels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Lower quiet value = louder&lt;/span&gt;
&lt;span class="c"&gt;# Higher quiet value = quieter&lt;/span&gt;
&lt;span class="c"&gt;# Typical range: 60-80&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;queue-sales]
&lt;span class="nv"&gt;quiet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;70  &lt;span class="c"&gt;# Balanced&lt;/span&gt;
&lt;span class="nv"&gt;quiet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;40  &lt;span class="c"&gt;# Louder&lt;/span&gt;
&lt;span class="nv"&gt;quiet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100 &lt;span class="c"&gt;# Quieter/silence preservation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CPU Usage Monitoring
&lt;/h3&gt;

&lt;p&gt;MOH playback CPU impact is minimal, but monitor with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;top &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 | &lt;span class="nb"&gt;grep &lt;/span&gt;asterisk
ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;asterisk | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  MOH Not Playing When Call Enters Queue
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Dead silence on hold instead of music.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"moh show classes"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If MOH classes don't appear or are empty:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check musiconhold.conf syntax:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   asterisk &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"moh reload"&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify audio files exist and are readable:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /var/lib/asterisk/moh/queue-sales/
   &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; asterisk:asterisk /var/lib/asterisk/moh/
   &lt;span class="nb"&gt;chmod &lt;/span&gt;755 /var/lib/asterisk/moh/&lt;span class="k"&gt;*&lt;/span&gt;
   &lt;span class="nb"&gt;chmod &lt;/span&gt;644 /var/lib/asterisk/moh/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reload MOH module:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"module reload res_musiconhold.so"&lt;/span&gt;
   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload musiconhold"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  MOH Class Not Found for Queue
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Asterisk CLI shows error: &lt;code&gt;Unable to locate MOH class queue-sales&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify queue name matches musiconhold.conf:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;queue_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;music_on_hold&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_queues&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check queues.conf has matching musiconhold entry:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 5 &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="s2"&gt;sales-queue&lt;/span&gt;&lt;span class="se"&gt;\]&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /etc/asterisk/queues.conf | &lt;span class="nb"&gt;grep &lt;/span&gt;musiconhold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Reload both configs:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload musiconhold"&lt;/span&gt;
   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload queues"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Audio Distortion or Quality Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; MOH playback sounds garbled, clipped, or distorted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check audio file format:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ffprobe &lt;span class="nt"&gt;-v&lt;/span&gt; error &lt;span class="nt"&gt;-show_entries&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sample_rate,channels,codec_name &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-of&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;noprint_wrappers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 /var/lib/asterisk/moh/sales/music.wav
   &lt;span class="c"&gt;# Should output: sample_rate=8000, channels=1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Re-encode to correct format:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; /var/lib/asterisk/moh/sales/music.wav &lt;span class="nt"&gt;-acodec&lt;/span&gt; pcm_s16le &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-ar&lt;/span&gt; 8000 &lt;span class="nt"&gt;-ac&lt;/span&gt; 1 /var/lib/asterisk/moh/sales/music-fixed.wav
   &lt;span class="nb"&gt;mv&lt;/span&gt; /var/lib/asterisk/moh/sales/music-fixed.wav &lt;span class="se"&gt;\&lt;/span&gt;
     /var/lib/asterisk/moh/sales/music.wav
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Adjust quiet parameter (reduce to increase volume):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;   [&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;]
   &lt;span class="n"&gt;quiet&lt;/span&gt;=&lt;span class="m"&gt;50&lt;/span&gt;  &lt;span class="c"&gt;# Increase volume (was 70)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check Asterisk log for codec errors:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"moh&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;music&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;codec"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wrong MOH Playing for Queue
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Queue plays default MOH instead of queue-specific MOH.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify queue configuration in database:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;queue_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;music_on_hold&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_queues&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;queue_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sales-queue'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check dialplan override:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"SetMusicOnHold"&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify queues.conf musiconhold assignment:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 10 &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="s2"&gt;sales-queue&lt;/span&gt;&lt;span class="se"&gt;\]&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /etc/asterisk/queues.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Force reload:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload musiconhold"&lt;/span&gt;
   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload queues"&lt;/span&gt;
   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload res_musiconhold.so"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  MOH Stops After Few Seconds
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Music plays briefly, then cuts out; silence follows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Causes:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Audio file too short or corrupt:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   sox /var/lib/asterisk/moh/sales/music.wav &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nb"&gt;stat&lt;/span&gt;
   &lt;span class="c"&gt;# Check duration; should be 2+ minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Directory permission issue:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /var/lib/asterisk/moh/sales/
   &lt;span class="c"&gt;# Should show: drwxr-xr-x asterisk:asterisk&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Asterisk process permissions:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;asterisk | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;
   &lt;span class="c"&gt;# Should run as asterisk user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; asterisk:asterisk /var/lib/asterisk/moh/
&lt;span class="nb"&gt;chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 755 /var/lib/asterisk/moh/
systemctl restart asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  High CPU Usage with MOH
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Asterisk CPU usage spikes when MOH is active.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use MP3 or WAV, not generator-based MOH:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;   [&lt;span class="n"&gt;queue&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;]
   &lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;files&lt;/span&gt;  &lt;span class="c"&gt;# Not mode=mp3 or mode=DAHDI
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify audio codec is efficient:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ffprobe /var/lib/asterisk/moh/sales/music.wav
   &lt;span class="c"&gt;# Should show PCM 8-bit or 16-bit, 8000 Hz&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reduce number of concurrent MOH streams if necessary:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"moh show files"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Logs Not Showing MOH Activity
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Enable MOH debug logging:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core set debug 3"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core set verbose 3"&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; moh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Check for specific errors:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"moh&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;music&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;hold"&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Implementing custom per-queue Music on Hold in Asterisk/ViciDial requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Audio Preparation:&lt;/strong&gt; Convert media to 8 kHz, 16-bit mono WAV files for optimal quality and compatibility&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Directory Structure:&lt;/strong&gt; Organize MOH files in &lt;code&gt;/var/lib/asterisk/moh/&lt;/code&gt; with separate subdirectories per queue&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration:&lt;/strong&gt; Define MOH classes in &lt;code&gt;musiconhold.conf&lt;/code&gt; and assign to queues via &lt;code&gt;queues.conf&lt;/code&gt; or ViciDial database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dialplan Integration:&lt;/strong&gt; Use &lt;code&gt;SetMusicOnHold()&lt;/code&gt; for dynamic selection and combine with queue applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Sync:&lt;/strong&gt; Link ViciDial &lt;code&gt;vicidial_queues&lt;/code&gt; table &lt;code&gt;music_on_hold&lt;/code&gt; field to Asterisk configurations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation &amp;amp; Testing:&lt;/strong&gt; Reload modules, verify with CLI commands, test with live calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring:&lt;/strong&gt; Use &lt;code&gt;asterisk -rx&lt;/code&gt; commands to monitor active MOH playback and troubleshoot issues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Ensure proper file permissions, audio quality, and system resource allocation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Key commands for day-to-day operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Reload after configuration changes&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload musiconhold"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"reload queues"&lt;/span&gt;

&lt;span class="c"&gt;# Verify MOH is loaded&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"moh show classes"&lt;/span&gt;

&lt;span class="c"&gt;# Monitor active queues with MOH&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show calls"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;Queue

&lt;span class="c"&gt;# Restart Asterisk if issues persist&lt;/span&gt;
systemctl restart asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With proper setup, ViciDial agents and customers will experience professional, branded music on hold tailored to each queue, improving perceived call quality and reducing perceived wait times.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
    <item>
      <title>ViciDial Recording Storage — NFS, S3 &amp; Archival Strategies</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Sun, 19 Jul 2026 03:30:03 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/vicidial-recording-storage-nfs-s3-archival-strategies-3j71</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/vicidial-recording-storage-nfs-s3-archival-strategies-3j71</guid>
      <description>&lt;h1&gt;
  
  
  ViciDial Recording Storage — NFS, S3 &amp;amp; Archival Strategies
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Master efficient recording storage architectures for ViciDial and Asterisk systems, comparing NFS block storage, S3 cloud solutions, and long-term archival strategies with production-tested configurations.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before implementing the strategies in this tutorial, you should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A running ViciDial installation (version 2.14+) with Asterisk 13+&lt;/li&gt;
&lt;li&gt;Root or sudo access to your ViciDial servers&lt;/li&gt;
&lt;li&gt;Basic understanding of Linux filesystems, NFS, and AWS S3 (or compatible service)&lt;/li&gt;
&lt;li&gt;Database access to the asterisk database and vicidial_log table&lt;/li&gt;
&lt;li&gt;Familiarity with Asterisk configuration files and the dialplan&lt;/li&gt;
&lt;li&gt;At least 100GB of free storage for testing configurations&lt;/li&gt;
&lt;li&gt;Network bandwidth planning completed for your expected call volume&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tutorial assumes CentOS 7/8 or Ubuntu 18.04+ operating systems. Commands should be adapted for your specific distribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding ViciDial Recording Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How ViciDial Records Calls
&lt;/h3&gt;

&lt;p&gt;ViciDial uses Asterisk's native recording capabilities to capture calls. When a call is bridged between an agent and a customer, Asterisk writes audio data to disk in real-time. The recording process is controlled by the dialplan and ViciDial application logic.&lt;/p&gt;

&lt;p&gt;Call recordings are stored in raw Asterisk format (typically WAV or GSM) and are referenced in the &lt;code&gt;vicidial_log&lt;/code&gt; table with the field &lt;code&gt;recording_id&lt;/code&gt;. Each call record contains a unique identifier linking to its corresponding audio file.&lt;/p&gt;

&lt;p&gt;Key recording-related fields in &lt;code&gt;vicidial_log&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;recording_id&lt;/code&gt; — Unique identifier for the recording file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;call_date&lt;/code&gt; — Timestamp of the call&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;length_in_sec&lt;/code&gt; — Duration of the recorded call&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt; — Call status (SALE, CANCEL, DNC, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Storage Sizing Calculations
&lt;/h3&gt;

&lt;p&gt;For production planning, use these baseline figures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GSM codec&lt;/strong&gt;: ~10-12 KB per second (monaural, 8kHz)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WAV codec&lt;/strong&gt;: ~32 KB per second (stereo, 16-bit, 8kHz)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MP3 (128kbps)&lt;/strong&gt;: ~16 KB per second&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a 100-agent center with average call length of 8 minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Daily recordings = 100 agents × 80 calls/day × 480 seconds × 11 KB/sec
                 = 100 × 80 × 480 × 11 KB = 42,240,000 KB ≈ 40 GB/day
Monthly storage = 40 GB × 30 = 1.2 TB
Annual storage = 1.2 TB × 12 = 14.4 TB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This calculation shows why storage strategy is critical for scaling operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording Path Configuration in ViciDial
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Default Recording Directory Structure
&lt;/h3&gt;

&lt;p&gt;ViciDial records calls to &lt;code&gt;/var/spool/asterisk/monitor/&lt;/code&gt; by default. This directory is defined in the dialplan and should match your storage backend.&lt;/p&gt;

&lt;p&gt;Verify current recording path in your extensions-vicidial.conf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"monitor&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;recording"&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Standard ViciDial recording command in the dialplan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;8600&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;MixMonitor&lt;/span&gt;(${&lt;span class="n"&gt;MIXMON_FILENAME&lt;/span&gt;}${&lt;span class="n"&gt;MIXMON_FORMAT&lt;/span&gt;},&lt;span class="n"&gt;b&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;8600&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Return&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring Custom Recording Paths
&lt;/h3&gt;

&lt;p&gt;To use a custom path, modify your ViciDial configuration. Edit the system settings table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_system_settings&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;recording_directory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/data/recordings'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;setting_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'asterisk_home'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then verify Asterisk has permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; asterisk:asterisk /data/recordings
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;755 /data/recordings
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-ld&lt;/span&gt; /data/recordings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create subdirectories by date for better organization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Create daily recording directories&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;0..30&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;date_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"-&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt; days"&lt;/span&gt; +%Y/%m/%d&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /data/recordings/&lt;span class="nv"&gt;$date_dir&lt;/span&gt;
  &lt;span class="nb"&gt;chown &lt;/span&gt;asterisk:asterisk /data/recordings/&lt;span class="nv"&gt;$date_dir&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  NFS-Based Recording Storage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  When to Use NFS
&lt;/h3&gt;

&lt;p&gt;NFS (Network File System) is ideal when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have multiple Asterisk servers sharing a single recording repository&lt;/li&gt;
&lt;li&gt;Your network has low latency and high bandwidth (gigabit or better)&lt;/li&gt;
&lt;li&gt;You want simple, proven file-level storage without cloud complexity&lt;/li&gt;
&lt;li&gt;HIPAA or data residency requirements mandate on-premises storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NFS is &lt;strong&gt;not&lt;/strong&gt; recommended for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-latency or unreliable networks (use local storage or S3)&lt;/li&gt;
&lt;li&gt;Single-server installations (use local storage instead)&lt;/li&gt;
&lt;li&gt;Extreme scale (1000+ agents) without clustering NFS via Ceph&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  NFS Server Setup
&lt;/h3&gt;

&lt;p&gt;Install NFS on your storage server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CentOS/RHEL&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;nfs-utils
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nfs-server
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start nfs-server

&lt;span class="c"&gt;# Ubuntu/Debian&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;nfs-kernel-server
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nfs-server
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start nfs-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the recording directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/recordings
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;nobody:nogroup /mnt/recordings
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;755 /mnt/recordings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure NFS exports in &lt;code&gt;/etc/exports&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;/&lt;span class="n"&gt;mnt&lt;/span&gt;/&lt;span class="n"&gt;recordings&lt;/span&gt; &lt;span class="m"&gt;192&lt;/span&gt;.&lt;span class="m"&gt;168&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;/&lt;span class="m"&gt;24&lt;/span&gt;(&lt;span class="n"&gt;rw&lt;/span&gt;,&lt;span class="n"&gt;sync&lt;/span&gt;,&lt;span class="n"&gt;no_subtree_check&lt;/span&gt;,&lt;span class="n"&gt;no_root_squash&lt;/span&gt;,&lt;span class="n"&gt;atime&lt;/span&gt;=&lt;span class="n"&gt;off&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parameters explained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rw&lt;/code&gt; — Read and write access&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sync&lt;/code&gt; — Force synchronous writes (safer, slightly slower)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;no_subtree_check&lt;/code&gt; — Improves performance, safe for most setups&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;no_root_squash&lt;/code&gt; — Allow root on client to write as root on server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;atime=off&lt;/code&gt; — Disable access time updates (significant performance gain)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apply the exports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;exportfs &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;exportfs &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NFS Client Configuration on Asterisk Servers
&lt;/h3&gt;

&lt;p&gt;Install NFS client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CentOS/RHEL&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;nfs-utils

&lt;span class="c"&gt;# Ubuntu/Debian&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;nfs-common
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create mount point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/spool/asterisk/monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mount the NFS share:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; nfs &lt;span class="nt"&gt;-o&lt;/span&gt; rw,sync,hard,intr,rsize&lt;span class="o"&gt;=&lt;/span&gt;8192,wsize&lt;span class="o"&gt;=&lt;/span&gt;8192 &lt;span class="se"&gt;\&lt;/span&gt;
  192.168.1.50:/mnt/recordings /var/spool/asterisk/monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mount options explained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hard&lt;/code&gt; — Keep retrying if server is unavailable (don't give up)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;intr&lt;/code&gt; — Allow interrupting hung NFS calls with Ctrl+C&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rsize/wsize&lt;/code&gt; — Read/write buffer sizes (8KB good for voice; larger for throughput)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify the mount:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount | &lt;span class="nb"&gt;grep &lt;/span&gt;monitor
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; /var/spool/asterisk/monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the mount persistent by adding to &lt;code&gt;/etc/fstab&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;192.168.1.50:/mnt/recordings /var/spool/asterisk/monitor nfs &lt;span class="se"&gt;\&lt;/span&gt;
rw,sync,hard,intr,rsize&lt;span class="o"&gt;=&lt;/span&gt;8192,wsize&lt;span class="o"&gt;=&lt;/span&gt;8192,_netdev 0 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;_netdev&lt;/code&gt; to ensure NFS mounts after network is up.&lt;/p&gt;

&lt;h3&gt;
  
  
  NFS Performance Tuning
&lt;/h3&gt;

&lt;p&gt;Monitor NFS performance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install nfs-utils if not present&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;nfs-utils

&lt;span class="c"&gt;# Check NFS statistics&lt;/span&gt;
nfsstat &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;

&lt;span class="c"&gt;# Monitor NFS operations per second&lt;/span&gt;
iostat &lt;span class="nt"&gt;-x&lt;/span&gt; 1 5 | &lt;span class="nb"&gt;grep &lt;/span&gt;nfs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For high-volume recording scenarios, increase buffer sizes on both server and client:&lt;/p&gt;

&lt;p&gt;On NFS &lt;strong&gt;server&lt;/strong&gt; (&lt;code&gt;/etc/nfs.conf&lt;/code&gt; or kernel parameters):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit /etc/sysctl.conf on the NFS server&lt;/span&gt;
net.core.rmem_max &lt;span class="o"&gt;=&lt;/span&gt; 134217728
net.core.wmem_max &lt;span class="o"&gt;=&lt;/span&gt; 134217728
net.ipv4.tcp_rmem &lt;span class="o"&gt;=&lt;/span&gt; 4096 87380 67108864
net.ipv4.tcp_wmem &lt;span class="o"&gt;=&lt;/span&gt; 4096 65536 67108864

&lt;span class="c"&gt;# Apply settings&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On NFS &lt;strong&gt;client&lt;/strong&gt; mount options, use larger buffers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; nfs &lt;span class="nt"&gt;-o&lt;/span&gt; rw,sync,hard,intr,rsize&lt;span class="o"&gt;=&lt;/span&gt;32768,wsize&lt;span class="o"&gt;=&lt;/span&gt;32768 &lt;span class="se"&gt;\&lt;/span&gt;
  192.168.1.50:/mnt/recordings /var/spool/asterisk/monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test throughput with &lt;code&gt;dd&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Write test&lt;/span&gt;
&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/spool/asterisk/monitor/test-write &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000 &lt;span class="nv"&gt;oflag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;direct
&lt;span class="c"&gt;# Expected: 100+ MB/s on gigabit Ethernet&lt;/span&gt;

&lt;span class="c"&gt;# Read test&lt;/span&gt;
&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/spool/asterisk/monitor/test-write &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/null &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M
&lt;span class="nb"&gt;rm&lt;/span&gt; /var/spool/asterisk/monitor/test-write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  S3-Based Recording Storage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why S3 for ViciDial
&lt;/h3&gt;

&lt;p&gt;Amazon S3 or compatible services (MinIO, DigitalOcean Spaces, Wasabi) offer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virtually unlimited scalability&lt;/li&gt;
&lt;li&gt;Automatic replication and durability (99.999999999%)&lt;/li&gt;
&lt;li&gt;Pay-as-you-go pricing&lt;/li&gt;
&lt;li&gt;Geographic redundancy without hardware&lt;/li&gt;
&lt;li&gt;Built-in archival to Glacier for cost reduction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network latency for write operations&lt;/li&gt;
&lt;li&gt;Per-request costs (though minimal)&lt;/li&gt;
&lt;li&gt;Requires cloud account or private S3 setup&lt;/li&gt;
&lt;li&gt;Slight complexity in failover and hybrid scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  S3 Bucket Configuration
&lt;/h3&gt;

&lt;p&gt;Create an S3 bucket for ViciDial recordings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Using AWS CLI v2&lt;/span&gt;
aws s3 mb s3://vicidial-recordings-prod &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1

&lt;span class="c"&gt;# Enable versioning for protection against accidental deletes&lt;/span&gt;
aws s3api put-bucket-versioning &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; vicidial-recordings-prod &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--versioning-configuration&lt;/span&gt; &lt;span class="nv"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Enabled

&lt;span class="c"&gt;# Set lifecycle policy to archive old recordings&lt;/span&gt;
aws s3api put-bucket-lifecycle-configuration &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; vicidial-recordings-prod &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lifecycle-configuration&lt;/span&gt; file://lifecycle.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;lifecycle.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Archive old recordings to Glacier"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"recordings/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Transitions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"Days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"StorageClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GLACIER"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Expiration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2555&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This policy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Moves recordings to Glacier after 90 days (cost reduction from $0.023/GB to $0.004/GB)&lt;/li&gt;
&lt;li&gt;Deletes after 7 years (adjust based on compliance requirements)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Using S3fuse or S3FS
&lt;/h3&gt;

&lt;p&gt;S3FS mounts S3 as a filesystem. Install on Asterisk server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CentOS/RHEL&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;epel-release
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;s3fs-fuse

&lt;span class="c"&gt;# Ubuntu/Debian&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;s3fs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create credentials file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Store AWS credentials (never commit to version control)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"AKIAIOSFODNN7EXAMPLE:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.s3fs-credentials
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.s3fs-credentials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mount S3 bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create mount point&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/spool/asterisk/monitor

&lt;span class="c"&gt;# Mount S3 bucket&lt;/span&gt;
s3fs vicidial-recordings-prod /var/spool/asterisk/monitor &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;passwd_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.s3fs-credentials &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;use_cache&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; allow_other &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;mp_umask&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;002 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; multithreaded &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;parallel_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;multipart_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parameters explained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;allow_other&lt;/code&gt; — Allow processes other than owner to access&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;multithreaded&lt;/code&gt; — Enable parallel uploads&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parallel_count&lt;/code&gt; — Number of parallel upload threads&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;multipart_size&lt;/code&gt; — Size of multipart uploads in MB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify mount:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount | &lt;span class="nb"&gt;grep &lt;/span&gt;s3fs
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; /var/spool/asterisk/monitor
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /var/spool/asterisk/monitor/test.txt
aws s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://vicidial-recordings-prod/test.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: S3FS has latency. For active recording paths, use local storage + asynchronous upload instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended Approach: Local + S3 Upload
&lt;/h3&gt;

&lt;p&gt;Use local storage for recording, then upload asynchronously to S3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# /usr/local/sbin/upload-recordings-to-s3.sh&lt;/span&gt;
&lt;span class="c"&gt;# Run via cron every 5 minutes&lt;/span&gt;

&lt;span class="nv"&gt;RECORDING_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/spool/asterisk/monitor"&lt;/span&gt;
&lt;span class="nv"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"s3://vicidial-recordings-prod"&lt;/span&gt;
&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"vicidial-prod"&lt;/span&gt;
&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/log/s3-upload.log"&lt;/span&gt;

&lt;span class="c"&gt;# Find recordings older than 1 minute (completed calls)&lt;/span&gt;
find &lt;span class="nv"&gt;$RECORDING_DIR&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.wav"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-mmin&lt;/span&gt; +1 | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;recording&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c"&gt;# Skip if already uploaded (check for .uploaded marker)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;recording&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.uploaded"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    continue
  fi&lt;/span&gt;

  &lt;span class="c"&gt;# Determine S3 path based on date&lt;/span&gt;
  &lt;span class="nv"&gt;file_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; %y &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$recording&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;s3_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/recordings/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file_date&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="nv"&gt;$recording&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="c"&gt;# Upload to S3&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$recording&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$s3_path&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$AWS_PROFILE&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# Mark as uploaded&lt;/span&gt;
    &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;recording&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.uploaded"&lt;/span&gt;

    &lt;span class="c"&gt;# Optionally delete local copy after 7 days&lt;/span&gt;
    find &lt;span class="nv"&gt;$RECORDING_DIR&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.uploaded"&lt;/span&gt; &lt;span class="nt"&gt;-mtime&lt;/span&gt; +7 | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;marker&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
      &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$marker&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;done
  fi
done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;] Upload cycle complete"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add to crontab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt;/5 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/sbin/upload-recordings-to-s3.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeps recording writes fast (local disk)&lt;/li&gt;
&lt;li&gt;Asynchronously uploads to S3 without blocking&lt;/li&gt;
&lt;li&gt;Maintains local copies for immediate playback&lt;/li&gt;
&lt;li&gt;Can delete after 7 days to save local storage&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  S3-Compatible Services (MinIO)
&lt;/h3&gt;

&lt;p&gt;For on-premises S3 storage, use MinIO:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install MinIO on a dedicated server&lt;/span&gt;
wget https://dl.min.io/server/minio/release/linux-amd64/minio
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x minio
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;minio /usr/local/bin/

&lt;span class="c"&gt;# Create data directory&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/minio-data
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;minio:minio /mnt/minio-data

&lt;span class="c"&gt;# Start MinIO&lt;/span&gt;
&lt;span class="nv"&gt;MINIO_ROOT_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="nv"&gt;MINIO_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;securepassword &lt;span class="se"&gt;\&lt;/span&gt;
  /usr/local/bin/minio server /mnt/minio-data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access MinIO console at &lt;code&gt;http://minio-server:9001&lt;/code&gt; and configure like S3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording Archival and Retention Strategies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Database-Driven Archival
&lt;/h3&gt;

&lt;p&gt;Track recordings in the database and implement retention policies programmatically.&lt;/p&gt;

&lt;p&gt;Query active recordings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;call_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;recording_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;LENGTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;duration_days&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;TIMESTAMPDIFF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;age_days&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;recording_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;recording_id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt; &lt;span class="k"&gt;DAY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a retention policy based on status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Archive recordings for closed campaigns after 30 days&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;PROCEDURE&lt;/span&gt; &lt;span class="n"&gt;archive_old_recordings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;DECLARE&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;DECLARE&lt;/span&gt; &lt;span class="n"&gt;rec_id&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;DECLARE&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="k"&gt;CURSOR&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;recording_id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;recording_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;recording_id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPDIFF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'N'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;DECLARE&lt;/span&gt; &lt;span class="k"&gt;CONTINUE&lt;/span&gt; &lt;span class="k"&gt;HANDLER&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;FOUND&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;OPEN&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;read_loop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LOOP&lt;/span&gt;
    &lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;rec_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
      &lt;span class="n"&gt;LEAVE&lt;/span&gt; &lt;span class="n"&gt;read_loop&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;-- Log archival action&lt;/span&gt;
    &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;vicidial_log_archive&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recording_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;archived_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;archive_location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rec_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;'S3'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;CLOSE&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute archival:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; vicidialuser &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"CALL archive_old_recordings();"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementing Deletion Policies
&lt;/h3&gt;

&lt;p&gt;Create a safe deletion script that confirms recordings still exist before removal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# /usr/local/sbin/purge-old-recordings.sh&lt;/span&gt;
&lt;span class="c"&gt;# Delete recordings older than retention period&lt;/span&gt;

&lt;span class="nv"&gt;RETENTION_DAYS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;365
&lt;span class="nv"&gt;RECORDING_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/spool/asterisk/monitor"&lt;/span&gt;
&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"vicidialuser"&lt;/span&gt;
&lt;span class="nv"&gt;DB_PASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"PASSWORD"&lt;/span&gt;
&lt;span class="nv"&gt;DB_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"asterisk"&lt;/span&gt;

&lt;span class="c"&gt;# Find recordings older than retention period&lt;/span&gt;
find &lt;span class="nv"&gt;$RECORDING_DIR&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.wav"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-mtime&lt;/span&gt; +&lt;span class="nv"&gt;$RETENTION_DAYS&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;recording&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$recording&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;recording_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="c"&gt;# Verify in database that this recording should be deleted&lt;/span&gt;
  &lt;span class="nv"&gt;exists&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="nv"&gt;$DB_USER&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt;$DB_PASS&lt;/span&gt; &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"SELECT COUNT(*) FROM vicidial_log WHERE recording_id='&lt;/span&gt;&lt;span class="nv"&gt;$recording_id&lt;/span&gt;&lt;span class="s2"&gt;' LIMIT 1;"&lt;/span&gt; &lt;span class="nv"&gt;$DB_NAME&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$exists&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# Safe to delete&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deleting: &lt;/span&gt;&lt;span class="nv"&gt;$recording&lt;/span&gt;&lt;span class="s2"&gt; (age: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; %y &lt;span class="nv"&gt;$recording&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$recording&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schedule in cron:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run daily at 2 AM&lt;/span&gt;
0 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/sbin/purge-old-recordings.sh &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/purge-recordings.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Compression and Reencoding
&lt;/h3&gt;

&lt;p&gt;For cost optimization, compress older recordings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Compress recordings older than 7 days to MP3&lt;/span&gt;

&lt;span class="nv"&gt;DAYS_OLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;7
&lt;span class="nv"&gt;RECORDING_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/spool/asterisk/monitor"&lt;/span&gt;

find &lt;span class="nv"&gt;$RECORDING_DIR&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.wav"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-mtime&lt;/span&gt; +&lt;span class="nv"&gt;$DAYS_OLD&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;wav_file&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;mp3_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;wav_file&lt;/span&gt;&lt;span class="p"&gt;%.wav&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.mp3"&lt;/span&gt;

  &lt;span class="c"&gt;# Skip if already compressed&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$mp3_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;

  &lt;span class="c"&gt;# Compress using ffmpeg at 64kbps (acceptable quality for voice)&lt;/span&gt;
  ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$wav_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-acodec&lt;/span&gt; libmp3lame &lt;span class="nt"&gt;-ab&lt;/span&gt; 64k &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$mp3_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# Calculate space saved&lt;/span&gt;
    &lt;span class="nv"&gt;wav_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;%s &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$wav_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;mp3_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;%s &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$mp3_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;saved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;wav_size &lt;span class="o"&gt;-&lt;/span&gt; mp3_size&lt;span class="k"&gt;))&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Compressed &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="nv"&gt;$wav_file&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;: saved &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;saved&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes"&lt;/span&gt;

    &lt;span class="c"&gt;# Remove WAV after successful compression&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$wav_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typical compression results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Original WAV (8-bit, 8kHz): 480 KB per minute&lt;/li&gt;
&lt;li&gt;MP3 (64kbps): 480 KB per minute (same!)&lt;/li&gt;
&lt;li&gt;MP3 (32kbps): 240 KB per minute (50% savings, lower quality)&lt;/li&gt;
&lt;li&gt;GSM (8kbps): ~60 KB per minute (87% savings, acceptable for voice)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For best results with ViciDial, keep WAV for recent recordings (fast playback), compress to MP3 at 64kbps for 7-30 days, then archive to Glacier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Archival Verification
&lt;/h3&gt;

&lt;p&gt;Periodically verify archived recordings are recoverable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Test recovery of random archived recordings&lt;/span&gt;

&lt;span class="nv"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"s3://vicidial-recordings-prod"&lt;/span&gt;
&lt;span class="nv"&gt;SAMPLE_SIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10

&lt;span class="c"&gt;# Get random sample of archived recordings&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nv"&gt;$S3_BUCKET&lt;/span&gt;/recordings/ &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $NF}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;shuf&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$SAMPLE_SIZE&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;recording&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do

  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Testing recovery of: &lt;/span&gt;&lt;span class="nv"&gt;$recording&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="c"&gt;# Download to temp location&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$S3_BUCKET&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$recording&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /tmp/test-recovery.wav&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;filesize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;%s /tmp/test-recovery.wav&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✓ Successfully recovered: &lt;/span&gt;&lt;span class="nv"&gt;$recording&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$filesize&lt;/span&gt;&lt;span class="s2"&gt; bytes)"&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; /tmp/test-recovery.wav
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✗ FAILED to recover: &lt;/span&gt;&lt;span class="nv"&gt;$recording&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring and Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Monitoring Recording Storage
&lt;/h3&gt;

&lt;p&gt;Create a monitoring script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# /usr/local/sbin/monitor-recording-storage.sh&lt;/span&gt;

&lt;span class="nv"&gt;RECORDING_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/spool/asterisk/monitor"&lt;/span&gt;
&lt;span class="nv"&gt;WARNING_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80  &lt;span class="c"&gt;# percent full&lt;/span&gt;
&lt;span class="nv"&gt;CRITICAL_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;90

&lt;span class="c"&gt;# Get usage percentage&lt;/span&gt;
&lt;span class="nv"&gt;usage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nv"&gt;$RECORDING_DIR&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $5}'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/%//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Count recordings by age&lt;/span&gt;
&lt;span class="nv"&gt;recent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find &lt;span class="nv"&gt;$RECORDING_DIR&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.wav"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-mtime&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;week_old&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find &lt;span class="nv"&gt;$RECORDING_DIR&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.wav"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-mtime&lt;/span&gt; +7 | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Size statistics&lt;/span&gt;
&lt;span class="nv"&gt;total_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; &lt;span class="nv"&gt;$RECORDING_DIR&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Output metrics&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Recording Storage Status:"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  Total: &lt;/span&gt;&lt;span class="nv"&gt;$total_size&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  Usage: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;usage&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  Recent recordings (&amp;lt; 1 day): &lt;/span&gt;&lt;span class="nv"&gt;$recent&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  Old recordings (&amp;gt; 7 days): &lt;/span&gt;&lt;span class="nv"&gt;$week_old&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Alert if threshold exceeded&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$usage&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; &lt;span class="nv"&gt;$CRITICAL_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"CRITICAL: Recording storage is &lt;/span&gt;&lt;span class="nv"&gt;$usage&lt;/span&gt;&lt;span class="s2"&gt;% full!"&lt;/span&gt;
  &lt;span class="c"&gt;# Send alert (email, Slack, etc.)&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$usage&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; &lt;span class="nv"&gt;$WARNING_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"WARNING: Recording storage is &lt;/span&gt;&lt;span class="nv"&gt;$usage&lt;/span&gt;&lt;span class="s2"&gt;% full"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add to Nagios/Icinga monitoring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run every 5 minutes&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;/5 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/sbin/monitor-recording-storage.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NFS Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem: NFS mount hangs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check NFS server connectivity&lt;/span&gt;
showmount &lt;span class="nt"&gt;-e&lt;/span&gt; 192.168.1.50

&lt;span class="c"&gt;# Force unmount if hung&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;umount &lt;span class="nt"&gt;-f&lt;/span&gt; /var/spool/asterisk/monitor

&lt;span class="c"&gt;# Remount with different options&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; nfs &lt;span class="nt"&gt;-o&lt;/span&gt; soft,intr,retrans&lt;span class="o"&gt;=&lt;/span&gt;3,timeo&lt;span class="o"&gt;=&lt;/span&gt;15 &lt;span class="se"&gt;\&lt;/span&gt;
  192.168.1.50:/mnt/recordings /var/spool/asterisk/monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem: Slow NFS performance&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check client-side NFS operations&lt;/span&gt;
nfsstat &lt;span class="nt"&gt;-c&lt;/span&gt;

&lt;span class="c"&gt;# Increase network buffers&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.core.rmem_max&lt;span class="o"&gt;=&lt;/span&gt;134217728
&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.core.wmem_max&lt;span class="o"&gt;=&lt;/span&gt;134217728

&lt;span class="c"&gt;# Monitor packet loss (run on client)&lt;/span&gt;
ping &lt;span class="nt"&gt;-c&lt;/span&gt; 100 192.168.1.50 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"packet loss"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem: Permission denied writing to NFS mount&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify Asterisk user can write&lt;/span&gt;
&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; asterisk &lt;span class="nb"&gt;touch&lt;/span&gt; /var/spool/asterisk/monitor/test.wav

&lt;span class="c"&gt;# Check ownership and permissions&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-ld&lt;/span&gt; /var/spool/asterisk/monitor
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-ld&lt;/span&gt; /mnt/recordings  &lt;span class="c"&gt;# on NFS server&lt;/span&gt;

&lt;span class="c"&gt;# Fix permissions&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; asterisk:asterisk /var/spool/asterisk/monitor
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;755 /var/spool/asterisk/monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  S3 Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem: S3 uploads failing&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check AWS credentials&lt;/span&gt;
aws sts get-caller-identity

&lt;span class="c"&gt;# Test S3 bucket access&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://vicidial-recordings-prod/

&lt;span class="c"&gt;# Check IAM permissions (need s3:PutObject, s3:GetObject, s3:ListBucket)&lt;/span&gt;
aws iam get-user-policy &lt;span class="nt"&gt;--user-name&lt;/span&gt; vicidial-s3-user &lt;span class="nt"&gt;--policy-name&lt;/span&gt; vicidial-s3-policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem: S3FS mount unreliable&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check S3FS logs&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/syslog | &lt;span class="nb"&gt;grep &lt;/span&gt;s3fs

&lt;span class="c"&gt;# Unmount and remount with diagnostics&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;fusermount &lt;span class="nt"&gt;-u&lt;/span&gt; /var/spool/asterisk/monitor
s3fs &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; vicidial-recordings-prod /var/spool/asterisk/monitor &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;passwd_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.s3fs-credentials 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;tee&lt;/span&gt; /tmp/s3fs-debug.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Asterisk CLI Monitoring
&lt;/h3&gt;

&lt;p&gt;Monitor recordings in real-time via Asterisk CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Connect to Asterisk console&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show channels verbose"&lt;/span&gt;

&lt;span class="c"&gt;# Check MixMonitor status&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"mixmonitor show"&lt;/span&gt;

&lt;span class="c"&gt;# List active recordings&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"mixmonitor list"&lt;/span&gt;

&lt;span class="c"&gt;# Stop a specific recording (if needed)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"mixmonitor stop &amp;lt;call_id&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;View Asterisk recording logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Follow Asterisk message log&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"mixmonitor&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;recording"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Effective ViciDial recording storage requires matching your architecture to operational needs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use NFS when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have 2-10 Asterisk servers sharing recordings&lt;/li&gt;
&lt;li&gt;You have high-bandwidth, low-latency LAN infrastructure&lt;/li&gt;
&lt;li&gt;You need quick local playback without cloud dependency&lt;/li&gt;
&lt;li&gt;You want simplicity without cloud account management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use S3 when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need unlimited scalability&lt;/li&gt;
&lt;li&gt;You want automatic geographic redundancy&lt;/li&gt;
&lt;li&gt;You need cost-effective long-term archival with Glacier&lt;/li&gt;
&lt;li&gt;You operate in multiple geographic regions&lt;/li&gt;
&lt;li&gt;You prefer pay-as-you-go pricing over capital equipment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best practice hybrid approach:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record to local Asterisk storage for performance&lt;/li&gt;
&lt;li&gt;Upload asynchronously to S3 or NFS backup&lt;/li&gt;
&lt;li&gt;Compress and archive to Glacier after 90 days&lt;/li&gt;
&lt;li&gt;Delete after 1-7 years per compliance requirements&lt;/li&gt;
&lt;li&gt;Monitor storage utilization continuously&lt;/li&gt;
&lt;li&gt;Test archival recovery monthly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key metrics to track:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Daily recording volume (GB)&lt;/li&gt;
&lt;li&gt;Storage utilization (%)&lt;/li&gt;
&lt;li&gt;Upload success rate (%)&lt;/li&gt;
&lt;li&gt;Retrieval latency (seconds)&lt;/li&gt;
&lt;li&gt;Cost per call recorded ($/call)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implement monitoring with appropriate alerting, test failover scenarios regularly, and review retention policies annually as call volume and compliance requirements evolve. The most sophisticated storage system is worthless if recordings cannot be recovered when needed.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
    <item>
      <title>Troubleshooting One-Way Audio in ViciDial / Asterisk</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Sat, 18 Jul 2026 03:30:02 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/troubleshooting-one-way-audio-in-vicidial-asterisk-1api</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/troubleshooting-one-way-audio-in-vicidial-asterisk-1api</guid>
      <description>&lt;h1&gt;
  
  
  Troubleshooting One-Way Audio in ViciDial / Asterisk
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Learn to diagnose and resolve one-way audio issues in ViciDial and Asterisk environments, including codec mismatches, RTP problems, and ViciDial-specific configuration errors.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting this guide, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A working ViciDial installation (3.x or 4.x recommended)&lt;/li&gt;
&lt;li&gt;SSH access to your ViciDial server with sudo privileges&lt;/li&gt;
&lt;li&gt;Basic understanding of SIP and RTP protocols&lt;/li&gt;
&lt;li&gt;Familiarity with Asterisk CLI commands&lt;/li&gt;
&lt;li&gt;Access to Asterisk configuration files at &lt;code&gt;/etc/asterisk/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;MySQL/MariaDB access to the asterisk database&lt;/li&gt;
&lt;li&gt;A test scenario to reproduce the one-way audio issue&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding One-Way Audio in ViciDial/Asterisk
&lt;/h2&gt;

&lt;p&gt;One-way audio occurs when call participants can hear one direction of conversation but not the other. In ViciDial environments, this typically manifests as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Agent hears caller, caller cannot hear agent&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caller hears agent, agent cannot hear caller&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One direction is garbled, other direction is clear&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The root causes in ViciDial/Asterisk deployments usually involve:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Codec mismatches&lt;/strong&gt; between endpoints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RTP media path issues&lt;/strong&gt; (firewall, NAT, port ranges)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SIP signaling vs. media path divergence&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ViciDial bridge or transfer configuration errors&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incorrect SIP peer definitions&lt;/strong&gt; in sip-vicidial.conf&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Call recording configuration conflicts&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Carrier/trunk misconfigurations&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Section 1: Initial Diagnostic Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Check Asterisk Version and Core Status
&lt;/h3&gt;

&lt;p&gt;Start by verifying your Asterisk installation is running correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show version"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show license"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show status"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output shows Asterisk version, licensing info, and active SIP channels. If you see zero channels or connection errors, restart Asterisk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl restart asterisk
&lt;span class="c"&gt;# Wait 15 seconds&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show version"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable SIP/RTP Debugging
&lt;/h3&gt;

&lt;p&gt;Enable verbose logging to capture the call flow. SSH into your ViciDial server and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug on"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtp set debug on"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core set verbose 3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reproduce the one-way audio issue while these debug logs are active. Monitor the log file in real-time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"INVITE|SDP|RTP|Media"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop debugging after reproducing the issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug off"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtp set debug off"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Review Call Logs in ViciDial Database
&lt;/h3&gt;

&lt;p&gt;Query the vicidial_log table to find the call record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;MINUTE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; 
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key fields to examine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;length_in_sec&lt;/code&gt; - Call duration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt; - Call outcome&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;recording_id&lt;/code&gt; - Whether recording was attempted&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user&lt;/code&gt; - Agent username&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;carrier_id&lt;/code&gt; - Trunk used for the call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more detailed call segment information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_closer_log&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;MINUTE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; 
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 2: Codec Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Codec mismatches are the most common cause of one-way audio. ViciDial bridges calls between different endpoints, each with preferred codecs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identify Active Codecs
&lt;/h3&gt;

&lt;p&gt;Check which codecs are allowed on your SIP peers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer AGENTPEER detailed"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; codec
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer TRUNKPEER detailed"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; codec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Codecs (inbound): (ulaw|alaw|gsm)
Codecs (outbound): (ulaw|alaw|gsm)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Review SIP Configuration for Agent Channels
&lt;/h3&gt;

&lt;p&gt;Edit your ViciDial SIP configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano /etc/asterisk/sip-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Search for the agent peer definition. A typical configuration looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="m"&gt;6001&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;peer&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="n"&gt;dynamic&lt;/span&gt;
&lt;span class="n"&gt;secret&lt;/span&gt;=&lt;span class="n"&gt;agentpassword&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;alaw&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;gsm&lt;/span&gt;
&lt;span class="n"&gt;dtmfmode&lt;/span&gt;=&lt;span class="n"&gt;rfc2833&lt;/span&gt;
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;qualify&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure your agent device (softphone, desk phone) supports at least one of these codecs. Common issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Only allow=ulaw is set&lt;/strong&gt; - But agent phone only supports opus or g729&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nat=no&lt;/strong&gt; - But agent is behind NAT, causing one-way audio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dtmfmode=info&lt;/strong&gt; - May conflict with certain PBXes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Review Carrier/Trunk Codec Configuration
&lt;/h3&gt;

&lt;p&gt;Check trunk configurations in sip-vicidial.conf or extensions-vicidial.conf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 20 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="s2"&gt;carrier&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;trunk"&lt;/span&gt; /etc/asterisk/sip-vicidial.conf | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example proper trunk peer definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;mycarrier&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;peer&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="n"&gt;carrier&lt;/span&gt;.&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;secret&lt;/span&gt;=&lt;span class="n"&gt;carrierpassword&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;carrier&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;alaw&lt;/span&gt;
&lt;span class="n"&gt;fromuser&lt;/span&gt;=&lt;span class="n"&gt;myaccount&lt;/span&gt;
&lt;span class="n"&gt;fromdomain&lt;/span&gt;=&lt;span class="n"&gt;carrier&lt;/span&gt;.&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;insecure&lt;/span&gt;=&lt;span class="n"&gt;port&lt;/span&gt;,&lt;span class="n"&gt;invite&lt;/span&gt;
&lt;span class="n"&gt;qualify&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Codec Negotiation in Call Logs
&lt;/h3&gt;

&lt;p&gt;After running debug, search the logs for codec negotiation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"codec"&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for messages indicating codec selection or rejection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[2024-01-15 14:23:45] Peer 'AGENTPEER' is now Reachable
[2024-01-15 14:23:52] Setting RTP codecs based on INVITE: (ulaw|alaw|gsm)
[2024-01-15 14:23:52] Answering call from AGENTPEER with codec ulaw
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see codec mismatch messages, your trunk and agent don't share compatible codecs. Solution: Add matching codecs to both peer definitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 3: RTP and Media Path Diagnosis
&lt;/h2&gt;

&lt;p&gt;RTP (Real-time Transport Protocol) carries the actual audio. One-way audio often indicates RTP flowing in only one direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check RTP Port Configuration
&lt;/h3&gt;

&lt;p&gt;Verify RTP port range in asterisk.conf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"rtpstart&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;rtpend"&lt;/span&gt; /etc/asterisk/asterisk.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Default configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;rtp&lt;/span&gt;]
&lt;span class="n"&gt;rtpstart&lt;/span&gt;=&lt;span class="m"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;rtpend&lt;/span&gt;=&lt;span class="m"&gt;20000&lt;/span&gt;
&lt;span class="n"&gt;tos&lt;/span&gt;=&lt;span class="n"&gt;ef&lt;/span&gt;
&lt;span class="n"&gt;cos&lt;/span&gt;=&lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure your firewall allows this entire range. Check current firewall rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status | &lt;span class="nb"&gt;grep &lt;/span&gt;10000
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-L&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;10000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If not visible, open the RTP port range:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 10000:20000/udp
&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10000-20000/udp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Diagnose NAT Issues
&lt;/h3&gt;

&lt;p&gt;One-way audio frequently occurs when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Agent is behind NAT, trunk is public&lt;/strong&gt; - Agent can hear trunk (outbound RTP works), trunk can't hear agent (return RTP blocked)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both behind NAT&lt;/strong&gt; - NAT traversal needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Check if peers are behind NAT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer AGENTPEER"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; nat
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer CARRIER"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; nat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correct NAT configurations:&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;agents behind NAT&lt;/strong&gt; (typical softphone scenario):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;agentpeer&lt;/span&gt;]
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;directmedia&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;strong&gt;carrier/trunk behind NAT&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;trunk&lt;/span&gt;]
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;directmedia&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;directmedia=no&lt;/code&gt; forces Asterisk to remain in the media path, preventing direct RTP between endpoints that can't reach each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analyze RTP Packet Flow
&lt;/h3&gt;

&lt;p&gt;Use tcpdump to capture RTP packets during a call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Capture RTP from a specific source/destination&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; any &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'udp and portrange 10000-20000'&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; rtp_capture.pcap

&lt;span class="c"&gt;# In another terminal, reproduce the issue and let it run for 30 seconds&lt;/span&gt;
&lt;span class="c"&gt;# Then Ctrl+C the tcpdump&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Analyze the capture with Wireshark or tshark:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tshark &lt;span class="nt"&gt;-r&lt;/span&gt; rtp_capture.pcap &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'rtp'&lt;/span&gt; &lt;span class="nt"&gt;-Y&lt;/span&gt; &lt;span class="s1"&gt;'rtp'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One direction has packets, other has none&lt;/strong&gt; - Classic one-way audio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asymmetric packet counts&lt;/strong&gt; - Unbalanced conversation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Packets with 0 length&lt;/strong&gt; - Malformed RTP&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Check Asterisk RTP Statistics
&lt;/h3&gt;

&lt;p&gt;During an active call, check RTP status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtp show stats"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output shows all active RTP streams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RTP Stats:
  RTP Stream #1: 192.168.1.100:5060 -&amp;gt; 203.0.113.50:5060
    Rx Packets: 1250, Rx Bytes: 75000
    Tx Packets: 0, Tx Bytes: 0
    RTT: 45ms, Jitter: 12ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;strong&gt;Rx Packets &amp;gt; 0 but Tx Packets = 0&lt;/strong&gt; (or vice versa), you have one-way audio. This indicates RTP is flowing inbound but not outbound.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 4: ViciDial-Specific Configuration Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Verify Bridge Settings in extensions-vicidial.conf
&lt;/h3&gt;

&lt;p&gt;ViciDial creates bridges between agents and callers using dialplan. Check the bridge configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 30 &lt;span class="s2"&gt;"exten =&amp;gt; _X.,1,"&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for bridge commands like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;.,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Bridge&lt;/span&gt;(${&lt;span class="n"&gt;CALLVAR&lt;/span&gt;},${&lt;span class="n"&gt;BRIDGEOPTIONS&lt;/span&gt;})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common bridge option misconfigurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;; &lt;span class="n"&gt;WRONG&lt;/span&gt; - &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;audio&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;caller&lt;/span&gt;
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;6001&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Bridge&lt;/span&gt;(&lt;span class="n"&gt;SIP&lt;/span&gt;/&lt;span class="n"&gt;agentpeer&lt;/span&gt;,&lt;span class="n"&gt;p&lt;/span&gt;)

; &lt;span class="n"&gt;CORRECT&lt;/span&gt; - &lt;span class="n"&gt;Bidirectional&lt;/span&gt; &lt;span class="n"&gt;audio&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;proper&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;6001&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Bridge&lt;/span&gt;(&lt;span class="n"&gt;SIP&lt;/span&gt;/&lt;span class="n"&gt;agentpeer&lt;/span&gt;,&lt;span class="n"&gt;mwT&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bridge option meanings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;m&lt;/code&gt; - Play music on hold while calling&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;w&lt;/code&gt; - Allow attended transfers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;T&lt;/code&gt; - Allow DTMF transfers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;p&lt;/code&gt; - Pass through DTMF (may break audio)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Check ViciDial Call Recording Configuration
&lt;/h3&gt;

&lt;p&gt;Call recording sometimes interferes with audio paths. Check recording settings in /etc/asterisk/extensions-vicidial.conf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"MixMonitor&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;Monitor"&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the recording directory exists and has proper permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /var/spool/asterisk/monitor/
&lt;span class="c"&gt;# Should output:&lt;/span&gt;
&lt;span class="c"&gt;# drwxr-xr-x  asterisk asterisk  /var/spool/asterisk/monitor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If recording is interfering, temporarily disable it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# In extensions-vicidial.conf, comment out MixMonitor lines&lt;/span&gt;
&lt;span class="c"&gt;# exten =&amp;gt; _X.,2,MixMonitor(/var/spool/asterisk/monitor/${MIXMONITOR_FILENAME},${AUDIOHOOKS})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart dialplan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retest the call. If audio works with recording disabled, the recording configuration is the issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify Agent Channel Context
&lt;/h3&gt;

&lt;p&gt;Agents connect via SIP. Verify their context points to proper dialplan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer 6001"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;Context&lt;/span&gt;: &lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;internal&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This context must exist and contain the proper dialplan. Check it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 20 &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="s2"&gt;from-internal&lt;/span&gt;&lt;span class="se"&gt;\]&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Should contain logic to route calls to agents or queues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check Dialplan Variables Affecting Audio
&lt;/h3&gt;

&lt;p&gt;Some ViciDial dialplan variable misconfigurations cause audio issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"CALLVAR&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;BRIDGEOPTIONS&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;AUDIOHOOKS"&lt;/span&gt; /etc/asterisk/extensions-vicidial.conf | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These variables should be properly set before bridge execution. If missing or malformed, one-way audio results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 5: Carrier and Trunk Issues
&lt;/h2&gt;

&lt;p&gt;One-way audio often originates from carrier/trunk misconfiguration, especially for inbound calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query ViciDial Carrier Configuration
&lt;/h3&gt;

&lt;p&gt;Check configured carriers in the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_carrier_list&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;carrier_active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Y'&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;carrier_name&lt;/code&gt; - Identifier in config&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;carrier_tech&lt;/code&gt; - Usually SIP&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;outbound_carrier_id&lt;/code&gt; - Used for outbound calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_carrier_log&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;MINUTE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Trunk Peer Definition Matches Carrier Config
&lt;/h3&gt;

&lt;p&gt;Find the carrier in sip-vicidial.conf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 30 &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="s2"&gt;carrier_name&lt;/span&gt;&lt;span class="se"&gt;\]&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /etc/asterisk/sip-vicidial.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example proper carrier definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;myvoipcarrier&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;peer&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="n"&gt;sip&lt;/span&gt;.&lt;span class="n"&gt;carrier&lt;/span&gt;.&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;secret&lt;/span&gt;=&lt;span class="n"&gt;carrier_auth_secret&lt;/span&gt;
&lt;span class="n"&gt;fromuser&lt;/span&gt;=&lt;span class="n"&gt;account_id&lt;/span&gt;
&lt;span class="n"&gt;fromdomain&lt;/span&gt;=&lt;span class="n"&gt;sip&lt;/span&gt;.&lt;span class="n"&gt;carrier&lt;/span&gt;.&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;from&lt;/span&gt;-&lt;span class="n"&gt;carrier&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;nat&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;directmedia&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;insecure&lt;/span&gt;=&lt;span class="n"&gt;port&lt;/span&gt;,&lt;span class="n"&gt;invite&lt;/span&gt;
&lt;span class="n"&gt;qualify&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt;=&lt;span class="m"&gt;5060&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test Carrier Connectivity
&lt;/h3&gt;

&lt;p&gt;Verify the trunk is registered and reachable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show registry"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output shows registered trunks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host                            Username    Refresh State       Reg.Expires ID
sip.carrier.com:5060           account_id  105     Registered  3600       0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If status is "Unregistered" or missing, the carrier configuration is incorrect. Check registration credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-B&lt;/span&gt; 5 &lt;span class="nt"&gt;-A&lt;/span&gt; 15 &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="s2"&gt;myvoipcarrier&lt;/span&gt;&lt;span class="se"&gt;\]&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /etc/asterisk/sip-vicidial.conf | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"fromuser|secret|host"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check Carrier Audio Codecs Against Agent Codecs
&lt;/h3&gt;

&lt;p&gt;This is critical. Many carriers only support ulaw. Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer myvoipcarrier detailed"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; codec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If carrier only supports ulaw but your agents are sending gsm, one-way audio occurs during transcoding.&lt;/p&gt;

&lt;p&gt;Solution: Align codecs between carrier and agent peers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;myvoipcarrier&lt;/span&gt;]
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;alaw&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;agentpeer&lt;/span&gt;]
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;alaw&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitor Active Trunk Calls
&lt;/h3&gt;

&lt;p&gt;Check what calls are currently using trunks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show channels"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;carrier
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shows active channels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Peer             User/ANR          Call ID              Seq (Tx/Rx)  Lag    Jitter   Time
myvoipcarrier    account_id        [call_id]            102/103      0 ms  0 ms     0:00:15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 6: Advanced Debugging Techniques
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Capture Full SIP Trace for Single Call
&lt;/h3&gt;

&lt;p&gt;Create a granular SIP trace for one specific call. First, identify an inbound number or agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For agent 6001:&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug peer 6001"&lt;/span&gt;

&lt;span class="c"&gt;# For carrier trunk:&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug peer myvoipcarrier"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reproduce the issue, then immediately stop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug off"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extract the trace from logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"6001&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;myvoipcarrier"&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"INVITE|100 Trying|180 Ringing|200 OK|ACK|BYE"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/sip_trace.log
&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/sip_trace.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;INVITE from agent&lt;/strong&gt; - SIP setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;200 OK with SDP&lt;/strong&gt; - Media parameters exchanged&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACK&lt;/strong&gt; - Confirmation received&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RTP port assignment in SDP&lt;/strong&gt; - Is it the same for both directions?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Parse SDP Offers and Answers
&lt;/h3&gt;

&lt;p&gt;SDP (Session Description Protocol) defines media parameters. A typical problematic SDP exchange:&lt;/p&gt;

&lt;p&gt;Agent INVITE SDP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;m=audio 10000 RTP/AVP 0 8 3
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trunk 200 OK SDP (with mismatched port):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;m=audio 15432 RTP/AVP 0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ports differ (10000 vs 15432). If Asterisk doesn't properly manage this bridge, one-way audio results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable Asterisk Memory and Resource Monitoring
&lt;/h3&gt;

&lt;p&gt;One-way audio sometimes indicates resource exhaustion. Monitor during calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"memory show summary"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name                          Allocated  Freed      In Use
astobj2                       2097152    0          2097152
cache                         2097152    0          1048576
channel                       524288     0          262144
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Excessive memory usage indicates a leak. Check file descriptor limits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ulimit&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;
&lt;span class="c"&gt;# Increase if needed:&lt;/span&gt;
&lt;span class="nb"&gt;ulimit&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 65536
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Monitor CPU usage during problematic calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;top &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;pgrep asterisk&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Should show &amp;lt; 80% CPU for normal operation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Verbose Logging for Dialplan Execution
&lt;/h3&gt;

&lt;p&gt;Enable verbose dialplan logging to see call flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core set verbose 7"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During a call, logs will show:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;[2024-01-15 14:35:42] -- &amp;lt;SIP/agentpeer-00000001&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;executing &lt;span class="o"&gt;[&lt;/span&gt;6001@from-internal:1] &lt;span class="k"&gt;in &lt;/span&gt;new stack
&lt;span class="go"&gt;[2024-01-15 14:35:42] -- Executing [6001@from-internal:1] Bridge("SIP/agentpeer-00000001", "SIP/trunk")
[2024-01-15 14:35:45] -- SIP/trunk-00000002 answered SIP/agentpeer-00000001
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the bridge fails or doesn't execute, dialplan is the issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 7: Troubleshooting Checklist
&lt;/h2&gt;

&lt;p&gt;Use this systematic checklist when one-way audio is reported:&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial Verification
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Call actually exists (check vicidial_log)&lt;/li&gt;
&lt;li&gt;[ ] Asterisk is running (&lt;code&gt;asterisk -rx "core show version"&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;[ ] SIP and RTP are active (&lt;code&gt;asterisk -rx "sip show status"&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Audio Path Verification
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Agent phone is receiving audio (one-way confirmed)&lt;/li&gt;
&lt;li&gt;[ ] Caller ID displays correctly (signaling working)&lt;/li&gt;
&lt;li&gt;[ ] Call duration shows progress (not immediate hangup)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Codec Analysis
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Agent phone supports configured codec&lt;/li&gt;
&lt;li&gt;[ ] Carrier/trunk supports same codec&lt;/li&gt;
&lt;li&gt;[ ] No codec transcoding required&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;disallow=all&lt;/code&gt; used before &lt;code&gt;allow=&lt;/code&gt; statements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Network and RTP
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Firewall allows RTP port range (10000:20000)&lt;/li&gt;
&lt;li&gt;[ ] No VPN or security appliances blocking RTP&lt;/li&gt;
&lt;li&gt;[ ] NAT settings correct for peer location&lt;/li&gt;
&lt;li&gt;[ ] RTP statistics show bidirectional packet flow&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ViciDial Configuration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] sip-vicidial.conf peers properly defined&lt;/li&gt;
&lt;li&gt;[ ] extensions-vicidial.conf bridge options correct&lt;/li&gt;
&lt;li&gt;[ ] Agent context points to proper dialplan&lt;/li&gt;
&lt;li&gt;[ ] Recording configuration not interfering&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Carrier/Trunk Verification
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Trunk is registered (&lt;code&gt;sip show registry&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;[ ] Trunk responds to qualify checks&lt;/li&gt;
&lt;li&gt;[ ] Trunk codec compatible with agents&lt;/li&gt;
&lt;li&gt;[ ] Trunk definition syntax correct&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Section 8: Common Scenarios and Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scenario 1: Agent Hears Caller, Caller Cannot Hear Agent
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Typical Cause:&lt;/strong&gt; Agent RTP is not reaching the carrier/external caller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtp show stats"&lt;/span&gt;
&lt;span class="c"&gt;# Look for stream with Rx packets but 0 Tx packets from agent side&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check NAT settings: &lt;code&gt;asterisk -rx "sip show peer agentpeer" | grep nat&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;nat=yes&lt;/code&gt; and &lt;code&gt;directmedia=no&lt;/code&gt; for agent peer&lt;/li&gt;
&lt;li&gt;Reload SIP config: &lt;code&gt;asterisk -rx "sip reload"&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Scenario 2: Caller Hears Agent, Agent Cannot Hear Caller
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Typical Cause:&lt;/strong&gt; Caller RTP (from carrier) is not reaching agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;carrier_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;call_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;DATE_SUB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;MINUTE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the carrier, then check its codec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peer [carrier_name] detailed"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;codec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify agent allows carrier's codec&lt;/li&gt;
&lt;li&gt;Add codec to agent peer: &lt;code&gt;allow=[carrier_codec]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ensure directmedia=no on both sides&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Scenario 3: Both Directions Have Audio, But Intermittent Cutouts
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Typical Cause:&lt;/strong&gt; Jitter, packet loss, or RTP port exhaustion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check for port exhaustion&lt;/span&gt;
netstat &lt;span class="nt"&gt;-an&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;ESTABLISHED | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="c"&gt;# If &amp;gt; 50,000, approaching limit&lt;/span&gt;

&lt;span class="c"&gt;# Check jitter on active calls&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtp show stats"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; jitter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Increase RTP port range in asterisk.conf:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;rtp&lt;/span&gt;]
&lt;span class="n"&gt;rtpstart&lt;/span&gt;=&lt;span class="m"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;rtpend&lt;/span&gt;=&lt;span class="m"&gt;30000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Restart Asterisk&lt;/li&gt;
&lt;li&gt;Investigate network jitter with tcpdump&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Section 9: Performance Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Optimize Asterisk for ViciDial Load
&lt;/h3&gt;

&lt;p&gt;For high-call-volume ViciDial environments, optimize Asterisk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit /etc/asterisk/asterisk.conf&lt;/span&gt;
nano /etc/asterisk/asterisk.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add these settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;options&lt;/span&gt;]
&lt;span class="n"&gt;threadpool_initial_size&lt;/span&gt;=&lt;span class="m"&gt;50&lt;/span&gt;
&lt;span class="n"&gt;threadpool_max_size&lt;/span&gt;=&lt;span class="m"&gt;500&lt;/span&gt;
&lt;span class="n"&gt;cache_record_buffers&lt;/span&gt;=&lt;span class="m"&gt;128&lt;/span&gt;
&lt;span class="n"&gt;maxcalls&lt;/span&gt;=&lt;span class="m"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;maxload&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;9&lt;/span&gt;
&lt;span class="n"&gt;maxfiles&lt;/span&gt;=&lt;span class="m"&gt;65536&lt;/span&gt;

[&lt;span class="n"&gt;rtp&lt;/span&gt;]
&lt;span class="n"&gt;rtpstart&lt;/span&gt;=&lt;span class="m"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;rtpend&lt;/span&gt;=&lt;span class="m"&gt;30000&lt;/span&gt;
&lt;span class="n"&gt;tos&lt;/span&gt;=&lt;span class="n"&gt;ef&lt;/span&gt;
&lt;span class="n"&gt;cos&lt;/span&gt;=&lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;dtmf_mode&lt;/span&gt;=&lt;span class="n"&gt;rfc2833_fax&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tune SIP Settings for Reliability
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/sip.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
&lt;span class="c"&gt;# Increase retransmit timers for carrier reliability
&lt;/span&gt;&lt;span class="n"&gt;retransmission_timer&lt;/span&gt;=&lt;span class="m"&gt;500&lt;/span&gt;
&lt;span class="n"&gt;max_retransmissions&lt;/span&gt;=&lt;span class="m"&gt;6&lt;/span&gt;

&lt;span class="c"&gt;# Improve scalability
&lt;/span&gt;&lt;span class="n"&gt;srvlookup&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;pedantic&lt;/span&gt;=&lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;notifyringing&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;alaw&lt;/span&gt;

&lt;span class="c"&gt;# NAT settings
&lt;/span&gt;&lt;span class="n"&gt;localnet&lt;/span&gt;=&lt;span class="m"&gt;192&lt;/span&gt;.&lt;span class="m"&gt;168&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;/&lt;span class="m"&gt;255&lt;/span&gt;.&lt;span class="m"&gt;255&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;externip&lt;/span&gt;=&lt;span class="m"&gt;203&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;113&lt;/span&gt;.&lt;span class="m"&gt;50&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitor Call Quality Metrics
&lt;/h3&gt;

&lt;p&gt;Create a monitoring script to track audio issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# save as /usr/local/bin/monitor_vicidial_audio.sh&lt;/span&gt;

&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; ==="&lt;/span&gt;

    &lt;span class="c"&gt;# Check active calls&lt;/span&gt;
    &lt;span class="nv"&gt;ACTIVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show calls"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"active calls"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Active calls: &lt;/span&gt;&lt;span class="nv"&gt;$ACTIVE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# Check codec mismatches&lt;/span&gt;
    &lt;span class="nv"&gt;MISMATCHES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"codec mismatch"&lt;/span&gt; /var/log/asterisk/messages&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Codec mismatches: &lt;/span&gt;&lt;span class="nv"&gt;$MISMATCHES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# Check RTP errors&lt;/span&gt;
    &lt;span class="nv"&gt;RTP_ERRS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"RTP.*error&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;RTP.*timeout"&lt;/span&gt; /var/log/asterisk/messages&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"RTP errors: &lt;/span&gt;&lt;span class="nv"&gt;$RTP_ERRS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;sleep &lt;/span&gt;60
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/monitor_vicidial_audio.sh
/usr/local/bin/monitor_vicidial_audio.sh &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Common Mistakes When Troubleshooting One-Way Audio
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1: Only reloading SIP without reloading dialplan&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# WRONG - Incomplete reload&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip reload"&lt;/span&gt;

&lt;span class="c"&gt;# CORRECT - Both modules&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip reload"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Mistake 2: Not checking both ends of the call&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One-way audio is directional. You must check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent peer config AND carrier peer config&lt;/li&gt;
&lt;li&gt;Agent RTP port range AND carrier port range&lt;/li&gt;
&lt;li&gt;Agent NAT setting AND carrier NAT setting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3: Assuming all one-way audio has same root cause&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cause depends on direction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent→Caller one-way? Check agent RTP and NAT&lt;/li&gt;
&lt;li&gt;Caller→Agent one-way? Check carrier RTP and codec&lt;/li&gt;
&lt;li&gt;Both intermittent? Check jitter and packet loss&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Persistent One-Way Audio After Configuration Changes
&lt;/h3&gt;

&lt;p&gt;If one-way audio persists after following this guide:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Full Asterisk restart&lt;/strong&gt; (not reload):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl stop asterisk
&lt;span class="nb"&gt;sleep &lt;/span&gt;5
systemctl start asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clear SIP cache and registrations&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip prune realtime all"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip prune peers"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check for conflicting configurations&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Look for duplicate peer definitions&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /etc/asterisk/sip-vicidial.conf | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"agentpeer|carrier"&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If duplicates exist, remove them.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify database consistency&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Check for agents with malformed SIP config&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%%'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;user_phone_type&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SIP'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'IAX2'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Escalation: Capture full trace&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If none of above resolves it, capture comprehensive data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set everything to debug&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip set debug on"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"rtp set debug on"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core set verbose 10"&lt;/span&gt;

&lt;span class="c"&gt;# Let it log for 30 seconds during problem call&lt;/span&gt;
&lt;span class="c"&gt;# Then save logs&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; /var/log/asterisk/messages /tmp/vicidial_audio_debug_&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;.log

&lt;span class="c"&gt;# Analyze&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"INVITE&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;200 OK&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;ACK&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;RTP"&lt;/span&gt; /tmp/vicidial_audio_debug_&lt;span class="k"&gt;*&lt;/span&gt;.log | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;One-way audio in ViciDial/Asterisk environments typically results from codec mismatches, RTP path failures, or configuration errors. Use this systematic approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Confirm the issue&lt;/strong&gt; with database queries and call logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable debugging&lt;/strong&gt; to see SIP and RTP flows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check codecs&lt;/strong&gt; on both agent and carrier peers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify RTP connectivity&lt;/strong&gt; with tcpdump and packet analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review ViciDial-specific&lt;/strong&gt; bridge and dialplan configurations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspect carrier/trunk&lt;/strong&gt; definitions and registration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use advanced tools&lt;/strong&gt; like SDP analysis and RTP statistics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor continuously&lt;/strong&gt; to prevent recurrence&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most common fixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;nat=yes&lt;/code&gt; and &lt;code&gt;directmedia=no&lt;/code&gt; for peers behind NAT&lt;/li&gt;
&lt;li&gt;Align codecs between all SIP peers&lt;/li&gt;
&lt;li&gt;Ensure firewall allows RTP port range&lt;/li&gt;
&lt;li&gt;Verify bridge options include &lt;code&gt;m&lt;/code&gt;, &lt;code&gt;w&lt;/code&gt;, and &lt;code&gt;T&lt;/code&gt; flags&lt;/li&gt;
&lt;li&gt;Restart Asterisk (not just reload) after major config changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this guide and methodical debugging, you'll resolve even complex one-way audio issues in production ViciDial environments.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
    <item>
      <title>ViciDial Agent Screen Layout &amp; Custom CRM Fields</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Fri, 17 Jul 2026 03:30:03 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/vicidial-agent-screen-layout-custom-crm-fields-459a</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/vicidial-agent-screen-layout-custom-crm-fields-459a</guid>
      <description>&lt;h1&gt;
  
  
  ViciDial Agent Screen Layout &amp;amp; Custom CRM Fields
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Master the art of customizing your ViciDial agent screen interface, configuring dynamic CRM fields, and optimizing call center workflows for maximum productivity and data capture.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before diving into agent screen customization, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ViciDial 2.14 or higher installed and operational&lt;/li&gt;
&lt;li&gt;Root or sudo access to your ViciDial server&lt;/li&gt;
&lt;li&gt;MySQL/MariaDB database access with administrative privileges&lt;/li&gt;
&lt;li&gt;Basic understanding of ViciDial user roles and permissions&lt;/li&gt;
&lt;li&gt;Asterisk 13+ running alongside ViciDial&lt;/li&gt;
&lt;li&gt;Direct access to &lt;code&gt;/usr/src/astguiclient/&lt;/code&gt; directory structure&lt;/li&gt;
&lt;li&gt;A test campaign and test user account created&lt;/li&gt;
&lt;li&gt;Backup of your database and configuration files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify your installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SELECT VERSION();"&lt;/span&gt;
asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show version"&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /usr/src/astguiclient/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding ViciDial Agent Screen Architecture
&lt;/h2&gt;

&lt;p&gt;The ViciDial agent screen (&lt;code&gt;/agc/vicidial.php&lt;/code&gt;) is the primary interface where call center agents interact with leads, manage calls, and capture customer data. The screen's layout and available fields directly impact agent efficiency and data quality.&lt;/p&gt;

&lt;p&gt;The agent screen pulls configuration from multiple sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database tables (&lt;code&gt;vicidial_lists&lt;/code&gt;, &lt;code&gt;vicidial_campaigns&lt;/code&gt;, &lt;code&gt;custom_fields&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Campaign-specific settings&lt;/li&gt;
&lt;li&gt;User permission levels&lt;/li&gt;
&lt;li&gt;Script definitions&lt;/li&gt;
&lt;li&gt;Custom field mappings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core agent screen PHP file loads JavaScript modules that dynamically render the interface based on database queries. Every field, button, and display element can be customized through the database or direct PHP modification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing Agent Screen Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Via Web Administration Panel
&lt;/h3&gt;

&lt;p&gt;The primary method for most users involves the ViciDial admin panel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://your-vicidial-server/vicidial/admin.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Login with an admin account, then navigate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Admin → Campaigns → Select Campaign → Agent Screen Layout&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This interface provides a visual editor for reordering and enabling/disabling default fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  Database Direct Access
&lt;/h3&gt;

&lt;p&gt;For advanced customization, query the database directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; vicidial &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key tables for agent screen configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Check campaign-specific agent screen settings&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;screen_layout&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_CAMPAIGN'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- View all custom fields in the system&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;field_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt; 
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;field_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Check which fields are assigned to a campaign&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_fields&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'XXXXX'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  File System Configuration
&lt;/h3&gt;

&lt;p&gt;The actual agent screen PHP file is located at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/src/astguiclient/agc/vicidial.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this is the compiled version. The source files are typically in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/src/astguiclient/src/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Core configuration for screen appearance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/etc/asterisk/vicidial.conf
/usr/src/astguiclient/conf/vicidial_client.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Default Agent Screen Fields
&lt;/h2&gt;

&lt;p&gt;ViciDial includes these standard fields by default:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field Name&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Database Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lead ID&lt;/td&gt;
&lt;td&gt;Unique lead identifier&lt;/td&gt;
&lt;td&gt;vicidial_list.lead_id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phone Number&lt;/td&gt;
&lt;td&gt;Contact number&lt;/td&gt;
&lt;td&gt;vicidial_list.phone_number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;td&gt;Call disposition&lt;/td&gt;
&lt;td&gt;vicidial_log.status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;Contact name&lt;/td&gt;
&lt;td&gt;vicidial_list.first_name, last_name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Address&lt;/td&gt;
&lt;td&gt;Contact address&lt;/td&gt;
&lt;td&gt;vicidial_list.address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;City/State/Zip&lt;/td&gt;
&lt;td&gt;Location data&lt;/td&gt;
&lt;td&gt;vicidial_list.city, state, postal_code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;td&gt;Contact email&lt;/td&gt;
&lt;td&gt;vicidial_list.email&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owner&lt;/td&gt;
&lt;td&gt;Campaign owner&lt;/td&gt;
&lt;td&gt;vicidial_campaigns.owner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notes&lt;/td&gt;
&lt;td&gt;Call notes&lt;/td&gt;
&lt;td&gt;vicidial_log.notes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Call Recording&lt;/td&gt;
&lt;td&gt;Audio link&lt;/td&gt;
&lt;td&gt;vicidial_log.uniqueid&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Creating and Managing Custom CRM Fields
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating New Custom Fields
&lt;/h3&gt;

&lt;p&gt;Access the custom fields management section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://your-vicidial-server/vicidial/admin.php → 
Tools → Custom Fields
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the database directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;VALUES&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'customer_tier'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Customer Tier'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'SELECT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Custom field types supported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TEXT - Single line text input
TEXTAREA - Multi-line text area
SELECT - Dropdown selection
RADIOBUTTON - Radio button group
CHECKBOX - Checkbox option
HIDDEN - Hidden field (not displayed but stored)
DATE - Date picker
DATETIME - Date and time picker
EMAIL - Email validation field
NUMERIC - Number-only input
PHONE - Phone number formatting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Practical Custom Field Examples
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Example 1: Creating a Customer Tier field&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;VALUES&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'customer_tier'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'Customer Tier'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'SELECT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'Standard|Gold|Platinum'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example 2: Creating a Previous Purchase Amount field&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;VALUES&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'previous_purchase_amount'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Previous Purchase Amount ($)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'NUMERIC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example 3: Creating a Last Contact Date field&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;VALUES&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'last_contact_date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Last Contact Date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DATE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example 4: Creating a Compliance Notes field&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;VALUES&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'compliance_notes'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Compliance Notes'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'TEXTAREA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Assigning Custom Fields to Campaigns
&lt;/h3&gt;

&lt;p&gt;Once created, assign custom fields to specific campaigns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Get the campaign ID&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;campaign_name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaigns&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Sales_Campaign_001'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Assign custom field to campaign (assuming campaign_id = 101)&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_fields&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;campaign_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;VALUES&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;field_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;field_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'customer_tier'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reordering Custom Fields on Agent Screen
&lt;/h3&gt;

&lt;p&gt;Control field display order through the &lt;code&gt;field_position&lt;/code&gt; column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Update field positions for campaign 101&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_fields&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;field_position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;field_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;field_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;field_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'customer_tier'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_fields&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;field_position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;field_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;field_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;field_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'previous_purchase_amount'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_fields&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;field_position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;field_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;field_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;field_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'last_contact_date'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Modifying Agent Screen Layout via PHP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Understanding the Agent Screen Rendering
&lt;/h3&gt;

&lt;p&gt;The agent screen is primarily rendered by &lt;code&gt;/usr/src/astguiclient/agc/vicidial.php&lt;/code&gt;. This file queries the database and generates HTML/JavaScript dynamically.&lt;/p&gt;

&lt;p&gt;To customize the agent screen layout directly in PHP, you'll need to work with the source files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /usr/src/astguiclient/
&lt;span class="c"&gt;# Backup original&lt;/span&gt;
&lt;span class="nb"&gt;cp &lt;/span&gt;agc/vicidial.php agc/vicidial.php.backup

&lt;span class="c"&gt;# Edit the file&lt;/span&gt;
nano agc/vicidial.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example: Adding a Custom Section to the Agent Screen
&lt;/h3&gt;

&lt;p&gt;Most customizations involve modifying sections where lead data is displayed. The typical structure includes a form that displays lead information.&lt;/p&gt;

&lt;p&gt;Find the section where lead fields are rendered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;// Typical structure in vicidial.php
// Around line 2000-3000, you'll find the HTML form construction

// Example of adding a custom section after the standard fields
?&amp;gt;
&lt;span class="c"&gt;&amp;lt;!-- Custom CRM Section --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"customcrmtable"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Customer Tier:&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"customer_tier"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"customer_tier"&lt;/span&gt; &lt;span class="na"&gt;maxlength=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt; &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"30"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Previous Purchase:&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"previous_purchase_amount"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"previous_purchase_amount"&lt;/span&gt; &lt;span class="na"&gt;maxlength=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt; &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"30"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding JavaScript to Populate Custom Fields
&lt;/h3&gt;

&lt;p&gt;When a lead is loaded, custom fields should be populated from the database. Add JavaScript to handle this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Add this to vicidial.php JavaScript section&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;populate_custom_fields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/agc/ajax_load_custom_fields.php&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_custom_fields&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer_tier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer_tier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer_tier&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;previous_purchase_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;previous_purchase_amount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;previous_purchase_amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;last_contact_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;last_contact_date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;last_contact_date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating Backend PHP Scripts for Custom Field Handling
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating AJAX Handler for Custom Fields
&lt;/h3&gt;

&lt;p&gt;Create a new PHP file to handle custom field operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano /usr/src/astguiclient/agc/ajax_load_custom_fields.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="c1"&gt;// ajax_load_custom_fields.php - Handle custom field loading and saving&lt;/span&gt;

&lt;span class="c1"&gt;// Include ViciDial configuration&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/usr/src/astguiclient/conf/vicidial_client.conf'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Get database connection details from config&lt;/span&gt;
&lt;span class="nv"&gt;$DB_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$DB_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'vicidial'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$DB_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$DB_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'asterisk'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Connect to database&lt;/span&gt;
&lt;span class="nv"&gt;$mysqli&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;mysqli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$DB_server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$DB_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$DB_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$DB_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;connect_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Database connection failed'&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'action'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'action'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$lead_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'lead_id'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'lead_id'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'get_custom_fields'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$lead_id&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"
        SELECT field_name, field_value 
        FROM vicidial_custom_field_data 
        WHERE lead_id = ?
    "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'i'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$lead_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_result&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nv"&gt;$fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch_assoc&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'field_name'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'field_value'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fields&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'save_custom_fields'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$lead_id&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$field_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'field_name'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'field_name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$field_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'field_value'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'field_value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Check if record exists&lt;/span&gt;
    &lt;span class="nv"&gt;$check_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"
        SELECT id FROM vicidial_custom_field_data 
        WHERE lead_id = ? AND field_name = ?
    "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$check_stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$check_query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$check_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'is'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$check_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$check_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$check_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_result&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$check_result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;num_rows&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Update existing&lt;/span&gt;
        &lt;span class="nv"&gt;$update_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"
            UPDATE vicidial_custom_field_data 
            SET field_value = ?, date_modified = NOW()
            WHERE lead_id = ? AND field_name = ?
        "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$update_stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$update_query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$update_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sis'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$update_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'updated'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nv"&gt;$update_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Insert new&lt;/span&gt;
        &lt;span class="nv"&gt;$insert_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"
            INSERT INTO vicidial_custom_field_data 
            (lead_id, field_name, field_value, date_created) 
            VALUES (?, ?, ?, NOW())
        "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$insert_stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$insert_query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$insert_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'iss'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$insert_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'inserted'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nv"&gt;$insert_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$check_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ensuring Custom Field Data Table Exists
&lt;/h3&gt;

&lt;p&gt;Create the custom field data storage table if it doesn't exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;vicidial_custom_field_data&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lead_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;field_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;field_value&lt;/span&gt; &lt;span class="nb"&gt;LONGTEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;date_created&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;date_modified&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_lead_id&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_field_name&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;unique_lead_field&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InnoDB&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;CHARSET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;utf8mb4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Agent Screen Customization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Conditional Field Display Based on Campaign
&lt;/h3&gt;

&lt;p&gt;Display different custom fields depending on which campaign is active:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In vicidial.php, after loading campaign info&lt;/span&gt;
&lt;span class="nv"&gt;$campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_REQUEST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'campaign_id'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$campaign_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"
    SELECT vcf.field_id, cf.field_name, cf.field_label, cf.field_type
    FROM vicidial_campaign_fields vcf
    JOIN custom_fields cf ON vcf.field_id = cf.field_id
    WHERE vcf.campaign_id = ? AND vcf.active = 1
    ORDER BY vcf.field_position ASC
"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$campaign_query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'i'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$campaign_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_result&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$custom_fields_html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch_assoc&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$custom_fields_html&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$custom_fields_html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating Dynamic Field Dependencies
&lt;/h3&gt;

&lt;p&gt;Make certain fields appear/disappear based on other field values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Add to vicidial.php JavaScript section&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer_tier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;tier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Platinum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Show VIP fields&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;previous_purchase_amount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;table-row&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vip_notes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;table-row&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Hide VIP fields&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;previous_purchase_amount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vip_notes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom Screen Layout Presets
&lt;/h3&gt;

&lt;p&gt;Create multiple screen layout configurations for different agent types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create layout preference table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;agent_screen_layouts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;layout_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;layout_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;layout_description&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_group&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;field_configuration&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="nb"&gt;TINYINT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;date_created&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;date_modified&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InnoDB&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;CHARSET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;utf8mb4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Insert preset layouts&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;agent_screen_layouts&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;layout_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;VALUES&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'Sales Agent Layout'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'SALES'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'[{"field":"customer_tier","position":1,"visible":true},{"field":"previous_purchase_amount","position":2,"visible":true}]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;agent_screen_layouts&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;layout_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;VALUES&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'Support Agent Layout'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'SUPPORT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'[{"field":"issue_type","position":1,"visible":true},{"field":"priority","position":2,"visible":true}]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring Custom Field Validation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adding Server-Side Validation
&lt;/h3&gt;

&lt;p&gt;In your AJAX handler, add validation logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In ajax_load_custom_fields.php, add validation function&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validate_custom_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$field_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'NUMERIC'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;is_numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$field_value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Field must be numeric'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'EMAIL'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;filter_var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$field_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;FILTER_VALIDATE_EMAIL&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Invalid email format'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'PHONE'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;// Basic phone validation&lt;/span&gt;
            &lt;span class="nv"&gt;$phone_clean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/[^0-9]/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$phone_clean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Phone number too short'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'DATE'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;strtotime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$field_value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Invalid date format'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Use in save operation&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'save_custom_fields'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get field type first&lt;/span&gt;
    &lt;span class="nv"&gt;$type_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SELECT field_type FROM custom_fields WHERE field_name = ?"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$type_stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$type_query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$type_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$type_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$type_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$type_stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_result&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$type_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$type_result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch_assoc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate&lt;/span&gt;
    &lt;span class="nv"&gt;$validation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_custom_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$type_row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'field_type'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$validation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$validation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
        &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Proceed with save...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Client-Side Validation with JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Add to agent screen JavaScript&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validate_field_before_save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;field_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;field_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NUMERIC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field_value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please enter a valid number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EMAIL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;email_pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[^\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+@&lt;/span&gt;&lt;span class="se"&gt;[^\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.[^\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;email_pattern&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field_value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please enter a valid email address&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PHONE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;phone_clean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;field_value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phone_clean&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Phone number must be at least 10 digits&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Optimization for Custom Fields
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Indexing Custom Field Tables
&lt;/h3&gt;

&lt;p&gt;Ensure database queries remain fast with proper indexing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Optimize custom_fields table&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt; 
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_field_name&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_active&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Optimize custom field data table&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vicidial_custom_field_data&lt;/span&gt; 
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_lead_field&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_modified&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date_modified&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Caching Custom Field Configuration
&lt;/h3&gt;

&lt;p&gt;Implement caching to reduce database queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In vicidial.php or shared include file&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get_campaign_custom_fields_cached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$campaign_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$cache_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"campaign_fields_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$campaign_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Try to get from APC/APCu cache&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;extension_loaded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'apc'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;apc_fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cache_key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cached&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$cached&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Query database&lt;/span&gt;
    &lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"
        SELECT cf.field_id, cf.field_name, cf.field_label, cf.field_type
        FROM vicidial_campaign_fields vcf
        JOIN custom_fields cf ON vcf.field_id = cf.field_id
        WHERE vcf.campaign_id = ? AND vcf.active = 1
        ORDER BY vcf.field_position ASC
    "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'i'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$campaign_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_result&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nv"&gt;$fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch_assoc&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$fields&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Store in cache for 1 hour&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;extension_loaded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'apc'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;apc_store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cache_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$fields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Integrating with External CRM Systems
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Syncing Custom Fields to External CRM
&lt;/h3&gt;

&lt;p&gt;Create a background job to sync ViciDial custom fields to external systems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;// /usr/src/astguiclient/cronjobs/sync_crm_fields.php
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nb"&gt;set_time_limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;require_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/usr/src/astguiclient/conf/vicidial_client.conf'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$mysqli&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;mysqli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'vicidial'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'asterisk'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Get all leads with modified custom fields&lt;/span&gt;
&lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"
    SELECT DISTINCT vcfd.lead_id, vcfd.field_name, vcfd.field_value
    FROM vicidial_custom_field_data vcfd
    JOIN vicidial_list vl ON vcfd.lead_id = vl.lead_id
    WHERE vcfd.date_modified &amp;gt;= DATE_SUB(NOW(), INTERVAL 1 HOUR)
    ORDER BY vcfd.lead_id
"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch_assoc&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$lead_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'lead_id'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;$field_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'field_name'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;$field_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'field_value'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// Send to external CRM API&lt;/span&gt;
    &lt;span class="nf"&gt;send_to_external_crm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;send_to_external_crm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$field_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$crm_api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'https://api.externalcrm.com/leads/update'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$crm_api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'CRM_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'lead_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'field_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'field_value'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$field_value&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nv"&gt;$ch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$crm_api_url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_RETURNTRANSFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_HTTPHEADER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'Authorization: Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$crm_api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_POSTFIELDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schedule this with cron:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add to crontab for vicidial user&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;/15 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/bin/php /usr/src/astguiclient/cronjobs/sync_crm_fields.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting Agent Screen Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Custom Fields Not Appearing on Agent Screen
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Custom fields are created but don't show on the agent screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify fields are assigned to the campaign:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_fields&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;YOUR_CAMPAIGN_ID&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check if custom fields table exists:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'custom_fields'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'vicidial_custom_field_data'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Clear browser cache and refresh agent screen&lt;/li&gt;
&lt;li&gt;Check Asterisk logs:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/asterisk/messages | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"custom&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;field"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom Field Data Not Saving
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Agent enters data in custom fields but it doesn't persist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify AJAX script exists:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /usr/src/astguiclient/agc/ajax_load_custom_fields.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check database permissions:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; asterisk
SHOW GRANTS FOR &lt;span class="s1"&gt;'vicidial'&lt;/span&gt;@&lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Verify custom field data table structure:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DESCRIBE&lt;/span&gt; &lt;span class="n"&gt;vicidial_custom_field_data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Check browser console for JavaScript errors (F12)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Verify PHP error logging is enabled:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"error_log&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;display_errors"&lt;/span&gt; /etc/php.ini
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/php-errors.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Agent Screen Slow Performance with Many Custom Fields
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Agent screen loads slowly when many custom fields are present.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check if custom fields are properly indexed:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;custom_fields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_custom_field_data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add missing indexes:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vicidial_custom_field_data&lt;/span&gt; 
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_lead_id&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Limit fields loaded per campaign - don't load unused fields:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Disable inactive fields&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_fields&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;campaign_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;YOUR_CAMPAIGN_ID&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_campaign_fields&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Enable query caching in MySQL:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt;
SET GLOBAL query_cache_type &lt;span class="o"&gt;=&lt;/span&gt; ON&lt;span class="p"&gt;;&lt;/span&gt;
SET GLOBAL query_cache_size &lt;span class="o"&gt;=&lt;/span&gt; 268435456&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript Errors on Agent Screen
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Browser console shows JavaScript errors related to custom fields.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check if jQuery is loaded properly:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Open browser console and run&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Verify AJAX endpoint returns valid JSON:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost/agc/ajax_load_custom_fields.php &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"action=get_custom_fields&amp;amp;lead_id=12345"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add error handling to AJAX calls:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/agc/ajax_load_custom_fields.php&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_custom_fields&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;dataType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AJAX Error: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Response: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom Field Values Displaying Incorrectly
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Custom field values show strange characters or encoding issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify database character encoding:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vicidial_custom_field_data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Should show DEFAULT CHARSET=utf8mb4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Fix encoding if needed:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vicidial_custom_field_data&lt;/span&gt; 
&lt;span class="k"&gt;CONVERT&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="nb"&gt;CHARACTER&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;utf8mb4&lt;/span&gt; &lt;span class="k"&gt;COLLATE&lt;/span&gt; &lt;span class="n"&gt;utf8mb4_unicode_ci&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Ensure PHP file has UTF-8 header:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type: application/json; charset=utf-8'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check field_value column type supports required length:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DESCRIBE&lt;/span&gt; &lt;span class="n"&gt;vicidial_custom_field_data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- field_value should be LONGTEXT or TEXT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Mastering ViciDial agent screen customization and custom CRM fields provides significant advantages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Agent Screen Architecture&lt;/strong&gt; - Understand that the agent screen draws from multiple database sources and can be customized both through the admin panel and direct PHP/database modifications.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Custom Fields Foundation&lt;/strong&gt; - Create custom fields through the admin interface or database inserts, then assign them to campaigns with proper positioning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Storage&lt;/strong&gt; - Implement a dedicated custom field data table (&lt;code&gt;vicidial_custom_field_data&lt;/code&gt;) for storing field values per lead, enabling scalability and efficiency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AJAX Integration&lt;/strong&gt; - Build backend PHP scripts that handle field loading and saving asynchronously, providing a responsive agent experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validation&lt;/strong&gt; - Implement both client-side JavaScript and server-side PHP validation to ensure data quality and prevent invalid entries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt; - Index tables properly, implement caching strategies, and avoid loading unnecessary fields to maintain agent screen responsiveness.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;External Integration&lt;/strong&gt; - Create cronjobs to sync custom fields with external CRM systems, enabling unified customer data across platforms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Troubleshooting&lt;/strong&gt; - Systematically verify database structure, permissions, JavaScript errors, and encoding issues when problems arise.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The agent screen is the core of agent productivity. By investing time in proper customization, validation, and optimization, you create an environment where agents work efficiently while capturing high-quality data that drives business intelligence and customer relationship management.&lt;/p&gt;

&lt;p&gt;Always backup your database before making structural changes, and test customizations in a staging environment before deploying to production. Monitor performance metrics after implementing custom fields, and use database indexes strategically to maintain responsiveness as your custom field data grows.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
    <item>
      <title>Asterisk Voicemail System — Setup, Email Integration &amp; IVR</title>
      <dc:creator>Moisi Trungu</dc:creator>
      <pubDate>Thu, 16 Jul 2026 03:30:03 +0000</pubDate>
      <link>https://dev.to/moisi_trungu_31647b7ac300/asterisk-voicemail-system-setup-email-integration-ivr-1k93</link>
      <guid>https://dev.to/moisi_trungu_31647b7ac300/asterisk-voicemail-system-setup-email-integration-ivr-1k93</guid>
      <description>&lt;h1&gt;
  
  
  Asterisk Voicemail System — Setup, Email Integration &amp;amp; IVR
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Learn to configure production-grade voicemail in Asterisk with email notifications, custom greetings, transcription, and IVR routing for ViciDial environments.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting this tutorial, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asterisk 13.x or higher&lt;/strong&gt; installed and running (ViciDial typically runs 11.x through 16.x)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root or sudo access&lt;/strong&gt; to your Asterisk server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL/MariaDB&lt;/strong&gt; with an existing Asterisk database (vicidial user with appropriate permissions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postfix or Exim&lt;/strong&gt; mail server configured and tested&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sox&lt;/strong&gt; and &lt;strong&gt;mailx&lt;/strong&gt; utilities installed for audio handling and mail delivery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asterisk source code&lt;/strong&gt; downloaded (for custom modules if needed)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ViciDial version 2.12+&lt;/strong&gt; (if integrating with ViciDial's agent interface)&lt;/li&gt;
&lt;li&gt;Working SIP extensions or IAX2 trunks configured&lt;/li&gt;
&lt;li&gt;Basic understanding of Asterisk dialplan syntax and Linux command line&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify Asterisk is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show version"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status asterisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding Asterisk Voicemail Architecture
&lt;/h2&gt;

&lt;p&gt;Asterisk voicemail operates through the &lt;code&gt;app_voicemail&lt;/code&gt; module, which stores voicemail messages in the filesystem (typically &lt;code&gt;/var/spool/asterisk/voicemail/&lt;/code&gt;) and maintains metadata in Asterisk's astdb or via ODBC connectivity.&lt;/p&gt;

&lt;p&gt;The complete voicemail flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Call arrives&lt;/strong&gt; → matches voicemail extension in dialplan&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answer and greeting&lt;/strong&gt; → plays custom or default greeting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Record message&lt;/strong&gt; → stores to filesystem with timestamp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notification&lt;/strong&gt; → triggers email dispatch via MTA&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval&lt;/strong&gt; → agent retrieves via &lt;code&gt;*98&lt;/code&gt; or IVR menu&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive&lt;/strong&gt; → system moves old messages or deletes per policy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In ViciDial environments, voicemail ties directly to the &lt;code&gt;vicidial_users&lt;/code&gt; table and can be correlated with &lt;code&gt;vicidial_log&lt;/code&gt; entries for compliance and reporting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 1: Core Voicemail Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installing and Enabling app_voicemail
&lt;/h3&gt;

&lt;p&gt;Verify app_voicemail is compiled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"module show like voicemail"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If not loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"module load app_voicemail"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make it persistent across restarts, edit &lt;code&gt;/etc/asterisk/modules.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;modules&lt;/span&gt;]
&lt;span class="n"&gt;autoload&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;

; &lt;span class="n"&gt;Specific&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;
&lt;span class="n"&gt;load&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;app_voicemail&lt;/span&gt;.&lt;span class="n"&gt;so&lt;/span&gt;
&lt;span class="n"&gt;load&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;res_adsi&lt;/span&gt;.&lt;span class="n"&gt;so&lt;/span&gt;
&lt;span class="n"&gt;load&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;res_smdi&lt;/span&gt;.&lt;span class="n"&gt;so&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating the Voicemail Context
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/voicemail.conf&lt;/code&gt; to define voicemail boxes and contexts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
; &lt;span class="n"&gt;Format&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;storing&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt;
&lt;span class="n"&gt;format&lt;/span&gt;=&lt;span class="n"&gt;wav49&lt;/span&gt;|&lt;span class="n"&gt;gsm&lt;/span&gt;|&lt;span class="n"&gt;wav&lt;/span&gt;
; &lt;span class="n"&gt;Maximum&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; (&lt;span class="m"&gt;600&lt;/span&gt; = &lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;)
&lt;span class="n"&gt;maxmessage&lt;/span&gt;=&lt;span class="m"&gt;600&lt;/span&gt;
&lt;span class="n"&gt;minmessage&lt;/span&gt;=&lt;span class="m"&gt;3&lt;/span&gt;
; &lt;span class="n"&gt;Silence&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt; (&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;milliseconds&lt;/span&gt;)
&lt;span class="n"&gt;silencethreshold&lt;/span&gt;=&lt;span class="m"&gt;128&lt;/span&gt;
; &lt;span class="n"&gt;Number&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;silence&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;
&lt;span class="n"&gt;maxsilence&lt;/span&gt;=&lt;span class="m"&gt;10&lt;/span&gt;
; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="n"&gt;serveremail&lt;/span&gt;=&lt;span class="n"&gt;asterisk&lt;/span&gt;@&lt;span class="n"&gt;yourdomain&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;fromstring&lt;/span&gt;=&lt;span class="n"&gt;ViciDial&lt;/span&gt; &lt;span class="n"&gt;Voicemail&lt;/span&gt;
&lt;span class="n"&gt;emaildateformat&lt;/span&gt;=%&lt;span class="n"&gt;A&lt;/span&gt;, %&lt;span class="n"&gt;B&lt;/span&gt; %&lt;span class="n"&gt;d&lt;/span&gt;, %&lt;span class="n"&gt;Y&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; %&lt;span class="n"&gt;l&lt;/span&gt;:%&lt;span class="n"&gt;M&lt;/span&gt; %&lt;span class="n"&gt;p&lt;/span&gt;
&lt;span class="n"&gt;attachvolumeenvelope&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;attach&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
; &lt;span class="n"&gt;Envelope&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;
&lt;span class="n"&gt;emailsubject&lt;/span&gt;=&lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; ${&lt;span class="n"&gt;VM_CALLERID&lt;/span&gt;}
&lt;span class="n"&gt;emailbody&lt;/span&gt;=&lt;span class="n"&gt;Dear&lt;/span&gt; ${&lt;span class="n"&gt;VM_NAME&lt;/span&gt;},\&lt;span class="n"&gt;n&lt;/span&gt;\&lt;span class="n"&gt;nYou&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;received&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; ${&lt;span class="n"&gt;VM_CALLERID&lt;/span&gt;}.\&lt;span class="n"&gt;n&lt;/span&gt;\&lt;span class="n"&gt;nMessage&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;: ${&lt;span class="n"&gt;VM_DUR&lt;/span&gt;} &lt;span class="n"&gt;seconds&lt;/span&gt;\&lt;span class="n"&gt;nReceived&lt;/span&gt;: ${&lt;span class="n"&gt;VM_DATE&lt;/span&gt;}\&lt;span class="n"&gt;n&lt;/span&gt;\&lt;span class="n"&gt;nPlease&lt;/span&gt; &lt;span class="n"&gt;retrieve&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;earliest&lt;/span&gt; &lt;span class="n"&gt;convenience&lt;/span&gt;.\&lt;span class="n"&gt;n&lt;/span&gt;\&lt;span class="n"&gt;nViciDial&lt;/span&gt; &lt;span class="n"&gt;Team&lt;/span&gt;

[&lt;span class="n"&gt;vicidial_context&lt;/span&gt;]
; &lt;span class="n"&gt;Mailbox&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;: &lt;span class="n"&gt;mailbox&lt;/span&gt;=&amp;gt;&lt;span class="n"&gt;password&lt;/span&gt;,&lt;span class="n"&gt;fullname&lt;/span&gt;,&lt;span class="n"&gt;email&lt;/span&gt;,&lt;span class="n"&gt;pager_email&lt;/span&gt;,&lt;span class="n"&gt;options&lt;/span&gt;
; &lt;span class="n"&gt;Options&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;: &lt;span class="n"&gt;u&lt;/span&gt;(&lt;span class="n"&gt;unavail&lt;/span&gt;), &lt;span class="n"&gt;b&lt;/span&gt;(&lt;span class="n"&gt;busy&lt;/span&gt;), &lt;span class="n"&gt;t&lt;/span&gt;(&lt;span class="n"&gt;tempgreeting&lt;/span&gt;), &lt;span class="n"&gt;g&lt;/span&gt;(&lt;span class="n"&gt;greeting&lt;/span&gt;)
&lt;span class="m"&gt;6001&lt;/span&gt;=&amp;gt;&lt;span class="m"&gt;9999&lt;/span&gt;,&lt;span class="n"&gt;Agent&lt;/span&gt; &lt;span class="n"&gt;Smith&lt;/span&gt;,&lt;span class="n"&gt;agent&lt;/span&gt;.&lt;span class="n"&gt;smith&lt;/span&gt;@&lt;span class="n"&gt;company&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;,,&lt;span class="n"&gt;uj&lt;/span&gt;(&lt;span class="n"&gt;voicemail&lt;/span&gt;-&lt;span class="n"&gt;inactive&lt;/span&gt;)
&lt;span class="m"&gt;6002&lt;/span&gt;=&amp;gt;&lt;span class="m"&gt;8888&lt;/span&gt;,&lt;span class="n"&gt;Agent&lt;/span&gt; &lt;span class="n"&gt;Jones&lt;/span&gt;,&lt;span class="n"&gt;agent&lt;/span&gt;.&lt;span class="n"&gt;jones&lt;/span&gt;@&lt;span class="n"&gt;company&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;,,&lt;span class="n"&gt;uj&lt;/span&gt;(&lt;span class="n"&gt;voicemail&lt;/span&gt;-&lt;span class="n"&gt;inactive&lt;/span&gt;)
&lt;span class="m"&gt;6003&lt;/span&gt;=&amp;gt;&lt;span class="m"&gt;7777&lt;/span&gt;,&lt;span class="n"&gt;Agent&lt;/span&gt; &lt;span class="n"&gt;Brown&lt;/span&gt;,&lt;span class="n"&gt;agent&lt;/span&gt;.&lt;span class="n"&gt;brown&lt;/span&gt;@&lt;span class="n"&gt;company&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;,,&lt;span class="n"&gt;u&lt;/span&gt;
; &lt;span class="n"&gt;Bulk&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; (&lt;span class="m"&gt;6100&lt;/span&gt;-&lt;span class="m"&gt;6199&lt;/span&gt;)
&lt;span class="m"&gt;6100&lt;/span&gt;=&amp;gt;&lt;span class="m"&gt;1111&lt;/span&gt;,&lt;span class="n"&gt;Sales&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;,&lt;span class="n"&gt;sales100&lt;/span&gt;@&lt;span class="n"&gt;company&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="m"&gt;6101&lt;/span&gt;=&amp;gt;&lt;span class="m"&gt;1111&lt;/span&gt;,&lt;span class="n"&gt;Sales&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt; &lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="n"&gt;sales101&lt;/span&gt;@&lt;span class="n"&gt;company&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The format breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mailbox&lt;/code&gt; = voicemail extension (typically matches SIP extension)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;password&lt;/code&gt; = digit code to access voicemail&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fullname&lt;/code&gt; = announced name for callers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;email&lt;/code&gt; = primary notification recipient&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pager_email&lt;/code&gt; = secondary recipient (often mobile)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;options&lt;/code&gt; = flags for unavailable (&lt;code&gt;u&lt;/code&gt;), busy (&lt;code&gt;b&lt;/code&gt;), temporary greeting (&lt;code&gt;t&lt;/code&gt;), greeting recorded (&lt;code&gt;g&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Linking ViciDial Users to Voicemail
&lt;/h3&gt;

&lt;p&gt;ViciDial's &lt;code&gt;vicidial_users&lt;/code&gt; table has voicemail integration points. Query the schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;COLUMNS&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%voicemail%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your ViciDial version supports it, you'll see fields like &lt;code&gt;voicemail_ext&lt;/code&gt; or &lt;code&gt;vm_enabled&lt;/code&gt;. Alternatively, create a mapping table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;vicidial_voicemail_map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vm_ext&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;vm_password&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;vm_email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;vm_enabled&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'N'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_user_id&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InnoDB&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;CHARSET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Populate it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;vicidial_voicemail_map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vm_ext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vm_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vm_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vm_enabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'9999'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Y'&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'ACTIVE'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'AGENT'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 2: Dialplan Integration &amp;amp; IVR Routing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating the Voicemail Dialplan Context
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/asterisk/extensions-vicidial.conf&lt;/code&gt; and add a dedicated voicemail context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;voicemail&lt;/span&gt;-&lt;span class="n"&gt;context&lt;/span&gt;]
; &lt;span class="n"&gt;Main&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Verbose&lt;/span&gt;(&lt;span class="m"&gt;2&lt;/span&gt;,&lt;span class="n"&gt;Entering&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; ${&lt;span class="n"&gt;CALLERID&lt;/span&gt;(&lt;span class="n"&gt;num&lt;/span&gt;)})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;VM_CONTEXT&lt;/span&gt;=&lt;span class="n"&gt;vicidial_context&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;vm&lt;/span&gt;-&lt;span class="n"&gt;welcome&lt;/span&gt;,&lt;span class="n"&gt;noanswer&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;VoiceMail&lt;/span&gt;(${&lt;span class="n"&gt;EXTEN&lt;/span&gt;}@${&lt;span class="n"&gt;VM_CONTEXT&lt;/span&gt;},&lt;span class="n"&gt;u&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

; &lt;span class="n"&gt;Voicemail&lt;/span&gt; &lt;span class="n"&gt;menu&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;agents&lt;/span&gt; (*&lt;span class="m"&gt;98&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt;)
[&lt;span class="n"&gt;voicemail&lt;/span&gt;-&lt;span class="n"&gt;main&lt;/span&gt;-&lt;span class="n"&gt;menu&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Voicemail&lt;/span&gt; &lt;span class="n"&gt;Main&lt;/span&gt; &lt;span class="n"&gt;Menu&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; ${&lt;span class="n"&gt;CALLERID&lt;/span&gt;(&lt;span class="n"&gt;num&lt;/span&gt;)})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;ATTEMPT&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;authenticate&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;ATTEMPT&lt;/span&gt;=$[${&lt;span class="n"&gt;ATTEMPT&lt;/span&gt;} + &lt;span class="m"&gt;1&lt;/span&gt;])
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;vm&lt;/span&gt;-&lt;span class="n"&gt;login&lt;/span&gt;,&lt;span class="n"&gt;noanswer&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Read&lt;/span&gt;(&lt;span class="n"&gt;VMBOX&lt;/span&gt;,&lt;span class="n"&gt;vm&lt;/span&gt;-&lt;span class="n"&gt;enter&lt;/span&gt;-&lt;span class="n"&gt;id&lt;/span&gt;,&lt;span class="m"&gt;5&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Read&lt;/span&gt;(&lt;span class="n"&gt;VMPASS&lt;/span&gt;,&lt;span class="n"&gt;vm&lt;/span&gt;-&lt;span class="n"&gt;enter&lt;/span&gt;-&lt;span class="n"&gt;password&lt;/span&gt;,&lt;span class="m"&gt;5&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;VoiceMailMain&lt;/span&gt;(${&lt;span class="n"&gt;VMBOX&lt;/span&gt;}@${&lt;span class="n"&gt;VM_CONTEXT&lt;/span&gt;})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

; &lt;span class="n"&gt;Access&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;directly&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="m"&gt;610&lt;/span&gt;[&lt;span class="m"&gt;0&lt;/span&gt;-&lt;span class="m"&gt;9&lt;/span&gt;],&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Direct&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; ${&lt;span class="n"&gt;EXTEN&lt;/span&gt;})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="m"&gt;610&lt;/span&gt;[&lt;span class="m"&gt;0&lt;/span&gt;-&lt;span class="m"&gt;9&lt;/span&gt;],&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;VoiceMail&lt;/span&gt;(${&lt;span class="n"&gt;EXTEN&lt;/span&gt;}@&lt;span class="n"&gt;vicidial_context&lt;/span&gt;,&lt;span class="n"&gt;u&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="m"&gt;610&lt;/span&gt;[&lt;span class="m"&gt;0&lt;/span&gt;-&lt;span class="m"&gt;9&lt;/span&gt;],&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

; &lt;span class="n"&gt;Fallback&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt;
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;i&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;invalid&lt;/span&gt;,&lt;span class="n"&gt;noanswer&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;i&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  IVR-Based Voicemail Routing
&lt;/h3&gt;

&lt;p&gt;For more sophisticated call routing, create an IVR menu that directs calls to agent voicemail or live queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;vicidial&lt;/span&gt;-&lt;span class="n"&gt;ivr&lt;/span&gt;-&lt;span class="n"&gt;main&lt;/span&gt;]
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(=== &lt;span class="n"&gt;ViciDial&lt;/span&gt; &lt;span class="n"&gt;IVR&lt;/span&gt; &lt;span class="n"&gt;Entry&lt;/span&gt; ===)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Set&lt;/span&gt;(&lt;span class="n"&gt;CHANNEL&lt;/span&gt;(&lt;span class="n"&gt;language&lt;/span&gt;)=&lt;span class="n"&gt;en&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;vm&lt;/span&gt;-&lt;span class="n"&gt;goodbye&lt;/span&gt;,&lt;span class="n"&gt;noanswer&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;(&lt;span class="n"&gt;menu&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;press&lt;/span&gt;-&lt;span class="m"&gt;1&lt;/span&gt;-&lt;span class="n"&gt;for&lt;/span&gt;-&lt;span class="n"&gt;sales&lt;/span&gt;,&lt;span class="n"&gt;noanswer&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;press&lt;/span&gt;-&lt;span class="m"&gt;2&lt;/span&gt;-&lt;span class="n"&gt;for&lt;/span&gt;-&lt;span class="n"&gt;support&lt;/span&gt;,&lt;span class="n"&gt;noanswer&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;press&lt;/span&gt;-&lt;span class="m"&gt;0&lt;/span&gt;-&lt;span class="n"&gt;operator&lt;/span&gt;,&lt;span class="n"&gt;noanswer&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;WaitExten&lt;/span&gt;(&lt;span class="m"&gt;5&lt;/span&gt;)

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(=== &lt;span class="n"&gt;Sales&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt; ===)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Queue&lt;/span&gt;(&lt;span class="n"&gt;sales_queue&lt;/span&gt;,&lt;span class="n"&gt;t&lt;/span&gt;,,,&lt;span class="m"&gt;300&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;VoiceMail&lt;/span&gt;(&lt;span class="m"&gt;6100&lt;/span&gt;@&lt;span class="n"&gt;vicidial_context&lt;/span&gt;,&lt;span class="n"&gt;u&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;2&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(=== &lt;span class="n"&gt;Support&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt; ===)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;2&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Queue&lt;/span&gt;(&lt;span class="n"&gt;support_queue&lt;/span&gt;,&lt;span class="n"&gt;t&lt;/span&gt;,,,&lt;span class="m"&gt;300&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;2&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;VoiceMail&lt;/span&gt;(&lt;span class="m"&gt;6200&lt;/span&gt;@&lt;span class="n"&gt;vicidial_context&lt;/span&gt;,&lt;span class="n"&gt;u&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;2&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(=== &lt;span class="n"&gt;Operator&lt;/span&gt; &lt;span class="n"&gt;Transfer&lt;/span&gt; ===)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Dial&lt;/span&gt;(&lt;span class="n"&gt;SIP&lt;/span&gt;/&lt;span class="m"&gt;6001&lt;/span&gt;,&lt;span class="m"&gt;20&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;VoiceMail&lt;/span&gt;(&lt;span class="m"&gt;6001&lt;/span&gt;@&lt;span class="n"&gt;vicidial_context&lt;/span&gt;,&lt;span class="n"&gt;b&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;t&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;vm&lt;/span&gt;-&lt;span class="n"&gt;goodbye&lt;/span&gt;,&lt;span class="n"&gt;noanswer&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;t&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;i&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;Playback&lt;/span&gt;(&lt;span class="n"&gt;invalid&lt;/span&gt;,&lt;span class="n"&gt;noanswer&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="n"&gt;i&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Goto&lt;/span&gt;(&lt;span class="n"&gt;s&lt;/span&gt;,&lt;span class="n"&gt;menu&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assign this context to your inbound trunk in &lt;code&gt;sip.conf&lt;/code&gt; or SIP configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;my_carrier_trunk&lt;/span&gt;]
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;peer&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;vicidial&lt;/span&gt;-&lt;span class="n"&gt;ivr&lt;/span&gt;-&lt;span class="n"&gt;main&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;=&lt;span class="n"&gt;carrier&lt;/span&gt;.&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;insecure&lt;/span&gt;=&lt;span class="n"&gt;invite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 3: Email Integration &amp;amp; Notifications
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Configuring Postfix for Voicemail Delivery
&lt;/h3&gt;

&lt;p&gt;Ensure Postfix is installed and running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;postfix mailutils
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start postfix
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;postfix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test mail delivery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Test message"&lt;/span&gt; | mail &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"Test"&lt;/span&gt; youremail@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Advanced Voicemail Email Configuration
&lt;/h3&gt;

&lt;p&gt;Update &lt;code&gt;/etc/asterisk/voicemail.conf&lt;/code&gt; with full email settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="n"&gt;delivery&lt;/span&gt;
&lt;span class="n"&gt;serveremail&lt;/span&gt;=&lt;span class="n"&gt;asterisk&lt;/span&gt;@&lt;span class="n"&gt;yourdomain&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;fromstring&lt;/span&gt;=&lt;span class="n"&gt;ViciDial&lt;/span&gt; &lt;span class="n"&gt;Voicemail&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;
&lt;span class="n"&gt;emaildateformat&lt;/span&gt;=%&lt;span class="n"&gt;A&lt;/span&gt;, %&lt;span class="n"&gt;B&lt;/span&gt; %&lt;span class="n"&gt;d&lt;/span&gt;, %&lt;span class="n"&gt;Y&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; %&lt;span class="n"&gt;l&lt;/span&gt;:%&lt;span class="n"&gt;M&lt;/span&gt; %&lt;span class="n"&gt;p&lt;/span&gt;

; &lt;span class="n"&gt;Attach&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;audio&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;
&lt;span class="n"&gt;attach&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;attachvolumeenvelope&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;

; &lt;span class="n"&gt;Audio&lt;/span&gt; &lt;span class="n"&gt;formats&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;attach&lt;/span&gt; (&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;compatible&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;)
&lt;span class="n"&gt;format&lt;/span&gt;=&lt;span class="n"&gt;wav49&lt;/span&gt;|&lt;span class="n"&gt;gsm&lt;/span&gt;|&lt;span class="n"&gt;wav&lt;/span&gt;

; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="n"&gt;templates&lt;/span&gt;
&lt;span class="n"&gt;emailsubject&lt;/span&gt;=&lt;span class="n"&gt;Voicemail&lt;/span&gt;: ${&lt;span class="n"&gt;VM_CALLERID&lt;/span&gt;} - ${&lt;span class="n"&gt;VM_DATE&lt;/span&gt;}
&lt;span class="n"&gt;emailbody&lt;/span&gt;=&lt;span class="n"&gt;Dear&lt;/span&gt; ${&lt;span class="n"&gt;VM_NAME&lt;/span&gt;},\&lt;span class="n"&gt;n&lt;/span&gt;\&lt;span class="n"&gt;nYou&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;.\&lt;span class="n"&gt;n&lt;/span&gt;\&lt;span class="n"&gt;nCaller&lt;/span&gt;: ${&lt;span class="n"&gt;VM_CALLERID&lt;/span&gt;}\&lt;span class="n"&gt;nLength&lt;/span&gt;: ${&lt;span class="n"&gt;VM_DUR&lt;/span&gt;} &lt;span class="n"&gt;seconds&lt;/span&gt;\&lt;span class="n"&gt;nReceived&lt;/span&gt;: ${&lt;span class="n"&gt;VM_DATE&lt;/span&gt;}\&lt;span class="n"&gt;nBox&lt;/span&gt;: ${&lt;span class="n"&gt;VM_MAILBOX&lt;/span&gt;}\&lt;span class="n"&gt;n&lt;/span&gt;\&lt;span class="n"&gt;nTo&lt;/span&gt; &lt;span class="n"&gt;retrieve&lt;/span&gt;: &lt;span class="n"&gt;Dial&lt;/span&gt; *&lt;span class="m"&gt;98&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;enter&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;.\&lt;span class="n"&gt;n&lt;/span&gt;\&lt;span class="n"&gt;nViciDial&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;

; &lt;span class="n"&gt;Pager&lt;/span&gt;/&lt;span class="n"&gt;SMS&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt; (&lt;span class="n"&gt;text&lt;/span&gt;-&lt;span class="n"&gt;only&lt;/span&gt;)
&lt;span class="n"&gt;pagerfromstring&lt;/span&gt;=&lt;span class="n"&gt;ViciDial&lt;/span&gt; &lt;span class="n"&gt;Alerts&lt;/span&gt;
&lt;span class="n"&gt;pagersubject&lt;/span&gt;=&lt;span class="n"&gt;VM&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; ${&lt;span class="n"&gt;VM_CALLERID&lt;/span&gt;}
&lt;span class="n"&gt;pagerbody&lt;/span&gt;=${&lt;span class="n"&gt;VM_NAME&lt;/span&gt;}, &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; ${&lt;span class="n"&gt;VM_CALLERID&lt;/span&gt;} (${&lt;span class="n"&gt;VM_DUR&lt;/span&gt;}&lt;span class="n"&gt;s&lt;/span&gt;) &lt;span class="n"&gt;at&lt;/span&gt; ${&lt;span class="n"&gt;VM_DATE&lt;/span&gt;}

; &lt;span class="n"&gt;Maximum&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; (&lt;span class="n"&gt;prevent&lt;/span&gt; &lt;span class="n"&gt;bloat&lt;/span&gt;)
&lt;span class="n"&gt;volgain&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;emaildateformat&lt;/span&gt;=%&lt;span class="n"&gt;A&lt;/span&gt;, %&lt;span class="n"&gt;B&lt;/span&gt; %&lt;span class="n"&gt;d&lt;/span&gt;, %&lt;span class="n"&gt;Y&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; %&lt;span class="n"&gt;l&lt;/span&gt;:%&lt;span class="n"&gt;M&lt;/span&gt; %&lt;span class="n"&gt;p&lt;/span&gt;

[&lt;span class="n"&gt;vicidial_context&lt;/span&gt;]
; &lt;span class="n"&gt;Per&lt;/span&gt;-&lt;span class="n"&gt;mailbox&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt;
&lt;span class="m"&gt;6001&lt;/span&gt;=&amp;gt;&lt;span class="m"&gt;9999&lt;/span&gt;,&lt;span class="n"&gt;Agent&lt;/span&gt; &lt;span class="n"&gt;Smith&lt;/span&gt;,&lt;span class="n"&gt;smith&lt;/span&gt;@&lt;span class="n"&gt;company&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;,&lt;span class="n"&gt;smith&lt;/span&gt;.&lt;span class="n"&gt;mobile&lt;/span&gt;@&lt;span class="n"&gt;sms&lt;/span&gt;.&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;,&lt;span class="n"&gt;uj&lt;/span&gt;
&lt;span class="m"&gt;6002&lt;/span&gt;=&amp;gt;&lt;span class="m"&gt;8888&lt;/span&gt;,&lt;span class="n"&gt;Agent&lt;/span&gt; &lt;span class="n"&gt;Jones&lt;/span&gt;,&lt;span class="n"&gt;jones&lt;/span&gt;@&lt;span class="n"&gt;company&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;,,&lt;span class="n"&gt;u&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom Email Handler Script
&lt;/h3&gt;

&lt;p&gt;For advanced processing (transcription, CRM integration), create &lt;code&gt;/usr/local/bin/voicemail-handler.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# ViciDial Voicemail Handler Script&lt;/span&gt;
&lt;span class="c"&gt;# Called by Asterisk after voicemail recording&lt;/span&gt;

&lt;span class="nv"&gt;VMBOX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;CONTEXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;MAILBOX_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/spool/asterisk/voicemail/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTEXT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VMBOX&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;CALLERID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;DURATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$4&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Logging&lt;/span&gt;
&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/log/asterisk/voicemail-handler.log"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'+%Y-%m-%d %H:%M:%S'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; - Processing VM: Box=&lt;/span&gt;&lt;span class="nv"&gt;$VMBOX&lt;/span&gt;&lt;span class="s2"&gt;, Caller=&lt;/span&gt;&lt;span class="nv"&gt;$CALLERID&lt;/span&gt;&lt;span class="s2"&gt;, Duration=&lt;/span&gt;&lt;span class="nv"&gt;$DURATION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Get most recent message&lt;/span&gt;
&lt;span class="nv"&gt;LATEST_MSG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MAILBOX_DIR&lt;/span&gt;&lt;span class="s2"&gt;/new/"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.wav 2&amp;gt;/dev/null | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LATEST_MSG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'+%Y-%m-%d %H:%M:%S'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; - ERROR: No message found for &lt;/span&gt;&lt;span class="nv"&gt;$VMBOX&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Convert to MP3 (optional, requires sox)&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; sox &amp;amp;&amp;gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;MP3_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LATEST_MSG&lt;/span&gt;&lt;span class="p"&gt;%.wav&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.mp3"&lt;/span&gt;
    sox &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LATEST_MSG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; 128 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MP3_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;ATTACH_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MP3_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;ATTACH_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LATEST_MSG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Query ViciDial database for mailbox owner info&lt;/span&gt;
&lt;span class="nv"&gt;AGENT_EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; asterisk &lt;span class="nt"&gt;-pasterisk&lt;/span&gt; asterisk &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"SELECT email FROM vicidial_users WHERE extension='&lt;/span&gt;&lt;span class="nv"&gt;$VMBOX&lt;/span&gt;&lt;span class="s2"&gt;' LIMIT 1;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AGENT_EMAIL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'+%Y-%m-%d %H:%M:%S'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; - WARNING: No email found for extension &lt;/span&gt;&lt;span class="nv"&gt;$VMBOX&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Send email with attachment&lt;/span&gt;
&lt;span class="nv"&gt;MAIL_SUBJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Voicemail from &lt;/span&gt;&lt;span class="nv"&gt;$CALLERID&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DURATION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s)"&lt;/span&gt;
&lt;span class="nv"&gt;MAIL_BODY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"You have a new voicemail message.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Caller: &lt;/span&gt;&lt;span class="nv"&gt;$CALLERID&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Duration: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DURATION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; seconds&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Received: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'+%Y-%m-%d %H:%M:%S'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Please see attached audio file."&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MAIL_BODY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | mutt &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MAIL_SUBJECT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ATTACH_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AGENT_EMAIL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'+%Y-%m-%d %H:%M:%S'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; - Email sent to &lt;/span&gt;&lt;span class="nv"&gt;$AGENT_EMAIL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make it executable and update Asterisk to call it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; +x /usr/local/bin/voicemail-handler.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register in voicemail.conf (requires custom module compilation for some versions):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
; &lt;span class="n"&gt;Call&lt;/span&gt; &lt;span class="n"&gt;external&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;saved&lt;/span&gt;
; &lt;span class="n"&gt;Note&lt;/span&gt;: &lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="n"&gt;res_voicemail&lt;/span&gt; &lt;span class="n"&gt;compiled&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;SENDMAIL&lt;/span&gt; &lt;span class="n"&gt;support&lt;/span&gt;
&lt;span class="n"&gt;mailcmd&lt;/span&gt;=/&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;local&lt;/span&gt;/&lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;voicemail&lt;/span&gt;-&lt;span class="n"&gt;handler&lt;/span&gt;.&lt;span class="n"&gt;sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ODBC-Based Voicemail Storage (Alternative)
&lt;/h3&gt;

&lt;p&gt;For high-volume environments, store voicemail metadata in MySQL instead of astdb:&lt;/p&gt;

&lt;p&gt;Create the table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;asterisk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;voicemail_msgs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;mailbox&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;callerid&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;message_date&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;is_read&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'N'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'N'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_context_mailbox&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mailbox&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_date&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InnoDB&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;CHARSET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure ODBC in &lt;code&gt;/etc/asterisk/res_odbc.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;asterisk&lt;/span&gt;]
&lt;span class="n"&gt;dsn&lt;/span&gt;=&lt;span class="n"&gt;asterisk&lt;/span&gt;
&lt;span class="n"&gt;username&lt;/span&gt;=&lt;span class="n"&gt;asterisk&lt;/span&gt;
&lt;span class="n"&gt;password&lt;/span&gt;=&lt;span class="n"&gt;asterisk&lt;/span&gt;
&lt;span class="n"&gt;pooling&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;limit&lt;/span&gt;=&lt;span class="m"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update voicemail.conf to use ODBC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;general&lt;/span&gt;]
; &lt;span class="n"&gt;Enable&lt;/span&gt; &lt;span class="n"&gt;ODBC&lt;/span&gt; &lt;span class="n"&gt;support&lt;/span&gt;
&lt;span class="n"&gt;odbcstorage&lt;/span&gt;=&lt;span class="n"&gt;asterisk&lt;/span&gt;
&lt;span class="n"&gt;odbctable&lt;/span&gt;=&lt;span class="n"&gt;voicemail_msgs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 4: Agent Retrieval Interface
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Enabling *98 Voicemail Access
&lt;/h3&gt;

&lt;p&gt;Add to your main extension context in &lt;code&gt;extensions-vicidial.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="n"&gt;vicidial&lt;/span&gt;-&lt;span class="n"&gt;agents&lt;/span&gt;]
; &lt;span class="n"&gt;Agent&lt;/span&gt; &lt;span class="n"&gt;voicemail&lt;/span&gt; &lt;span class="n"&gt;retrieval&lt;/span&gt; (*&lt;span class="m"&gt;98&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;98&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Voicemail&lt;/span&gt; &lt;span class="n"&gt;retrieval&lt;/span&gt; &lt;span class="n"&gt;initiated&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; ${&lt;span class="n"&gt;CALLERID&lt;/span&gt;(&lt;span class="n"&gt;num&lt;/span&gt;)})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;98&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Authenticate&lt;/span&gt;(${&lt;span class="n"&gt;EXTEN&lt;/span&gt;})
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;98&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;VoiceMailMain&lt;/span&gt;(@&lt;span class="n"&gt;vicidial_context&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;98&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()

; &lt;span class="n"&gt;Record&lt;/span&gt; &lt;span class="n"&gt;temporary&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt; (*&lt;span class="m"&gt;99&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;99&lt;/span&gt;,&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="n"&gt;NoOp&lt;/span&gt;(&lt;span class="n"&gt;Recording&lt;/span&gt; &lt;span class="n"&gt;temporary&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;99&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;VoiceMailMain&lt;/span&gt;(${&lt;span class="n"&gt;CALLERID&lt;/span&gt;(&lt;span class="n"&gt;num&lt;/span&gt;)}@&lt;span class="n"&gt;vicidial_context&lt;/span&gt;,&lt;span class="n"&gt;t&lt;/span&gt;)
&lt;span class="n"&gt;exten&lt;/span&gt; =&amp;gt; &lt;span class="m"&gt;99&lt;/span&gt;,&lt;span class="n"&gt;n&lt;/span&gt;,&lt;span class="n"&gt;Hangup&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include this context in your SIP extensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="m"&gt;6000&lt;/span&gt;-&lt;span class="m"&gt;6299&lt;/span&gt;](&lt;span class="n"&gt;vicidial_agent&lt;/span&gt;)
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;friend&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;vicidial&lt;/span&gt;-&lt;span class="n"&gt;agents&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;,&lt;span class="n"&gt;gsm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Web-Based Voicemail Retrieval for ViciDial
&lt;/h3&gt;

&lt;p&gt;Add a custom PHP interface at &lt;code&gt;/var/www/html/vicidial/voicemail.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="c1"&gt;// ViciDial Web Voicemail Interface&lt;/span&gt;
&lt;span class="nb"&gt;session_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Verify agent is logged in&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Location: index.php"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$user_extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'extension'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Database connection&lt;/span&gt;
&lt;span class="nv"&gt;$db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;mysqli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"asterisk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"asterisk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"asterisk"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;connect_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Database connection failed: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;connect_error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Retrieve voicemail boxes for user&lt;/span&gt;
&lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SELECT vm_ext, vm_email FROM vicidial_voicemail_map WHERE user_id = ?"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_result&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Voicemail Manager&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.vm-box&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.controls&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Voicemail Manager&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

    &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;num_rows&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch_assoc&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"vm-box"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Extension &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;htmlspecialchars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'vm_ext'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Email: &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;htmlspecialchars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'vm_email'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"controls"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"accessVoicemail('&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'vm_ext'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        Access Voicemail
                    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"recordGreeting('&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'vm_ext'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        Record Greeting
                    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;p&amp;gt;No voicemail boxes configured for your account.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;accessVoicemail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Trigger Asterisk originate call to VoiceMailMain&lt;/span&gt;
        &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;voicemail-api.php&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;action=access&amp;amp;ext=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Voicemail dialing to &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;recordGreeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Record temporary greeting for &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;voicemail-api.php&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;action=greeting&amp;amp;ext=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Backend API at &lt;code&gt;/var/www/html/vicidial/voicemail-api.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="c1"&gt;// Voicemail API for originating calls&lt;/span&gt;
&lt;span class="nb"&gt;session_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;http_response_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Unauthorized'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'action'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/[^0-9]/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ext'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ext&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Invalid extension'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Use Asterisk Manager Interface or AGI to originate call&lt;/span&gt;
&lt;span class="nv"&gt;$user_ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'extension'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'greeting'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'t'&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'u'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Option 1: Use Asterisk Manager (requires AMI configuration)&lt;/span&gt;
&lt;span class="nv"&gt;$socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fsockopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5038&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$errno&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$errstr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Cannot connect to Asterisk'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Action: Login&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;Username: admin&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;Secret: your_ami_secret&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;fgets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Action: Originate&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
     &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"Channel: SIP/&lt;/span&gt;&lt;span class="nv"&gt;$user_ext&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
     &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"Context: vicidial-agents&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
     &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"Exten: &lt;/span&gt;&lt;span class="nv"&gt;$ext&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
     &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"Priority: 1&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
     &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"Async: true&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fgets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Action: Logoff&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$socket&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Voicemail access initiated'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 5: Advanced Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Voicemail Transcription
&lt;/h3&gt;

&lt;p&gt;Integrate with Google Cloud Speech-to-Text or Voicemail Transcription service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# /usr/local/bin/transcribe-voicemail.sh&lt;/span&gt;

&lt;span class="nv"&gt;VM_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VM_FILE&lt;/span&gt;&lt;span class="p"&gt;%.wav&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.txt"&lt;/span&gt;

&lt;span class="c"&gt;# Convert WAV to FLAC (Google requires)&lt;/span&gt;
flac &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VM_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VM_FILE&lt;/span&gt;&lt;span class="p"&gt;%.wav&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.flac"&lt;/span&gt;

&lt;span class="c"&gt;# Send to Google Speech-to-Text API&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-binary&lt;/span&gt; @&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VM_FILE&lt;/span&gt;&lt;span class="p"&gt;%.wav&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.flac"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: audio/flac"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://www.google.com/speech-api/json?output=json&amp;amp;client=chromium&amp;amp;key=YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Extract text from response&lt;/span&gt;
&lt;span class="nv"&gt;TRANSCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import json
import sys
with open('&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_FILE&lt;/span&gt;&lt;span class="s2"&gt;') as f:
    data = json.load(f)
    if 'result' in data and len(data['result']) &amp;gt; 0:
        print(data['result'][0]['alternative'][0]['transcript'])
"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Update database with transcript&lt;/span&gt;
mysql &lt;span class="nt"&gt;-u&lt;/span&gt; asterisk &lt;span class="nt"&gt;-pasterisk&lt;/span&gt; asterisk &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"INSERT INTO voicemail_transcripts (vm_file, transcript, created_date) &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
     VALUES ('&lt;/span&gt;&lt;span class="nv"&gt;$VM_FILE&lt;/span&gt;&lt;span class="s2"&gt;', '&lt;/span&gt;&lt;span class="nv"&gt;$TRANSCRIPT&lt;/span&gt;&lt;span class="s2"&gt;', NOW());"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Transcription complete: &lt;/span&gt;&lt;span class="nv"&gt;$TRANSCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Voicemail Purging &amp;amp; Archival
&lt;/h3&gt;

&lt;p&gt;Create a maintenance script at &lt;code&gt;/usr/local/bin/voicemail-maintenance.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Archive and delete old voicemail messages&lt;/span&gt;

&lt;span class="nv"&gt;VOICEMAIL_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/spool/asterisk/voicemail"&lt;/span&gt;
&lt;span class="nv"&gt;ARCHIVE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/backup/voicemail-archive"&lt;/span&gt;
&lt;span class="nv"&gt;RETENTION_DAYS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;90

&lt;span class="c"&gt;# Create archive directory&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARCHIVE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Find and archive messages older than 90 days&lt;/span&gt;
find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VOICEMAIL_ROOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.wav"&lt;/span&gt; &lt;span class="nt"&gt;-mtime&lt;/span&gt; +&lt;span class="nv"&gt;$RETENTION_DAYS&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;file&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;))&lt;/span&gt;
    &lt;span class="nv"&gt;mailbox&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Create archive structure&lt;/span&gt;
    &lt;span class="nv"&gt;archive_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARCHIVE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$context&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$mailbox&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$archive_path&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# Move file to archive&lt;/span&gt;
    &lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$archive_path&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;

    &lt;span class="c"&gt;# Also move metadata&lt;/span&gt;
    &lt;span class="nv"&gt;base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; .wav&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;.txt"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;.txt"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$archive_path&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;.gsm"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;.gsm"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$archive_path&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; - Archived: &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# Tar and compress old archives (&amp;gt;30 days old)&lt;/span&gt;
find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARCHIVE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-mtime&lt;/span&gt; +30 | xargs &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARCHIVE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/voicemail-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Voicemail maintenance completed at &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schedule in crontab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/bin/voicemail-maintenance.sh &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/voicemail-maintenance.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Voicemail Analytics &amp;amp; Reporting
&lt;/h3&gt;

&lt;p&gt;Query voicemail activity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Voicemail message count per agent&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;vm_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;message_length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;avg_duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;last_voicemail&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vicidial_log&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;vicidial_users&lt;/span&gt; &lt;span class="n"&gt;vu&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disposition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'VOICEMAIL'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;vm_count&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Voicemail response time (how long until agent retrieves)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;mailbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;total_voicemails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TIMESTAMPDIFF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HOUR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessed&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;avg_retrieval_hours&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;voicemail_msgs&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;accessed&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;mailbox&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Section 6: Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Voicemail Not Recording
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Calls go to voicemail but no message is left.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check voicemail module is loaded&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"module show like voicemail"&lt;/span&gt;

&lt;span class="c"&gt;# Check dialplan is correct&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan show voicemail-context"&lt;/span&gt;

&lt;span class="c"&gt;# Check filesystem permissions&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /var/spool/asterisk/voicemail/
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; asterisk:asterisk /var/spool/asterisk/voicemail/
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 755 /var/spool/asterisk/voicemail/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reload dialplan and modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan reload"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"module reload app_voicemail"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Email Notifications Not Sending
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Voicemails recorded but no email received.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check mail service&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status postfix
&lt;span class="nb"&gt;sudo &lt;/span&gt;mailq

&lt;span class="c"&gt;# Test mail delivery directly&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Test"&lt;/span&gt; | mail &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"Test Subject"&lt;/span&gt; youremail@example.com

&lt;span class="c"&gt;# Check Asterisk logs&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/asterisk/full
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"voicemail&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;email"&lt;/span&gt; /var/log/asterisk/full

&lt;span class="c"&gt;# Verify voicemail.conf settings&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A5&lt;/span&gt; &lt;span class="s2"&gt;"serveremail&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;attach&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;format"&lt;/span&gt; /etc/asterisk/voicemail.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ensure Postfix is running and configured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;postfix start
&lt;span class="c"&gt;# Test SMTP connectivity&lt;/span&gt;
telnet localhost 25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify voicemail.conf has &lt;code&gt;attach=yes&lt;/code&gt; and correct &lt;code&gt;serveremail&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent Cannot Access Voicemail via *98
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Dialing *98 hangs or says extension not found.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify extension pattern&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"dialplan show vicidial-agents"&lt;/span&gt;

&lt;span class="c"&gt;# Check SIP registration&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip show peers"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;600

&lt;span class="c"&gt;# Test with asterisk -vvv&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-vvv&lt;/span&gt;
&lt;span class="c"&gt;# Then dial *98 and watch console output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ensure voicemail-agents context is included in agent SIP profiles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;[&lt;span class="m"&gt;6000&lt;/span&gt;-&lt;span class="m"&gt;6299&lt;/span&gt;](&lt;span class="n"&gt;vicidial_agent&lt;/span&gt;)
&lt;span class="n"&gt;type&lt;/span&gt;=&lt;span class="n"&gt;friend&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;=&lt;span class="n"&gt;vicidial&lt;/span&gt;-&lt;span class="n"&gt;agents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload SIP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"sip reload"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Voicemail Files Corrupted or Unplayable
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Voicemail records but plays as noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check file format and integrity&lt;/span&gt;
file /var/spool/asterisk/voicemail/vicidial_context/6001/INBOX/&lt;span class="k"&gt;*&lt;/span&gt;.wav

&lt;span class="c"&gt;# Play file manually&lt;/span&gt;
sox /var/spool/asterisk/voicemail/vicidial_context/6001/INBOX/msg0000.wav &lt;span class="nt"&gt;-t&lt;/span&gt; raw -

&lt;span class="c"&gt;# Check codec mismatch in dialplan&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;asterisk &lt;span class="nt"&gt;-rx&lt;/span&gt; &lt;span class="s2"&gt;"core show codecs"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ensure codec configuration matches in &lt;code&gt;sip.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;allow&lt;/span&gt;=&lt;span class="n"&gt;ulaw&lt;/span&gt;,&lt;span class="n"&gt;gsm&lt;/span&gt;,&lt;span class="n"&gt;g729&lt;/span&gt;
&lt;span class="n"&gt;disallow&lt;/span&gt;=&lt;span class="n"&gt;all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify voicemail format in voicemail.conf is compatible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;format&lt;/span&gt;=&lt;span class="n"&gt;wav49&lt;/span&gt;|&lt;span class="n"&gt;gsm&lt;/span&gt;|&lt;span class="n"&gt;ulaw&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performance Issues with Large Voicemail Spool
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Asterisk slow, voicemail access lags.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check spool disk usage&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; /var/spool/asterisk/voicemail/

&lt;span class="c"&gt;# Count total files&lt;/span&gt;
find /var/spool/asterisk/voicemail &lt;span class="nt"&gt;-type&lt;/span&gt; f | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;

&lt;span class="c"&gt;# Check I/O wait&lt;/span&gt;
iostat &lt;span class="nt"&gt;-x&lt;/span&gt; 1 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Implement archival maintenance (see Section 5), migrate to ODBC backend, or use external storage:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
# Enable S
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>sip</category>
      <category>voip</category>
    </item>
  </channel>
</rss>
